--- Makefile.inc1.orig +++ Makefile.inc1 @@ -963,6 +963,14 @@ rm -f ${OBJTOP}/usr.sbin/ntp/libntpevent/.depend.*; \ fi +# 20181209 r341759 track migration across wpa update + @if [ -e "${OBJTOP}/usr.sbin/wpa/wpa_supplicant/.depend.rrm.o" ] && \ + egrep -q 'src/ap/rrm.c' \ + ${OBJTOP}/usr.sbin/wpa/wpa_supplicant/.depend.rrm.o; then \ + echo "Removing stale wpa dependencies"; \ + rm -f ${OBJTOP}/usr.sbin/wpa/*/.depend*; \ + fi + _worldtmp: .PHONY @echo @echo "--------------------------------------------------------------" --- contrib/wpa/CONTRIBUTIONS.orig +++ contrib/wpa/CONTRIBUTIONS @@ -140,7 +140,7 @@ Modified BSD license (no advertisement clause): -Copyright (c) 2002-2016, 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-2016, 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-2016, 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,115 @@ 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) --- contrib/wpa/hostapd/README.orig +++ contrib/wpa/hostapd/README @@ -2,7 +2,7 @@ Authenticator and RADIUS authentication server ================================================================ -Copyright (c) 2002-2016, 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 " @@ -100,6 +112,7 @@ 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; } @@ -111,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; @@ -119,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) { @@ -126,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); @@ -139,7 +186,7 @@ } while (fgets(buf, sizeof(buf), f)) { - int i, rem = 0; + int rem = 0; line++; @@ -169,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; @@ -190,25 +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); - os_memset(&(*acl)[*num].vlan_id, 0, - sizeof((*acl)[*num].vlan_id)); - (*acl)[*num].vlan_id.untagged = vlan_id; - (*acl)[*num].vlan_id.notempty = !!vlan_id; - (*num)++; } fclose(f); - qsort(*acl, *num, sizeof(**acl), hostapd_acl_comp); + if (*acl) + qsort(*acl, *num, sizeof(**acl), hostapd_acl_comp); return 0; } @@ -215,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) { @@ -223,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); @@ -312,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] == '*') { @@ -436,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++; @@ -471,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 != ' ' && @@ -522,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 */ @@ -684,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; @@ -710,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); @@ -755,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) @@ -978,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; @@ -1012,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; @@ -1057,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; @@ -1068,7 +1227,7 @@ return 0; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_IEEE80211N @@ -1085,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; @@ -1214,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) @@ -1307,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) { @@ -1857,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) { @@ -1916,6 +2161,25 @@ return 0; } + +static int hs20_parse_operator_icon(struct hostapd_bss_config *bss, char *pos, + int line) +{ + char **n; + + n = os_realloc_array(bss->hs20_operator_icon, + bss->hs20_operator_icon_count + 1, sizeof(char *)); + if (!n) + return -1; + bss->hs20_operator_icon = n; + bss->hs20_operator_icon[bss->hs20_operator_icon_count] = os_strdup(pos); + if (!bss->hs20_operator_icon[bss->hs20_operator_icon_count]) + return -1; + bss->hs20_operator_icon_count++; + + return 0; +} + #endif /* CONFIG_HS20 */ @@ -1986,6 +2250,132 @@ } +#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) @@ -2001,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); @@ -2058,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)) { @@ -2091,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) { @@ -2100,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) { @@ -2129,10 +2525,26 @@ } 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); @@ -2145,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 @@ -2207,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; @@ -2234,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) { @@ -2433,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) { @@ -2439,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) { @@ -2497,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) { @@ -2507,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); @@ -2517,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, @@ -2540,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'", @@ -2559,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); @@ -2637,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 @@ -2663,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; } @@ -2687,21 +3161,34 @@ } #endif /* CONFIG_ACS */ } else if (os_strcmp(buf, "dtim_period") == 0) { - bss->dtim_period = atoi(pos); - if (bss->dtim_period < 1 || bss->dtim_period > 255) { + int val = atoi(pos); + + if (val < 1 || val > 255) { wpa_printf(MSG_ERROR, "Line %d: invalid dtim_period %d", - line, bss->dtim_period); + line, val); return 1; } + bss->dtim_period = val; } else if (os_strcmp(buf, "bss_load_update_period") == 0) { - bss->bss_load_update_period = atoi(pos); - if (bss->bss_load_update_period < 0 || - bss->bss_load_update_period > 100) { + int val = atoi(pos); + + if (val < 0 || val > 100) { wpa_printf(MSG_ERROR, "Line %d: invalid bss_load_update_period %d", - line, bss->bss_load_update_period); + line, val); return 1; } + bss->bss_load_update_period = val; + } else if (os_strcmp(buf, "chan_util_avg_period") == 0) { + int val = atoi(pos); + + if (val < 0) { + wpa_printf(MSG_ERROR, + "Line %d: invalid chan_util_avg_period", + line); + return 1; + } + bss->chan_util_avg_period = val; } else if (os_strcmp(buf, "rts_threshold") == 0) { conf->rts_threshold = atoi(pos); if (conf->rts_threshold < -1 || conf->rts_threshold > 65535) { @@ -2728,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", @@ -2741,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; @@ -2862,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); @@ -2898,6 +3425,108 @@ } 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) { @@ -2978,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) { @@ -2991,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 = @@ -3000,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); @@ -3089,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); @@ -3132,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; @@ -3210,7 +3899,15 @@ 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) { @@ -3229,6 +3926,16 @@ #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, "na_mcast_to_ucast") == 0) { @@ -3291,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; @@ -3300,15 +4010,33 @@ } 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) \ @@ -3377,7 +4105,20 @@ wpabuf_free(bss->own_ie_override); bss->own_ie_override = tmp; + } else if (os_strcmp(buf, "sae_reflection_attack") == 0) { + bss->sae_reflection_attack = atoi(pos); + } else if (os_strcmp(buf, "sae_commit_override") == 0) { + wpabuf_free(bss->sae_commit_override); + bss->sae_commit_override = wpabuf_parse_bin(pos); #endif /* CONFIG_TESTING_OPTIONS */ +#ifdef CONFIG_SAE + } else if (os_strcmp(buf, "sae_password") == 0) { + if (parse_sae_password(bss, pos) < 0) { + wpa_printf(MSG_ERROR, "Line %d: Invalid sae_password", + line); + return 1; + } +#endif /* CONFIG_SAE */ } else if (os_strcmp(buf, "vendor_elements") == 0) { if (parse_wpabuf_hex(line, buf, &bss->vendor_elements, pos)) return 1; @@ -3386,6 +4127,8 @@ return 1; } else if (os_strcmp(buf, "sae_anti_clogging_threshold") == 0) { bss->sae_anti_clogging_threshold = atoi(pos); + } else if (os_strcmp(buf, "sae_sync") == 0) { + bss->sae_sync = atoi(pos); } else if (os_strcmp(buf, "sae_groups") == 0) { if (hostapd_parse_intlist(&bss->sae_groups, pos)) { wpa_printf(MSG_ERROR, @@ -3393,6 +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) { @@ -3478,19 +4223,132 @@ } 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'", --- 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. @@ -29,6 +29,10 @@ #include "common/version.h" #include "common/ieee802_11_defs.h" #include "common/ctrl_iface_common.h" +#ifdef CONFIG_DPP +#include "common/dpp.h" +#endif /* CONFIG_DPP */ +#include "common/wpa_ctrl.h" #include "crypto/tls.h" #include "drivers/driver.h" #include "eapol_auth/eapol_auth_sm.h" @@ -50,6 +54,7 @@ #include "ap/beacon.h" #include "ap/neighbor_db.h" #include "ap/rrm.h" +#include "ap/dpp_hostapd.h" #include "wps/wps_defs.h" #include "wps/wps.h" #include "fst/fst_ctrl_iface.h" @@ -76,9 +81,9 @@ static int hostapd_ctrl_iface_attach(struct hostapd_data *hapd, struct sockaddr_storage *from, - socklen_t fromlen) + socklen_t fromlen, const char *input) { - return ctrl_iface_attach(&hapd->ctrl_dst, from, fromlen); + return ctrl_iface_attach(&hapd->ctrl_dst, from, fromlen, input); } @@ -763,7 +768,7 @@ #endif /* CONFIG_INTERWORKING */ -#ifdef CONFIG_WNM +#ifdef CONFIG_WNM_AP static int hostapd_ctrl_iface_disassoc_imminent(struct hostapd_data *hapd, const char *cmd) @@ -838,7 +843,7 @@ 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; @@ -878,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"); @@ -888,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; @@ -1017,7 +933,8 @@ if (ret != 3) { wpa_printf(MSG_DEBUG, "MBO requires three arguments: mbo=::"); - return -1; + ret = -1; + goto fail; } if (mbo_reason > MBO_TRANSITION_REASON_PREMIUM_AP) { @@ -1024,7 +941,8 @@ wpa_printf(MSG_DEBUG, "Invalid MBO transition reason code %u", mbo_reason); - return -1; + ret = -1; + goto fail; } /* Valid values for Cellular preference are: 0, 1, 255 */ @@ -1032,7 +950,8 @@ wpa_printf(MSG_DEBUG, "Invalid MBO cellular capability %u", cell_pref); - return -1; + ret = -1; + goto fail; } if (reassoc_delay > 65535 || @@ -1040,7 +959,8 @@ !(req_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT))) { wpa_printf(MSG_DEBUG, "MBO: Assoc retry delay is only valid in disassoc imminent mode"); - return -1; + ret = -1; + goto fail; } *mbo_pos++ = MBO_ATTR_ID_TRANSITION_REASON; @@ -1063,16 +983,54 @@ ret = wnm_send_bss_tm_req(hapd, sta, req_mode, disassoc_timer, valid_int, bss_term_dur, url, - nei_pos > nei_rep ? nei_rep : NULL, - nei_pos - nei_rep, mbo_len ? mbo : NULL, - mbo_len); + nei_len ? nei_rep : NULL, nei_len, + mbo_len ? mbo : NULL, mbo_len); +#ifdef CONFIG_MBO +fail: +#endif /* CONFIG_MBO */ os_free(url); return ret; } -#endif /* CONFIG_WNM */ +static int hostapd_ctrl_iface_coloc_intf_req(struct hostapd_data *hapd, + const char *cmd) +{ + u8 addr[ETH_ALEN]; + struct sta_info *sta; + const char *pos; + unsigned int auto_report, timeout; + if (hwaddr_aton(cmd, addr)) { + wpa_printf(MSG_DEBUG, "Invalid STA MAC address"); + return -1; + } + + sta = ap_get_sta(hapd, addr); + if (!sta) { + wpa_printf(MSG_DEBUG, "Station " MACSTR + " not found for Collocated Interference Request", + MAC2STR(addr)); + return -1; + } + + pos = cmd + 17; + if (*pos != ' ') + return -1; + pos++; + auto_report = atoi(pos); + pos = os_strchr(pos, ' '); + if (!pos) + return -1; + pos++; + timeout = atoi(pos); + + return wnm_send_coloc_intf_req(hapd, sta, auto_report, timeout); +} + +#endif /* CONFIG_WNM_AP */ + + static int hostapd_ctrl_iface_get_key_mgmt(struct hostapd_data *hapd, char *buf, size_t buflen) { @@ -1096,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)) @@ -1109,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 "); @@ -1117,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 "); @@ -1154,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--; @@ -1282,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; @@ -1319,19 +1367,27 @@ wpa_printf(MSG_DEBUG, "WPS: Testing - wps_corrupt_pkhash=%d", wps_corrupt_pkhash); #endif /* CONFIG_WPS_TESTING */ -#ifdef CONFIG_INTERWORKING - } else if (os_strcasecmp(cmd, "gas_frag_limit") == 0) { - int val = atoi(value); - if (val <= 0) - ret = -1; - else - hapd->gas_frag_limit = val; -#endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_TESTING_OPTIONS } else if (os_strcasecmp(cmd, "ext_mgmt_frame_handling") == 0) { hapd->ext_mgmt_frame_handling = atoi(value); } else if (os_strcasecmp(cmd, "ext_eapol_frame_io") == 0) { hapd->ext_eapol_frame_io = atoi(value); +#ifdef CONFIG_DPP + } else if (os_strcasecmp(cmd, "dpp_config_obj_override") == 0) { + os_free(hapd->dpp_config_obj_override); + hapd->dpp_config_obj_override = os_strdup(value); + } else if (os_strcasecmp(cmd, "dpp_discovery_override") == 0) { + os_free(hapd->dpp_discovery_override); + hapd->dpp_discovery_override = os_strdup(value); + } else if (os_strcasecmp(cmd, "dpp_groups_override") == 0) { + os_free(hapd->dpp_groups_override); + hapd->dpp_groups_override = os_strdup(value); + } else if (os_strcasecmp(cmd, + "dpp_ignore_netaccesskey_mismatch") == 0) { + hapd->dpp_ignore_netaccesskey_mismatch = atoi(value); + } else if (os_strcasecmp(cmd, "dpp_test") == 0) { + dpp_test = atoi(value); +#endif /* CONFIG_DPP */ #endif /* CONFIG_TESTING_OPTIONS */ #ifdef CONFIG_MBO } else if (os_strcasecmp(cmd, "mbo_assoc_disallow") == 0) { @@ -1352,39 +1408,26 @@ * disallowing station logic. */ #endif /* CONFIG_MBO */ +#ifdef CONFIG_DPP + } else if (os_strcasecmp(cmd, "dpp_configurator_params") == 0) { + os_free(hapd->dpp_configurator_params); + hapd->dpp_configurator_params = os_strdup(value); +#endif /* CONFIG_DPP */ } else { - struct sta_info *sta; - struct vlan_description vlan_id; - ret = hostapd_set_iface(hapd->iconf, hapd->conf, cmd, value); if (ret) return ret; if (os_strcasecmp(cmd, "deny_mac_file") == 0) { - for (sta = hapd->sta_list; sta; sta = sta->next) { - if (hostapd_maclist_found( - hapd->conf->deny_mac, - hapd->conf->num_deny_mac, sta->addr, - &vlan_id) && - (!vlan_id.notempty || - !vlan_compare(&vlan_id, sta->vlan_desc))) - ap_sta_disconnect( - hapd, sta, sta->addr, - WLAN_REASON_UNSPECIFIED); - } - } else if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED && - os_strcasecmp(cmd, "accept_mac_file") == 0) { - for (sta = hapd->sta_list; sta; sta = sta->next) { - if (!hostapd_maclist_found( - hapd->conf->accept_mac, - hapd->conf->num_accept_mac, - sta->addr, &vlan_id) || - (vlan_id.notempty && - vlan_compare(&vlan_id, sta->vlan_desc))) - ap_sta_disconnect( - hapd, sta, sta->addr, - WLAN_REASON_UNSPECIFIED); - } + hostapd_disassoc_deny_mac(hapd); + } else if (os_strcasecmp(cmd, "accept_mac_file") == 0) { + hostapd_disassoc_accept_mac(hapd); + } else if (os_strncmp(cmd, "wme_ac_", 7) == 0 || + os_strncmp(cmd, "wmm_ac_", 7) == 0) { + hapd->parameter_set_count++; + if (ieee802_11_update_beacons(hapd->iface)) + wpa_printf(MSG_DEBUG, + "Failed to update beacons with WMM parameters"); } } @@ -1445,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) @@ -1534,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; @@ -1843,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 */ @@ -1859,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 @@ -2055,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; @@ -2140,11 +2616,52 @@ } +static int hostapd_ctrl_iface_req_beacon(struct hostapd_data *hapd, + const char *cmd, char *reply, + size_t reply_size) +{ + u8 addr[ETH_ALEN]; + const char *pos; + struct wpabuf *req; + int ret; + u8 req_mode = 0; + + if (hwaddr_aton(cmd, addr)) + return -1; + pos = os_strchr(cmd, ' '); + if (!pos) + return -1; + pos++; + if (os_strncmp(pos, "req_mode=", 9) == 0) { + int val = hex2byte(pos + 9); + + if (val < 0) + return -1; + req_mode = val; + pos += 11; + pos = os_strchr(pos, ' '); + if (!pos) + return -1; + pos++; + } + req = wpabuf_parse_bin(pos); + if (!req) + return -1; + + ret = hostapd_send_beacon_req(hapd, addr, req_mode, req); + wpabuf_free(req); + if (ret >= 0) + ret = os_snprintf(reply, reply_size, "%d", ret); + return ret; +} + + static int hostapd_ctrl_iface_set_neighbor(struct hostapd_data *hapd, char *buf) { struct wpa_ssid_value ssid; u8 bssid[ETH_ALEN]; struct wpabuf *nr, *lci = NULL, *civic = NULL; + int stationary = 0; char *tmp; int ret; @@ -2223,8 +2740,15 @@ } } + if (!buf) + goto set; + + if (os_strstr(buf, "stat")) + stationary = 1; + set: - ret = hostapd_neighbor_set(hapd, bssid, &ssid, nr, lci, civic); + ret = hostapd_neighbor_set(hapd, bssid, &ssid, nr, lci, civic, + stationary); wpabuf_free(nr); wpabuf_free(lci); @@ -2285,6 +2809,108 @@ } +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, @@ -2302,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); @@ -2349,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; @@ -2441,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; @@ -2451,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); @@ -2464,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; @@ -2480,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; @@ -2503,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)) @@ -2534,6 +3196,9 @@ reply_size); } else if (os_strcmp(buf, "PMKSA_FLUSH") == 0) { hostapd_ctrl_iface_pmksa_flush(hapd); + } else if (os_strncmp(buf, "PMKSA_ADD ", 10) == 0) { + if (hostapd_ctrl_iface_pmksa_add(hapd, buf + 10) < 0) + reply_len = -1; } else if (os_strncmp(buf, "SET_NEIGHBOR ", 13) == 0) { if (hostapd_ctrl_iface_set_neighbor(hapd, buf + 13)) reply_len = -1; @@ -2546,9 +3211,145 @@ } 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; @@ -2802,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; } @@ -2811,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; } @@ -2886,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; } @@ -2894,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; } @@ -2999,9 +3800,10 @@ static int hostapd_global_ctrl_iface_attach(struct hapd_interfaces *interfaces, struct sockaddr_storage *from, - socklen_t fromlen) + socklen_t fromlen, char *input) { - return ctrl_iface_attach(&interfaces->global_ctrl_dst, from, fromlen); + return ctrl_iface_attach(&interfaces->global_ctrl_dst, from, fromlen, + input); } @@ -3020,6 +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 */ } @@ -3371,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)) @@ -3478,8 +4294,6 @@ } } - dl_list_init(&interface->global_ctrl_dst); - interface->global_ctrl_sock = -1; os_get_random(gcookie, COOKIE_LEN); #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE @@ -3556,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; } @@ -3615,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; } @@ -3689,6 +4503,18 @@ } +static int hostapd_ctrl_check_event_enabled(struct wpa_ctrl_dst *dst, + const char *buf) +{ + /* Enable Probe Request events based on explicit request. + * Other events are enabled by default. + */ + if (str_starts(buf, RX_PROBE_REQUEST)) + return !!(dst->events & WPA_EVENT_RX_PROBE_REQUEST); + return 1; +} + + static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, enum wpa_msg_type type, const char *buf, size_t len) @@ -3723,7 +4549,8 @@ idx = 0; dl_list_for_each_safe(dst, next, ctrl_dst, struct wpa_ctrl_dst, list) { - if (level >= dst->debug_level) { + if ((level >= dst->debug_level) && + hostapd_ctrl_check_event_enabled(dst, buf)) { sockaddr_print(MSG_DEBUG, "CTRL_IFACE monitor send", &dst->addr, dst->addrlen); msg.msg_name = &dst->addr; --- contrib/wpa/hostapd/defconfig.orig +++ contrib/wpa/hostapd/defconfig @@ -31,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) @@ -50,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 @@ -157,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. @@ -166,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 @@ -243,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 @@ -256,6 +270,7 @@ # openssl = OpenSSL (default) # gnutls = GnuTLS # internal = Internal TLSv1 implementation (experimental) +# linux = Linux kernel AF_ALG and internal TLSv1 implementation (experimental) # none = Empty template #CONFIG_TLS=openssl @@ -268,6 +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 @@ -343,3 +362,20 @@ # 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/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-2016, 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. @@ -973,7 +973,7 @@ { printf("HLR/AuC testing gateway for hostapd EAP-SIM/AKA " "database/authenticator\n" - "Copyright (c) 2005-2016, 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 @@ -98,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 @@ -182,6 +199,11 @@ #chanlist=100 104 108 112 116 #chanlist=1 6 11-13 +# Exclude DFS channels from ACS +# This option can be used to exclude all DFS channels from the ACS channel list +# in cases where the driver supports DFS channels. +#acs_exclude_dfs=1 + # Beacon interval in kus (1.024 ms) (default: 100; range 15..65535) beacon_int=100 @@ -227,6 +249,19 @@ #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. @@ -294,7 +329,7 @@ # 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) @@ -403,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. @@ -476,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 @@ -482,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 @@ -692,6 +760,71 @@ # 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 @@ -789,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. @@ -801,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. @@ -835,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 @@ -1001,8 +1213,10 @@ # 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 @@ -1025,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 @@ -1088,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 @@ -1134,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 @@ -1163,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 @@ -1180,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 @@ -1196,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. @@ -1211,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 @@ -1244,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 @@ -1259,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) @@ -1285,10 +1692,17 @@ # 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. @@ -1299,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 @@ -1326,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 @@ -1516,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 @@ -1524,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 @@ -1596,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 @@ -1683,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. @@ -1853,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 @@ -1865,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 @@ -1879,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 @@ -1916,6 +2473,9 @@ # Enable neighbor report via radio measurements #rrm_neighbor_report=1 +# Enable beacon report via radio measurements +#rrm_beacon_report=1 + # Publish fine timing measurement (FTM) responder functionality # This parameter only controls publishing via Extended Capabilities element. # Actual functionality is managed outside hostapd. @@ -1925,6 +2485,12 @@ # 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 ######################################################### # --- 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-2016, 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. @@ -21,7 +21,7 @@ static const char *const hostapd_cli_version = "hostapd_cli v" VERSION_STR "\n" -"Copyright (c) 2004-2016, Jouni Malinen and contributors"; +"Copyright (c) 2004-2019, Jouni Malinen and contributors"; static struct wpa_ctrl *ctrl_conn; static int hostapd_cli_quit = 0; @@ -45,6 +45,8 @@ static void print_help(FILE *stream, const char *cmd); static char ** list_cmd_list(void); static void hostapd_cli_receive(int sock, void *eloop_ctx, void *sock_ctx); +static void update_stations(struct wpa_ctrl *ctrl); +static void cli_event(const char *str); static void usage(void) @@ -147,13 +149,45 @@ } +static int hostapd_cli_reconnect(const char *ifname) +{ + char *next_ctrl_ifname; + + hostapd_cli_close_connection(); + + if (!ifname) + return -1; + + next_ctrl_ifname = os_strdup(ifname); + os_free(ctrl_ifname); + ctrl_ifname = next_ctrl_ifname; + if (!ctrl_ifname) + return -1; + + ctrl_conn = hostapd_cli_open_connection(ctrl_ifname); + if (!ctrl_conn) + return -1; + if (!interactive && !action_file) + return 0; + if (wpa_ctrl_attach(ctrl_conn) == 0) { + hostapd_cli_attached = 1; + register_event_handler(ctrl_conn); + update_stations(ctrl_conn); + } else { + printf("Warning: Failed to attach to hostapd.\n"); + } + return 0; +} + + static void hostapd_cli_msg_cb(char *msg, size_t len) { + cli_event(msg); printf("%s\n", msg); } -static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print) +static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd, int print) { char buf[4096]; size_t len; @@ -181,7 +215,7 @@ } -static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd) +static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd) { return _wpa_ctrl_command(ctrl, cmd, 1); } @@ -286,6 +320,21 @@ } +static char ** hostapd_complete_stations(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + char **res = NULL; + + switch (arg) { + case 1: + res = cli_txt_list_array(&stations); + break; + } + + return res; +} + + static int hostapd_cli_cmd_new_sta(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -318,21 +367,6 @@ } -static char ** hostapd_complete_deauthenticate(const char *str, int pos) -{ - int arg = get_cmd_arg_num(str, pos); - char **res = NULL; - - switch (arg) { - case 1: - res = cli_txt_list_array(&stations); - break; - } - - return res; -} - - static int hostapd_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -351,21 +385,6 @@ } -static char ** hostapd_complete_disassociate(const char *str, int pos) -{ - int arg = get_cmd_arg_num(str, pos); - char **res = NULL; - - switch (arg) { - case 1: - res = cli_txt_list_array(&stations); - break; - } - - return res; -} - - #ifdef CONFIG_TAXONOMY static int hostapd_cli_cmd_signature(struct wpa_ctrl *ctrl, int argc, char *argv[]) @@ -701,8 +720,8 @@ } -static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd, - char *addr, size_t addr_len) +static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, const char *cmd, + char *addr, size_t addr_len, int print) { char buf[4096], *pos; size_t len; @@ -726,7 +745,8 @@ buf[len] = '\0'; if (memcmp(buf, "FAIL", 4) == 0) return -1; - printf("%s", buf); + if (print) + printf("%s", buf); pos = buf; while (*pos != '\0' && *pos != '\n') @@ -742,16 +762,33 @@ { char addr[32], cmd[64]; - if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr))) + if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 1)) return 0; do { snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr); - } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0); + } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 1) == 0); return -1; } +static int hostapd_cli_cmd_list_sta(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char addr[32], cmd[64]; + + if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 0)) + return 0; + do { + if (os_strcmp(addr, "") != 0) + printf("%s\n", addr); + os_snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr); + } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 0) == 0); + + return 0; +} + + static int hostapd_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[]) { print_help(stdout, argc > 0 ? argv[0] : NULL); @@ -888,6 +925,25 @@ } +static void update_stations(struct wpa_ctrl *ctrl) +{ + char addr[32], cmd[64]; + + if (!ctrl || !interactive) + return; + + cli_txt_list_flush(&stations); + + if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 0)) + return; + do { + if (os_strcmp(addr, "") != 0) + cli_txt_list_add(&stations, addr); + os_snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr); + } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 0) == 0); +} + + static void hostapd_cli_get_interfaces(struct wpa_ctrl *ctrl, struct dl_list *interfaces) { @@ -940,23 +996,7 @@ hostapd_cli_list_interfaces(ctrl); return 0; } - - hostapd_cli_close_connection(); - os_free(ctrl_ifname); - ctrl_ifname = os_strdup(argv[0]); - if (ctrl_ifname == NULL) - return -1; - - if (hostapd_cli_open_connection(ctrl_ifname)) { - printf("Connected to interface '%s.\n", ctrl_ifname); - if (wpa_ctrl_attach(ctrl_conn) == 0) { - hostapd_cli_attached = 1; - register_event_handler(ctrl_conn); - } else { - printf("Warning: Failed to attach to " - "hostapd.\n"); - } - } else { + if (hostapd_cli_reconnect(argv[0]) != 0) { printf("Could not connect to interface '%s' - re-trying\n", ctrl_ifname); } @@ -984,7 +1024,7 @@ static int hostapd_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; + char cmd[2048]; int res; if (argc != 2) { @@ -1002,6 +1042,44 @@ } +static char ** hostapd_complete_set(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + const char *fields[] = { +#ifdef CONFIG_WPS_TESTING + "wps_version_number", "wps_testing_dummy_cred", + "wps_corrupt_pkhash", +#endif /* CONFIG_WPS_TESTING */ +#ifdef CONFIG_INTERWORKING + "gas_frag_limit", +#endif /* CONFIG_INTERWORKING */ +#ifdef CONFIG_TESTING_OPTIONS + "ext_mgmt_frame_handling", "ext_eapol_frame_io", +#endif /* CONFIG_TESTING_OPTIONS */ +#ifdef CONFIG_MBO + "mbo_assoc_disallow", +#endif /* CONFIG_MBO */ + "deny_mac_file", "accept_mac_file", + }; + int i, num_fields = ARRAY_SIZE(fields); + + if (arg == 1) { + char **res; + + res = os_calloc(num_fields + 1, sizeof(char *)); + if (!res) + return NULL; + for (i = 0; i < num_fields; i++) { + res[i] = os_strdup(fields[i]); + if (!res[i]) + return res; + } + return res; + } + return NULL; +} + + static int hostapd_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[]) { char cmd[256]; @@ -1022,6 +1100,31 @@ } +static char ** hostapd_complete_get(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + const char *fields[] = { + "version", "tls_library", + }; + int i, num_fields = ARRAY_SIZE(fields); + + if (arg == 1) { + char **res; + + res = os_calloc(num_fields + 1, sizeof(char *)); + if (!res) + return NULL; + for (i = 0; i < num_fields; i++) { + res[i] = os_strdup(fields[i]); + if (!res[i]) + return res; + } + return res; + } + return NULL; +} + + #ifdef CONFIG_FST static int hostapd_cli_cmd_fst(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -1185,14 +1288,14 @@ char cmd[2048]; int res; - if (argc < 3 || argc > 5) { - printf("Invalid set_neighbor command: needs 3-5 arguments\n"); + if (argc < 3 || argc > 6) { + printf("Invalid set_neighbor command: needs 3-6 arguments\n"); return -1; } - res = os_snprintf(cmd, sizeof(cmd), "SET_NEIGHBOR %s %s %s %s %s", + res = os_snprintf(cmd, sizeof(cmd), "SET_NEIGHBOR %s %s %s %s %s %s", argv[0], argv[1], argv[2], argc >= 4 ? argv[3] : "", - argc == 5 ? argv[4] : ""); + argc >= 5 ? argv[4] : "", argc == 6 ? argv[5] : ""); if (os_snprintf_error(sizeof(cmd), res)) { printf("Too long SET_NEIGHBOR command.\n"); return -1; @@ -1261,6 +1364,143 @@ } +#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[]); @@ -1273,26 +1513,30 @@ "= pings hostapd" }, { "mib", hostapd_cli_cmd_mib, NULL, "= get MIB variables (dot1x, dot11, radius)" }, - { "relog", hostapd_cli_cmd_relog, NULL, NULL }, - { "status", hostapd_cli_cmd_status, NULL, NULL }, - { "sta", hostapd_cli_cmd_sta, NULL, + { "relog", hostapd_cli_cmd_relog, NULL, + "= reload/truncate debug log output file" }, + { "status", hostapd_cli_cmd_status, NULL, + "= show interface status info" }, + { "sta", hostapd_cli_cmd_sta, hostapd_complete_stations, " = 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_deauthenticate, + hostapd_complete_stations, " = deauthenticate a station" }, { "disassociate", hostapd_cli_cmd_disassociate, - hostapd_complete_disassociate, + hostapd_complete_stations, " = disassociate a station" }, #ifdef CONFIG_TAXONOMY - { "signature", hostapd_cli_cmd_signature, NULL, + { "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, NULL, + { "sa_query", hostapd_cli_cmd_sa_query, hostapd_complete_stations, " = send SA Query to a station" }, #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_WPS @@ -1321,9 +1565,12 @@ { "wps_get_status", hostapd_cli_cmd_wps_get_status, NULL, "= show current WPS status" }, #endif /* CONFIG_WPS */ - { "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent, NULL, NULL }, - { "ess_disassoc", hostapd_cli_cmd_ess_disassoc, NULL, NULL }, - { "bss_tm_req", hostapd_cli_cmd_bss_tm_req, NULL, NULL }, + { "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent, NULL, + "= send Disassociation Imminent notification" }, + { "ess_disassoc", hostapd_cli_cmd_ess_disassoc, NULL, + "= send ESS Dissassociation Imminent notification" }, + { "bss_tm_req", hostapd_cli_cmd_bss_tm_req, NULL, + "= send BSS Transition Management Request" }, { "get_config", hostapd_cli_cmd_get_config, NULL, "= show current configuration" }, { "help", hostapd_cli_cmd_help, hostapd_cli_complete_help, @@ -1331,9 +1578,11 @@ { "interface", hostapd_cli_cmd_interface, hostapd_complete_interface, "[ifname] = show interfaces/select interface" }, #ifdef CONFIG_FST - { "fst", hostapd_cli_cmd_fst, NULL, NULL }, + { "fst", hostapd_cli_cmd_fst, NULL, + " = send FST-MANAGER control interface command" }, #endif /* CONFIG_FST */ - { "raw", hostapd_cli_cmd_raw, 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, @@ -1340,26 +1589,95 @@ "= show full hostapd_cli license" }, { "quit", hostapd_cli_cmd_quit, NULL, "= exit hostapd_cli" }, - { "set", hostapd_cli_cmd_set, NULL, NULL }, - { "get", hostapd_cli_cmd_get, NULL, NULL }, - { "set_qos_map_set", hostapd_cli_cmd_set_qos_map_set, NULL, NULL }, - { "send_qos_map_conf", hostapd_cli_cmd_send_qos_map_conf, NULL, NULL }, - { "chan_switch", hostapd_cli_cmd_chan_switch, NULL, NULL }, - { "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif, NULL, NULL }, - { "hs20_deauth_req", hostapd_cli_cmd_hs20_deauth_req, NULL, NULL }, - { "vendor", hostapd_cli_cmd_vendor, NULL, NULL }, - { "enable", hostapd_cli_cmd_enable, NULL, NULL }, - { "reload", hostapd_cli_cmd_reload, NULL, NULL }, - { "disable", hostapd_cli_cmd_disable, NULL, NULL }, - { "erp_flush", hostapd_cli_cmd_erp_flush, NULL, NULL }, - { "log_level", hostapd_cli_cmd_log_level, NULL, NULL }, - { "pmksa", hostapd_cli_cmd_pmksa, NULL, NULL }, - { "pmksa_flush", hostapd_cli_cmd_pmksa_flush, NULL, NULL }, - { "set_neighbor", hostapd_cli_cmd_set_neighbor, NULL, NULL }, - { "remove_neighbor", hostapd_cli_cmd_remove_neighbor, NULL, NULL }, - { "req_lci", hostapd_cli_cmd_req_lci, NULL, NULL }, - { "req_range", hostapd_cli_cmd_req_range, NULL, NULL }, - { "driver_flags", hostapd_cli_cmd_driver_flags, NULL, NULL }, + { "set", hostapd_cli_cmd_set, hostapd_complete_set, + " = 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 } }; @@ -1471,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'; @@ -1504,19 +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; - register_event_handler(ctrl_conn); - } else { - printf("Warning: Failed to attach to " - "hostapd.\n"); - } - } - } + if (!ctrl_conn && hostapd_cli_reconnect(ctrl_ifname) == 0) + printf("Connection to hostapd re-established\n"); if (ctrl_conn) hostapd_cli_recv_pending(ctrl_conn, 1, 0); eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL); @@ -1611,17 +1918,34 @@ static void hostapd_cli_interactive(void) { + char *hfile = NULL; + char *home; + printf("\nInteractive mode\n\n"); +#ifdef CONFIG_HOSTAPD_CLI_HISTORY_DIR + home = CONFIG_HOSTAPD_CLI_HISTORY_DIR; +#else /* CONFIG_HOSTAPD_CLI_HISTORY_DIR */ + home = getenv("HOME"); +#endif /* CONFIG_HOSTAPD_CLI_HISTORY_DIR */ + if (home) { + const char *fname = ".hostapd_cli_history"; + int hfile_len = os_strlen(home) + 1 + os_strlen(fname) + 1; + hfile = os_malloc(hfile_len); + if (hfile) + os_snprintf(hfile, hfile_len, "%s/%s", home, fname); + } + eloop_register_signal_terminate(hostapd_cli_eloop_terminate, NULL); edit_init(hostapd_cli_edit_cmd_cb, hostapd_cli_edit_eof_cb, - hostapd_cli_edit_completion_cb, NULL, NULL, NULL); + hostapd_cli_edit_completion_cb, NULL, hfile, NULL); eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL); eloop_run(); cli_txt_list_flush(&stations); - edit_deinit(NULL, NULL); + edit_deinit(hfile, NULL); + os_free(hfile); eloop_cancel_timeout(hostapd_cli_ping, NULL, NULL); } @@ -1748,7 +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"); @@ -1769,17 +2093,8 @@ continue; } - if (interactive || action_file) { - if (wpa_ctrl_attach(ctrl_conn) == 0) { - hostapd_cli_attached = 1; - register_event_handler(ctrl_conn); - } else { - printf("Warning: Failed to attach to hostapd.\n"); - if (action_file) - return -1; - } - } - + if (action_file && !hostapd_cli_attached) + return -1; if (daemonize && os_daemonize(pid_file) && eloop_sock_requeue()) return -1; --- contrib/wpa/hostapd/main.c.orig +++ contrib/wpa/hostapd/main.c @@ -1,6 +1,6 @@ /* * hostapd / main() - * Copyright (c) 2002-2016, 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); @@ -248,7 +254,7 @@ * * This function is used to parse configuration file for a full interface (one * or more BSSes sharing the same radio) and allocate memory for the BSS - * interfaces. No actiual driver operations are started. + * interfaces. No actual driver operations are started. */ static struct hostapd_iface * hostapd_interface_init(struct hapd_interfaces *interfaces, const char *if_name, @@ -451,7 +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-2016, Jouni Malinen " + "Copyright (c) 2002-2019, Jouni Malinen " "and contributors\n"); } @@ -480,10 +486,13 @@ " -f log output to debug file instead of stdout\n" #endif /* CONFIG_DEBUG_FILE */ #ifdef CONFIG_DEBUG_LINUX_TRACING - " -T = record to Linux tracing in addition to logging\n" + " -T record to Linux tracing in addition to logging\n" " (records all messages regardless of debug verbosity)\n" #endif /* CONFIG_DEBUG_LINUX_TRACING */ " -i list of interface names to use\n" +#ifdef CONFIG_DEBUG_SYSLOG + " -s log output to syslog instead of stdout\n" +#endif /* CONFIG_DEBUG_SYSLOG */ " -S start all the interfaces synchronously\n" " -t include timestamps in some debug messages\n" " -v show hostapd version\n"); @@ -549,14 +558,14 @@ static int hostapd_get_interface_names(char ***if_names, size_t *if_names_size, - char *optarg) + char *arg) { char *if_name, *tmp, **nnames; size_t i; - if (!optarg) + if (!arg) return -1; - if_name = strtok_r(optarg, ",", &tmp); + if_name = strtok_r(arg, ",", &tmp); while (if_name) { nnames = os_realloc_array(*if_names, 1 + *if_names_size, @@ -659,9 +668,17 @@ interfaces.global_iface_name = NULL; interfaces.global_ctrl_sock = -1; dl_list_init(&interfaces.global_ctrl_dst); +#ifdef CONFIG_ETH_P_OUI + dl_list_init(&interfaces.eth_p_oui); +#endif /* CONFIG_ETH_P_OUI */ +#ifdef CONFIG_DPP + interfaces.dpp = dpp_global_init(); + if (!interfaces.dpp) + return -1; +#endif /* CONFIG_DPP */ for (;;) { - c = getopt(argc, argv, "b:Bde:f:hi:KP:STtu:vg:G:"); + c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:vg:G:"); if (c < 0) break; switch (c) { @@ -718,6 +735,11 @@ bss_config = tmp_bss; bss_config[num_bss_configs++] = optarg; break; +#ifdef CONFIG_DEBUG_SYSLOG + case 's': + wpa_debug_syslog = 1; + break; +#endif /* CONFIG_DEBUG_SYSLOG */ case 'S': start_ifaces_in_sync = 1; break; @@ -746,6 +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(); @@ -877,11 +903,16 @@ } os_free(interfaces.iface); +#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(); --- 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/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 --- contrib/wpa/hs20/client/est.c.orig +++ contrib/wpa/hs20/client/est.c @@ -16,6 +16,7 @@ #include #include #include +#include #ifdef OPENSSL_IS_BORINGSSL #include #endif /* OPENSSL_IS_BORINGSSL */ @@ -219,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; @@ -352,9 +357,17 @@ } } #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); @@ -666,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; @@ -721,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_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; @@ -2346,15 +2502,7 @@ return -1; } -#ifdef ANDROID - /* Allow processes running with Group ID as AID_WIFI - * to read/write files from osu-info directory - */ - if (chown(fname, -1, AID_WIFI)) { - wpa_printf(MSG_INFO, "Could not chown osu-info directory: %s", - strerror(errno)); - } -#endif /* ANDROID */ + android_update_permission(fname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); snprintf(buf, sizeof(buf), "SET osu_dir %s", fname); if (wpa_command(ifname, buf) < 0) { @@ -2920,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); @@ -3040,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) { @@ -3057,6 +3198,9 @@ case 'N': no_prod_assoc = 1; break; + case 'o': + ctx.osu_ssid = optarg; + break; case 'O': friendly_name = optarg; break; --- 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/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/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,17 +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 : - 80); + /* 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; @@ -607,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 && @@ -625,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; @@ -643,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; @@ -755,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; } @@ -788,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 @@ -920,14 +962,11 @@ enum hostapd_chan_status acs_init(struct hostapd_iface *iface) { - int err; - wpa_printf(MSG_INFO, "ACS: Automatic channel selection started, this may take a bit"); if (iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) { wpa_printf(MSG_INFO, "ACS: Offloading to driver"); - err = hostapd_drv_do_acs(iface->bss[0]); - if (err) + if (hostapd_drv_do_acs(iface->bss[0])) return HOSTAPD_CHAN_INVALID; return HOSTAPD_CHAN_ACS; } @@ -937,8 +976,7 @@ 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,6 +38,10 @@ } +#ifndef DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES +#define DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES 0 +#endif /* DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES */ + void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) { dl_list_init(&bss->anqp_elem); @@ -55,6 +61,10 @@ bss->wpa_group_rekey = 600; bss->wpa_gmk_rekey = 86400; + bss->wpa_group_update_count = 4; + bss->wpa_pairwise_update_count = 4; + bss->wpa_disable_eapol_key_retries = + DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES; bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK; bss->wpa_pairwise = WPA_CIPHER_TKIP; bss->wpa_group = WPA_CIPHER_TKIP; @@ -88,13 +98,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; } @@ -155,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; @@ -192,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; } @@ -207,6 +259,12 @@ { 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; @@ -221,6 +279,8 @@ } while (fgets(buf, sizeof(buf), f)) { + int vlan_id = 0; + line++; if (buf[0] == '#') @@ -236,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; } @@ -249,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); @@ -262,7 +353,6 @@ ret = -1; break; } - pos++; ok = 0; len = os_strlen(pos); @@ -281,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; } @@ -329,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); } @@ -380,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; @@ -420,10 +529,38 @@ } +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; @@ -436,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); @@ -463,10 +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); @@ -477,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; @@ -498,7 +632,7 @@ os_free(r1kh_prev); } } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_WPS os_free(conf->wps_pin_requests); @@ -511,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); @@ -530,6 +666,7 @@ os_free(conf->roaming_consortium); os_free(conf->venue_name); + os_free(conf->venue_url); os_free(conf->nai_realm_data); os_free(conf->network_auth_type); os_free(conf->anqp_3gpp_cell_net); @@ -559,11 +696,22 @@ 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); @@ -570,6 +718,9 @@ wpabuf_free(conf->assocresp_elements); os_free(conf->sae_groups); +#ifdef CONFIG_OWE + os_free(conf->owe_groups); +#endif /* CONFIG_OWE */ os_free(conf->wowlan_triggers); @@ -577,11 +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); } @@ -706,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", @@ -728,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; @@ -802,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 || @@ -812,7 +980,7 @@ "string"); return -1; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_IEEE80211N if (full_config && conf->ieee80211n && @@ -848,6 +1016,16 @@ wpa_printf(MSG_ERROR, "VHT (IEEE 802.11ac) with WEP is not allowed, disabling VHT capabilities"); } + + if (full_config && conf->ieee80211ac && bss->wpa && + !(bss->wpa_pairwise & WPA_CIPHER_CCMP) && + !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | + WPA_CIPHER_CCMP_256 | WPA_CIPHER_GCMP_256))) + { + bss->disable_11ac = 1; + wpa_printf(MSG_ERROR, + "VHT (IEEE 802.11ac) with WPA/WPA2 requires CCMP/GCMP to be enabled, disabling VHT capabilities"); + } #endif /* CONFIG_IEEE80211AC */ #ifdef CONFIG_WPS @@ -866,7 +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; @@ -895,6 +1075,15 @@ } #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; } @@ -976,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; @@ -1031,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 @@ -42,6 +42,7 @@ #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; @@ -122,6 +123,7 @@ 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 @@ -132,6 +134,7 @@ }; #define PMK_LEN 32 +#define KEYID_LEN 32 #define MIN_PASSPHRASE_LEN 8 #define MAX_PASSPHRASE_LEN 63 struct hostapd_sta_wpa_psk_short { @@ -145,9 +148,11 @@ 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 { @@ -160,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; @@ -169,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 { @@ -201,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 @@ -224,7 +238,20 @@ 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 */ @@ -242,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; @@ -287,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 @@ -313,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, @@ -319,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 @@ -351,12 +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; @@ -419,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; @@ -464,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 */ @@ -486,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; @@ -508,7 +561,7 @@ 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]; @@ -519,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; @@ -547,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_*) */ @@ -566,7 +628,10 @@ struct wpabuf *assocresp_elements; unsigned int sae_anti_clogging_threshold; + unsigned int sae_sync; + int sae_require_mfp; int *sae_groups; + struct sae_password_entry *sae_passwords; char *wowlan_triggers; /* Wake-on-WLAN triggers */ @@ -574,6 +639,8 @@ u8 bss_load_test[5]; u8 bss_load_test_set; struct wpabuf *own_ie_override; + int sae_reflection_attack; + struct wpabuf *sae_commit_override; #endif /* CONFIG_TESTING_OPTIONS */ #define MESH_ENABLED BIT(0) @@ -591,14 +658,79 @@ #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 { @@ -608,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, @@ -620,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; @@ -635,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; @@ -675,6 +812,7 @@ u8 vht_oper_chwidth; u8 vht_oper_centr_freq_seg0_idx; u8 vht_oper_centr_freq_seg1_idx; + u8 ht40_plus_minus_allowed; /* Use driver-generated interface addresses when adding multiple BSSs */ u8 use_driver_iface_addr; @@ -707,6 +845,22 @@ 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; }; @@ -714,6 +868,7 @@ 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); @@ -722,7 +877,7 @@ 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_valid(struct hostapd_vlan *vlan, struct vlan_description *vlan_desc); @@ -733,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" @@ -99,6 +100,13 @@ goto fail; #endif /* CONFIG_FST */ +#ifdef CONFIG_FILS + pos = hostapd_eid_fils_indic(hapd, buf, 0); + if (add_buf_data(&beacon, buf, pos - buf) < 0 || + add_buf_data(&proberesp, buf, pos - buf) < 0) + goto fail; +#endif /* CONFIG_FILS */ + if (add_buf(&beacon, hapd->wps_beacon_ie) < 0 || add_buf(&proberesp, hapd->wps_probe_resp_ie) < 0) goto fail; @@ -168,7 +176,8 @@ #endif /* CONFIG_HS20 */ #ifdef CONFIG_MBO - if (hapd->conf->mbo_enabled) { + if (hapd->conf->mbo_enabled || + OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd)) { pos = hostapd_eid_mbo(hapd, buf, sizeof(buf)); if (add_buf_data(&beacon, buf, pos - buf) < 0 || add_buf_data(&proberesp, buf, pos - buf) < 0 || @@ -177,6 +186,13 @@ } #endif /* CONFIG_MBO */ +#ifdef CONFIG_OWE + pos = hostapd_eid_owe_trans(hapd, buf, sizeof(buf)); + if (add_buf_data(&beacon, buf, pos - buf) < 0 || + add_buf_data(&proberesp, buf, pos - buf) < 0) + goto fail; +#endif /* CONFIG_OWE */ + add_buf(&beacon, hapd->conf->vendor_elements); add_buf(&proberesp, hapd->conf->vendor_elements); add_buf(&assocresp, hapd->conf->assocresp_elements); @@ -340,10 +356,44 @@ int hostapd_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); } @@ -554,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); } @@ -694,6 +744,15 @@ sta = ap_get_sta(hapd, dst); if (!sta || !(sta->flags & WLAN_STA_ASSOC)) bssid = wildcard_bssid; + } else if (is_broadcast_ether_addr(dst) && + len > 0 && data[0] == WLAN_ACTION_PUBLIC) { + /* + * The only current use case of Public Action frames with + * broadcast destination address is DPP PKEX. That case is + * directing all devices and not just the STAs within the BSS, + * so have to use the wildcard BSSID value. + */ + bssid = wildcard_bssid; } return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst, hapd->own_addr, bssid, data, len, 0); @@ -774,7 +833,9 @@ if ((acs_ch_list_all || freq_range_list_includes(&hapd->iface->conf->acs_ch_list, chan->chan)) && - !(chan->flag & HOSTAPD_CHAN_DISABLED)) + !(chan->flag & HOSTAPD_CHAN_DISABLED) && + !(hapd->iface->conf->acs_exclude_dfs && + (chan->flag & HOSTAPD_CHAN_RADAR))) int_array_add_unique(freq_list, chan->freq); } } @@ -829,6 +890,9 @@ &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 @@ -72,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, @@ -103,6 +103,14 @@ unsigned int freq, unsigned int wait, const u8 *dst, const u8 *data, size_t len); +static inline void +hostapd_drv_send_action_cancel_wait(struct hostapd_data *hapd) +{ + if (!hapd->driver || !hapd->driver->send_action_cancel_wait || + !hapd->drv_priv) + return; + hapd->driver->send_action_cancel_wait(hapd->drv_priv); +} int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr, u16 auth_alg); int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr, @@ -274,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); } @@ -347,4 +356,22 @@ 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,7 +57,11 @@ HOSTAPD_LEVEL_DEBUG, "MLME-AUTHENTICATE.indication(" MACSTR ", %s)", MAC2STR(sta->addr), mlme_auth_alg_str(sta->auth_alg)); - if (sta->auth_alg != WLAN_AUTH_FT && !(sta->flags & WLAN_STA_MFP)) + if (sta->auth_alg != WLAN_AUTH_FT && + sta->auth_alg != WLAN_AUTH_FILS_SK && + sta->auth_alg != WLAN_AUTH_FILS_SK_PFS && + sta->auth_alg != WLAN_AUTH_FILS_PK && + !(sta->flags & WLAN_STA_MFP)) mlme_deletekeys_request(hapd, sta); ap_sta_clear_disconnect_timeouts(hapd, sta); } @@ -105,7 +109,10 @@ HOSTAPD_LEVEL_DEBUG, "MLME-ASSOCIATE.indication(" MACSTR ")", MAC2STR(sta->addr)); - if (sta->auth_alg != WLAN_AUTH_FT) + if (sta->auth_alg != WLAN_AUTH_FT && + sta->auth_alg != WLAN_AUTH_FILS_SK && + sta->auth_alg != WLAN_AUTH_FILS_SK_PFS && + sta->auth_alg != WLAN_AUTH_FILS_PK) mlme_deletekeys_request(hapd, sta); ap_sta_clear_disconnect_timeouts(hapd, sta); } @@ -130,7 +137,10 @@ 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,10 +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"); @@ -183,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; --- 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" @@ -30,6 +31,7 @@ #include "hs20.h" #include "dfs.h" #include "taxonomy.h" +#include "ieee802_11_auth.h" #ifdef NEED_AP_MLME @@ -392,7 +394,16 @@ 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) @@ -443,8 +454,9 @@ /* Extended supported rates */ pos = hostapd_eid_ext_supp_rates(hapd, pos); - /* RSN, MDIE, WPA */ - pos = hostapd_eid_wpa(hapd, pos, epos - pos); + /* RSN, MDIE */ + if (hapd->conf->wpa != WPA_PROTO_WPA) + pos = hostapd_eid_wpa(hapd, pos, epos - pos); pos = hostapd_eid_bss_load(hapd, pos, epos - pos); @@ -491,10 +503,27 @@ 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); @@ -526,6 +555,7 @@ #endif /* CONFIG_HS20 */ pos = hostapd_eid_mbo(hapd, pos, (u8 *) resp + buflen - pos); + pos = hostapd_eid_owe_trans(hapd, pos, (u8 *) resp + buflen - pos); if (hapd->conf->vendor_elements) { os_memcpy(pos, wpabuf_head(hapd->conf->vendor_elements), @@ -618,7 +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; @@ -628,6 +658,7 @@ dl_list_del(&info->list); dl_list_add_tail(&iface->sta_seen, &info->list); os_get_reltime(&info->last_seen); + info->ssi_signal = ssi_signal; return; } @@ -637,6 +668,7 @@ return; os_memcpy(info->addr, addr, ETH_ALEN); os_get_reltime(&info->last_seen); + info->ssi_signal = ssi_signal; if (iface->num_sta_seen >= iface->conf->track_sta_max_num) { /* Expire oldest entry to make room for a new one */ @@ -707,14 +739,30 @@ int ret; u16 csa_offs[2]; size_t csa_offs_len; + u32 session_timeout, acct_interim_interval; + struct vlan_description vlan_id; + struct hostapd_sta_wpa_psk_short *psk = NULL; + char *identity = NULL; + char *radius_cui = NULL; if (len < IEEE80211_HDRLEN) return; ie = ((const u8 *) mgmt) + IEEE80211_HDRLEN; if (hapd->iconf->track_sta_max_num) - sta_track_add(hapd->iface, mgmt->sa); + sta_track_add(hapd->iface, mgmt->sa, ssi_signal); ie_len = len - IEEE80211_HDRLEN; + ret = ieee802_11_allowed_address(hapd, mgmt->sa, (const u8 *) mgmt, len, + &session_timeout, + &acct_interim_interval, &vlan_id, + &psk, &identity, &radius_cui, 1); + if (ret == HOSTAPD_ACL_REJECT) { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, + "Ignore Probe Request frame from " MACSTR + " due to ACL reject ", MAC2STR(mgmt->sa)); + return; + } + for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx, mgmt->sa, mgmt->da, mgmt->bssid, @@ -721,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) { @@ -909,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) @@ -1033,7 +1084,16 @@ } #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) { @@ -1100,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 - @@ -1155,10 +1217,29 @@ 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); @@ -1189,6 +1270,8 @@ #endif /* CONFIG_HS20 */ tailpos = hostapd_eid_mbo(hapd, tailpos, tail + tail_len - tailpos); + tailpos = hostapd_eid_owe_trans(hapd, tailpos, + tail + tail_len - tailpos); if (hapd->conf->vendor_elements) { os_memcpy(tailpos, wpabuf_head(hapd->conf->vendor_elements), @@ -1211,6 +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)) == @@ -1274,7 +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,7 +21,7 @@ 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 * --- 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. @@ -26,6 +26,30 @@ #include "taxonomy.h" +static size_t hostapd_write_ht_mcs_bitmask(char *buf, size_t buflen, + size_t curr_len, const u8 *mcs_set) +{ + int ret; + size_t len = curr_len; + + ret = os_snprintf(buf + len, buflen - len, + "ht_mcs_bitmask="); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + + /* 77 first bits (+ 3 reserved bits) */ + len += wpa_snprintf_hex(buf + len, buflen - len, mcs_set, 10); + + ret = os_snprintf(buf + len, buflen - len, "\n"); + if (os_snprintf_error(buflen - len, ret)) + return curr_len; + len += ret; + + return len; +} + + static int hostapd_get_sta_tx_rx(struct hostapd_data *hapd, struct sta_info *sta, char *buf, size_t buflen) @@ -32,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=%llu\ntx_bytes=%llu\ninactive_msec=%lu\n", + "rx_bytes=%llu\ntx_bytes=%llu\ninactive_msec=%lu\n" + "signal=%d\n", data.rx_packets, data.tx_packets, - data.rx_bytes, data.tx_bytes, data.inactive_msec); + data.rx_bytes, data.tx_bytes, data.inactive_msec, + data.signal); if (os_snprintf_error(buflen, ret)) return 0; - return ret; + len += ret; + + ret = os_snprintf(buf + len, buflen - len, "rx_rate_info=%lu", + data.current_rx_rate); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + if (data.flags & STA_DRV_DATA_RX_MCS) { + ret = os_snprintf(buf + len, buflen - len, " mcs %u", + data.rx_mcs); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + if (data.flags & STA_DRV_DATA_RX_VHT_MCS) { + ret = os_snprintf(buf + len, buflen - len, " vhtmcs %u", + data.rx_vhtmcs); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + if (data.flags & STA_DRV_DATA_RX_VHT_NSS) { + ret = os_snprintf(buf + len, buflen - len, " vhtnss %u", + data.rx_vht_nss); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + if (data.flags & STA_DRV_DATA_RX_SHORT_GI) { + ret = os_snprintf(buf + len, buflen - len, " shortGI"); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + ret = os_snprintf(buf + len, buflen - len, "\n"); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + + ret = os_snprintf(buf + len, buflen - len, "tx_rate_info=%lu", + data.current_tx_rate); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + if (data.flags & STA_DRV_DATA_TX_MCS) { + ret = os_snprintf(buf + len, buflen - len, " mcs %u", + data.tx_mcs); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + if (data.flags & STA_DRV_DATA_TX_VHT_MCS) { + ret = os_snprintf(buf + len, buflen - len, " vhtmcs %u", + data.tx_vhtmcs); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + if (data.flags & STA_DRV_DATA_TX_VHT_NSS) { + ret = os_snprintf(buf + len, buflen - len, " vhtnss %u", + data.tx_vht_nss); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + if (data.flags & STA_DRV_DATA_TX_SHORT_GI) { + ret = os_snprintf(buf + len, buflen - len, " shortGI"); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + ret = os_snprintf(buf + len, buflen - len, "\n"); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + + if ((sta->flags & WLAN_STA_VHT) && sta->vht_capabilities) { + ret = os_snprintf(buf + len, buflen - len, + "rx_vht_mcs_map=%04x\n" + "tx_vht_mcs_map=%04x\n", + le_to_host16(sta->vht_capabilities-> + vht_supported_mcs_set.rx_map), + le_to_host16(sta->vht_capabilities-> + vht_supported_mcs_set.tx_map)); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + + if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities) { + len = hostapd_write_ht_mcs_bitmask(buf, buflen, len, + sta->ht_capabilities-> + supported_mcs_set); + } + + if (data.flags & STA_DRV_DATA_LAST_ACK_RSSI) { + ret = os_snprintf(buf + len, buflen - len, + "last_ack_signal=%d\n", data.last_ack_rssi); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + + return len; } @@ -89,6 +207,7 @@ char *buf, size_t buflen) { int len, res, ret, i; + const char *keyid; if (!sta) return 0; @@ -176,6 +295,60 @@ 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; } @@ -278,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; @@ -477,7 +650,8 @@ size_t buflen) { struct hostapd_iface *iface = hapd->iface; - int len = 0, ret; + struct hostapd_hw_modes *mode = iface->current_mode; + int len = 0, ret, j; size_t i; ret = os_snprintf(buf + len, buflen - len, @@ -537,13 +711,17 @@ "channel=%u\n" "secondary_channel=%d\n" "ieee80211n=%d\n" - "ieee80211ac=%d\n", + "ieee80211ac=%d\n" + "beacon_int=%u\n" + "dtim_period=%d\n", iface->conf->channel, iface->conf->ieee80211n && !hapd->conf->disable_11n ? iface->conf->secondary_channel : 0, iface->conf->ieee80211n && !hapd->conf->disable_11n, iface->conf->ieee80211ac && - !hapd->conf->disable_11ac); + !hapd->conf->disable_11ac, + iface->conf->beacon_int, + hapd->conf->dtim_period); if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -551,15 +729,76 @@ ret = os_snprintf(buf + len, buflen - len, "vht_oper_chwidth=%d\n" "vht_oper_centr_freq_seg0_idx=%d\n" - "vht_oper_centr_freq_seg1_idx=%d\n", + "vht_oper_centr_freq_seg1_idx=%d\n" + "vht_caps_info=%08x\n", iface->conf->vht_oper_chwidth, iface->conf->vht_oper_centr_freq_seg0_idx, - iface->conf->vht_oper_centr_freq_seg1_idx); + iface->conf->vht_oper_centr_freq_seg1_idx, + iface->conf->vht_capab); if (os_snprintf_error(buflen - len, ret)) return len; len += ret; } + if (iface->conf->ieee80211ac && !hapd->conf->disable_11ac && mode) { + u16 rxmap = WPA_GET_LE16(&mode->vht_mcs_set[0]); + u16 txmap = WPA_GET_LE16(&mode->vht_mcs_set[4]); + + ret = os_snprintf(buf + len, buflen - len, + "rx_vht_mcs_map=%04x\n" + "tx_vht_mcs_map=%04x\n", + rxmap, txmap); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + } + + if (iface->conf->ieee80211n && !hapd->conf->disable_11n) { + ret = os_snprintf(buf + len, buflen - len, + "ht_caps_info=%04x\n", + hapd->iconf->ht_capab); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + } + + if (iface->conf->ieee80211n && !hapd->conf->disable_11n && mode) { + len = hostapd_write_ht_mcs_bitmask(buf, buflen, len, + mode->mcs_set); + } + + if (iface->current_rates && iface->num_rates) { + ret = os_snprintf(buf + len, buflen - len, "supported_rates="); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + + for (j = 0; j < iface->num_rates; j++) { + ret = os_snprintf(buf + len, buflen - len, "%s%02x", + j > 0 ? " " : "", + iface->current_rates[j].rate / 5); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + } + ret = os_snprintf(buf + len, buflen - len, "\n"); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + } + + for (j = 0; mode && j < mode->num_channels; j++) { + if (mode->channels[j].freq == iface->freq) { + ret = os_snprintf(buf + len, buflen - len, + "max_txpower=%u\n", + mode->channels[j].max_tx_power); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + break; + } + } + for (i = 0; i < iface->num_bss; i++) { struct hostapd_data *bss = iface->bss[i]; ret = os_snprintf(buf + len, buflen - len, @@ -578,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; } @@ -639,3 +887,108 @@ { 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 @@ -32,5 +32,9 @@ 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 */ @@ -747,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) @@ -767,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; + } } } @@ -776,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; @@ -840,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, @@ -1055,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; @@ -146,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); - } - } } @@ -176,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" @@ -31,12 +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) { @@ -45,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; @@ -171,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 @@ -198,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 && @@ -230,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)", @@ -252,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 { @@ -263,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 @@ -293,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); @@ -307,7 +389,7 @@ goto fail; } } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ } else if (hapd->conf->wps_state) { #ifdef CONFIG_WPS struct wpabuf *wps; @@ -375,19 +457,160 @@ 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; @@ -397,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); @@ -414,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; @@ -464,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, @@ -471,23 +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, 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; @@ -532,14 +830,26 @@ hapd->iconf->channel = channel; hapd->iconf->ieee80211n = ht; - if (!ht) + if (!ht) { hapd->iconf->ieee80211ac = 0; + } else if (hapd->iconf->ch_switch_vht_config) { + /* CHAN_SWITCH VHT config */ + if (hapd->iconf->ch_switch_vht_config & + CH_SWITCH_VHT_ENABLED) + hapd->iconf->ieee80211ac = 1; + else if (hapd->iconf->ch_switch_vht_config & + CH_SWITCH_VHT_DISABLED) + hapd->iconf->ieee80211ac = 0; + } + hapd->iconf->ch_switch_vht_config = 0; + hapd->iconf->secondary_channel = offset; hapd->iconf->vht_oper_chwidth = chwidth; hapd->iconf->vht_oper_centr_freq_seg0_idx = seg0_idx; hapd->iconf->vht_oper_centr_freq_seg1_idx = seg1_idx; - is_dfs = ieee80211_is_dfs(freq); + is_dfs = ieee80211_is_dfs(freq, hapd->iface->hw_features, + hapd->iface->num_hw_features); if (hapd->csa_in_progress && freq == hapd->cs_freq_params.freq) { @@ -552,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 */ } @@ -690,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, @@ -709,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) { @@ -730,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) @@ -748,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); @@ -755,6 +1106,7 @@ } +#ifndef NEED_AP_MLME static void hostapd_action_rx(struct hostapd_data *hapd, struct rx_mgmt *drv_mgmt) { @@ -762,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); @@ -773,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) { @@ -781,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); @@ -807,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 @@ -891,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; @@ -1122,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) { @@ -1164,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) { @@ -1237,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 || @@ -1314,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; @@ -1351,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: @@ -1367,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; --- 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); } @@ -161,8 +181,12 @@ wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS); if (hapd->conf->hs20_osu_providers_count) wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST); + if (hapd->conf->hs20_osu_providers_nai_count) + wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_NAI_LIST); if (hapd->conf->hs20_icons_count) wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST); + if (hapd->conf->hs20_operator_icon_count) + wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_ICON_METADATA); gas_anqp_set_element_len(buf, len); } #endif /* CONFIG_HS20 */ @@ -255,20 +279,29 @@ wpabuf_put_le16(buf, ANQP_DOMAIN_NAME); if (get_anqp_elem(hapd, ANQP_EMERGENCY_ALERT_URI)) wpabuf_put_le16(buf, ANQP_EMERGENCY_ALERT_URI); + if (get_anqp_elem(hapd, ANQP_TDLS_CAPABILITY)) + wpabuf_put_le16(buf, ANQP_TDLS_CAPABILITY); if (get_anqp_elem(hapd, ANQP_EMERGENCY_NAI)) wpabuf_put_le16(buf, ANQP_EMERGENCY_NAI); if (get_anqp_elem(hapd, ANQP_NEIGHBOR_REPORT)) wpabuf_put_le16(buf, ANQP_NEIGHBOR_REPORT); - for (id = 273; id < 277; id++) { - if (get_anqp_elem(hapd, id)) - wpabuf_put_le16(buf, id); - } - if (get_anqp_elem(hapd, ANQP_VENUE_URL)) +#ifdef CONFIG_FILS + if (!dl_list_empty(&hapd->conf->fils_realms) || + get_anqp_elem(hapd, ANQP_FILS_REALM_INFO)) + wpabuf_put_le16(buf, ANQP_FILS_REALM_INFO); +#endif /* CONFIG_FILS */ + if (get_anqp_elem(hapd, ANQP_CAG)) + wpabuf_put_le16(buf, ANQP_CAG); + if (hapd->conf->venue_url || get_anqp_elem(hapd, ANQP_VENUE_URL)) wpabuf_put_le16(buf, ANQP_VENUE_URL); if (get_anqp_elem(hapd, ANQP_ADVICE_OF_CHARGE)) wpabuf_put_le16(buf, ANQP_ADVICE_OF_CHARGE); if (get_anqp_elem(hapd, ANQP_LOCAL_CONTENT)) wpabuf_put_le16(buf, ANQP_LOCAL_CONTENT); + for (id = 280; id < 300; id++) { + if (get_anqp_elem(hapd, id)) + wpabuf_put_le16(buf, id); + } #ifdef CONFIG_HS20 anqp_add_hs_capab_list(hapd, buf); #endif /* CONFIG_HS20 */ @@ -299,6 +332,29 @@ } +static void anqp_add_venue_url(struct hostapd_data *hapd, struct wpabuf *buf) +{ + if (anqp_add_override(hapd, buf, ANQP_VENUE_URL)) + return; + + if (hapd->conf->venue_url) { + u8 *len; + unsigned int i; + + len = gas_anqp_add_element(buf, ANQP_VENUE_URL); + for (i = 0; i < hapd->conf->venue_url_count; i++) { + struct hostapd_venue_url *url; + + url = &hapd->conf->venue_url[i]; + wpabuf_put_u8(buf, 1 + url->url_len); + wpabuf_put_u8(buf, url->venue_number); + wpabuf_put_data(buf, url->url, url->url_len); + } + gas_anqp_set_element_len(buf, len); + } +} + + static void anqp_add_network_auth_type(struct hostapd_data *hapd, struct wpabuf *buf) { @@ -548,6 +604,36 @@ } +#ifdef CONFIG_FILS +static void anqp_add_fils_realm_info(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + size_t count; + + if (anqp_add_override(hapd, buf, ANQP_FILS_REALM_INFO)) + return; + + count = dl_list_len(&hapd->conf->fils_realms); + if (count > 10000) + count = 10000; + if (count) { + struct fils_realm *realm; + + wpabuf_put_le16(buf, ANQP_FILS_REALM_INFO); + wpabuf_put_le16(buf, 2 * count); + + dl_list_for_each(realm, &hapd->conf->fils_realms, + struct fils_realm, list) { + if (count == 0) + break; + wpabuf_put_data(buf, realm->hash, 2); + count--; + } + } +} +#endif /* CONFIG_FILS */ + + #ifdef CONFIG_HS20 static void anqp_add_operator_friendly_name(struct hostapd_data *hapd, @@ -621,6 +707,29 @@ } +static void anqp_add_icon(struct wpabuf *buf, struct hostapd_bss_config *bss, + const char *name) +{ + size_t j; + struct hs20_icon *icon = NULL; + + for (j = 0; j < bss->hs20_icons_count && !icon; j++) { + if (os_strcmp(name, bss->hs20_icons[j].name) == 0) + icon = &bss->hs20_icons[j]; + } + if (!icon) + return; /* icon info not found */ + + wpabuf_put_le16(buf, icon->width); + wpabuf_put_le16(buf, icon->height); + wpabuf_put_data(buf, icon->language, 3); + wpabuf_put_u8(buf, os_strlen(icon->type)); + wpabuf_put_str(buf, icon->type); + wpabuf_put_u8(buf, os_strlen(icon->name)); + wpabuf_put_str(buf, icon->name); +} + + static void anqp_add_osu_provider(struct wpabuf *buf, struct hostapd_bss_config *bss, struct hs20_osu_provider *p) @@ -649,32 +758,14 @@ /* OSU Method List */ count = wpabuf_put(buf, 1); - for (i = 0; p->method_list[i] >= 0; i++) + for (i = 0; p->method_list && p->method_list[i] >= 0; i++) wpabuf_put_u8(buf, p->method_list[i]); *count = i; /* Icons Available */ len2 = wpabuf_put(buf, 2); - for (i = 0; i < p->icons_count; i++) { - size_t j; - struct hs20_icon *icon = NULL; - - for (j = 0; j < bss->hs20_icons_count && !icon; j++) { - if (os_strcmp(p->icons[i], bss->hs20_icons[j].name) == - 0) - icon = &bss->hs20_icons[j]; - } - if (!icon) - continue; /* icon info not found */ - - wpabuf_put_le16(buf, icon->width); - wpabuf_put_le16(buf, icon->height); - wpabuf_put_data(buf, icon->language, 3); - wpabuf_put_u8(buf, os_strlen(icon->type)); - wpabuf_put_str(buf, icon->type); - wpabuf_put_u8(buf, os_strlen(icon->name)); - wpabuf_put_str(buf, icon->name); - } + for (i = 0; i < p->icons_count; i++) + anqp_add_icon(buf, bss, p->icons[i]); WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2); /* OSU_NAI */ @@ -728,6 +819,40 @@ } +static void anqp_add_osu_provider_nai(struct wpabuf *buf, + struct hs20_osu_provider *p) +{ + /* OSU_NAI for shared BSS (Single SSID) */ + if (p->osu_nai2) { + wpabuf_put_u8(buf, os_strlen(p->osu_nai2)); + wpabuf_put_str(buf, p->osu_nai2); + } else { + wpabuf_put_u8(buf, 0); + } +} + + +static void anqp_add_osu_providers_nai_list(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + if (hapd->conf->hs20_osu_providers_nai_count) { + size_t i; + u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); + wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_NAI_LIST); + wpabuf_put_u8(buf, 0); /* Reserved */ + + for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) { + anqp_add_osu_provider_nai( + buf, &hapd->conf->hs20_osu_providers[i]); + } + + gas_anqp_set_element_len(buf, len); + } +} + + static void anqp_add_icon_binary_file(struct hostapd_data *hapd, struct wpabuf *buf, const u8 *name, size_t name_len) @@ -783,9 +908,49 @@ gas_anqp_set_element_len(buf, len); } + +static void anqp_add_operator_icon_metadata(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + struct hostapd_bss_config *bss = hapd->conf; + size_t i; + u8 *len; + + if (!bss->hs20_operator_icon_count) + return; + + len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); + + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); + wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_ICON_METADATA); + wpabuf_put_u8(buf, 0); /* Reserved */ + + for (i = 0; i < bss->hs20_operator_icon_count; i++) + anqp_add_icon(buf, bss, bss->hs20_operator_icon[i]); + + gas_anqp_set_element_len(buf, len); +} + #endif /* CONFIG_HS20 */ +#ifdef CONFIG_MBO +static void anqp_add_mbo_cell_data_conn_pref(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + if (hapd->conf->mbo_cell_data_conn_pref >= 0) { + u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, MBO_ANQP_OUI_TYPE); + wpabuf_put_u8(buf, MBO_ANQP_SUBTYPE_CELL_CONN_PREF); + wpabuf_put_u8(buf, hapd->conf->mbo_cell_data_conn_pref); + gas_anqp_set_element_len(buf, len); + } +} +#endif /* CONFIG_MBO */ + + static size_t anqp_get_required_len(struct hostapd_data *hapd, const u16 *infoid, unsigned int num_infoid) @@ -821,6 +986,10 @@ len += 1000; if (request & ANQP_REQ_ICON_REQUEST) len += 65536; +#ifdef CONFIG_FILS + if (request & ANQP_FILS_REALM_INFO) + len += 2 * dl_list_len(&hapd->conf->fils_realms); +#endif /* CONFIG_FILS */ len += anqp_get_required_len(hapd, extra_req, num_extra_req); buf = wpabuf_alloc(len); @@ -860,8 +1029,19 @@ if (request & ANQP_REQ_EMERGENCY_NAI) anqp_add_elem(hapd, buf, ANQP_EMERGENCY_NAI); - for (i = 0; i < num_extra_req; i++) + for (i = 0; i < num_extra_req; i++) { +#ifdef CONFIG_FILS + if (extra_req[i] == ANQP_FILS_REALM_INFO) { + anqp_add_fils_realm_info(hapd, buf); + continue; + } +#endif /* CONFIG_FILS */ + if (extra_req[i] == ANQP_VENUE_URL) { + anqp_add_venue_url(hapd, buf); + continue; + } anqp_add_elem(hapd, buf, extra_req[i]); + } #ifdef CONFIG_HS20 if (request & ANQP_REQ_HS_CAPABILITY_LIST) @@ -878,8 +1058,17 @@ anqp_add_osu_providers_list(hapd, buf); if (request & ANQP_REQ_ICON_REQUEST) anqp_add_icon_binary_file(hapd, buf, icon_name, icon_name_len); + if (request & ANQP_REQ_OPERATOR_ICON_METADATA) + anqp_add_operator_icon_metadata(hapd, buf); + if (request & ANQP_REQ_OSU_PROVIDERS_NAI_LIST) + anqp_add_osu_providers_nai_list(hapd, buf); #endif /* CONFIG_HS20 */ +#ifdef CONFIG_MBO + if (request & ANQP_REQ_MBO_CELL_DATA_CONN_PREF) + anqp_add_mbo_cell_data_conn_pref(hapd, buf); +#endif /* CONFIG_MBO */ + return buf; } @@ -984,7 +1173,17 @@ get_anqp_elem(hapd, info_id) != NULL, qi); break; default: - if (!get_anqp_elem(hapd, info_id)) { +#ifdef CONFIG_FILS + if (info_id == ANQP_FILS_REALM_INFO && + !dl_list_empty(&hapd->conf->fils_realms)) { + wpa_printf(MSG_DEBUG, + "ANQP: FILS Realm Information (local)"); + } else +#endif /* CONFIG_FILS */ + if (info_id == ANQP_VENUE_URL && hapd->conf->venue_url) { + wpa_printf(MSG_DEBUG, + "ANQP: Venue URL (local)"); + } else if (!get_anqp_elem(hapd, info_id)) { wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u", info_id); break; @@ -1050,6 +1249,16 @@ set_anqp_req(ANQP_REQ_OSU_PROVIDERS_LIST, "OSU Providers list", hapd->conf->hs20_osu_providers_count, qi); break; + case HS20_STYPE_OPERATOR_ICON_METADATA: + set_anqp_req(ANQP_REQ_OPERATOR_ICON_METADATA, + "Operator Icon Metadata", + hapd->conf->hs20_operator_icon_count, qi); + break; + case HS20_STYPE_OSU_PROVIDERS_NAI_LIST: + set_anqp_req(ANQP_REQ_OSU_PROVIDERS_NAI_LIST, + "OSU Providers NAI List", + hapd->conf->hs20_osu_providers_nai_count, qi); + break; default: wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u", subtype); @@ -1092,49 +1301,12 @@ } -static void rx_anqp_vendor_specific(struct hostapd_data *hapd, - const u8 *pos, const u8 *end, - struct anqp_query_info *qi) +static void rx_anqp_vendor_specific_hs20(struct hostapd_data *hapd, + const u8 *pos, const u8 *end, + struct anqp_query_info *qi) { - u32 oui; u8 subtype; - if (end - pos < 4) { - wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP " - "Query element"); - return; - } - - oui = WPA_GET_BE24(pos); - pos += 3; - if (oui != OUI_WFA) { - wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x", - oui); - return; - } - -#ifdef CONFIG_P2P - if (*pos == P2P_OUI_TYPE) { - /* - * This is for P2P SD and will be taken care of by the P2P - * implementation. This query needs to be ignored in the generic - * GAS server to avoid duplicated response. - */ - wpa_printf(MSG_DEBUG, - "ANQP: Ignore WFA vendor type %u (P2P SD) in generic GAS server", - *pos); - qi->p2p_sd = 1; - return; - } -#endif /* CONFIG_P2P */ - - if (*pos != HS20_ANQP_OUI_TYPE) { - wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u", - *pos); - return; - } - pos++; - if (end - pos <= 1) return; @@ -1164,6 +1336,115 @@ #endif /* CONFIG_HS20 */ +#ifdef CONFIG_P2P +static void rx_anqp_vendor_specific_p2p(struct hostapd_data *hapd, + struct anqp_query_info *qi) +{ + /* + * This is for P2P SD and will be taken care of by the P2P + * implementation. This query needs to be ignored in the generic + * GAS server to avoid duplicated response. + */ + wpa_printf(MSG_DEBUG, + "ANQP: Ignore WFA vendor type %u (P2P SD) in generic GAS server", + P2P_OUI_TYPE); + qi->p2p_sd = 1; + return; +} +#endif /* CONFIG_P2P */ + + +#ifdef CONFIG_MBO + +static void rx_anqp_mbo_query_list(struct hostapd_data *hapd, u8 subtype, + struct anqp_query_info *qi) +{ + switch (subtype) { + case MBO_ANQP_SUBTYPE_CELL_CONN_PREF: + set_anqp_req(ANQP_REQ_MBO_CELL_DATA_CONN_PREF, + "Cellular Data Connection Preference", + hapd->conf->mbo_cell_data_conn_pref >= 0, qi); + break; + default: + wpa_printf(MSG_DEBUG, "ANQP: Unsupported MBO subtype %u", + subtype); + break; + } +} + + +static void rx_anqp_vendor_specific_mbo(struct hostapd_data *hapd, + const u8 *pos, const u8 *end, + struct anqp_query_info *qi) +{ + u8 subtype; + + if (end - pos < 1) + return; + + subtype = *pos++; + switch (subtype) { + case MBO_ANQP_SUBTYPE_QUERY_LIST: + wpa_printf(MSG_DEBUG, "ANQP: MBO Query List"); + while (pos < end) { + rx_anqp_mbo_query_list(hapd, *pos, qi); + pos++; + } + break; + default: + wpa_printf(MSG_DEBUG, "ANQP: Unsupported MBO query subtype %u", + subtype); + break; + } +} + +#endif /* CONFIG_MBO */ + + +static void rx_anqp_vendor_specific(struct hostapd_data *hapd, + const u8 *pos, const u8 *end, + struct anqp_query_info *qi) +{ + u32 oui; + + if (end - pos < 4) { + wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP " + "Query element"); + return; + } + + oui = WPA_GET_BE24(pos); + pos += 3; + if (oui != OUI_WFA) { + wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x", + oui); + return; + } + + switch (*pos) { +#ifdef CONFIG_P2P + case P2P_OUI_TYPE: + rx_anqp_vendor_specific_p2p(hapd, qi); + break; +#endif /* CONFIG_P2P */ +#ifdef CONFIG_HS20 + case HS20_ANQP_OUI_TYPE: + rx_anqp_vendor_specific_hs20(hapd, pos + 1, end, qi); + break; +#endif /* CONFIG_HS20 */ +#ifdef CONFIG_MBO + case MBO_ANQP_OUI_TYPE: + rx_anqp_vendor_specific_mbo(hapd, pos + 1, end, qi); + break; +#endif /* CONFIG_MBO */ + default: + wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u", + *pos); + break; + } +} + + static void gas_serv_req_local_processing(struct hostapd_data *hapd, const u8 *sa, u8 dialog_token, struct anqp_query_info *qi, int prot, @@ -1189,7 +1470,7 @@ } #endif /* CONFIG_P2P */ - if (wpabuf_len(buf) > hapd->gas_frag_limit || + if (wpabuf_len(buf) > hapd->conf->gas_frag_limit || hapd->conf->gas_comeback_delay) { struct gas_dialog_info *di; u16 comeback_delay = 1; @@ -1240,6 +1521,72 @@ } +#ifdef CONFIG_DPP +static void gas_serv_req_dpp_processing(struct hostapd_data *hapd, + const u8 *sa, u8 dialog_token, + int prot, struct wpabuf *buf) +{ + struct wpabuf *tx_buf; + + if (wpabuf_len(buf) > hapd->conf->gas_frag_limit || + hapd->conf->gas_comeback_delay) { + struct gas_dialog_info *di; + u16 comeback_delay = 1; + + if (hapd->conf->gas_comeback_delay) { + /* Testing - allow overriding of the delay value */ + comeback_delay = hapd->conf->gas_comeback_delay; + } + + wpa_printf(MSG_DEBUG, + "DPP: Too long response to fit in initial response - use GAS comeback"); + di = gas_dialog_create(hapd, sa, dialog_token); + if (!di) { + wpa_printf(MSG_INFO, "DPP: Could not create dialog for " + MACSTR " (dialog token %u)", + MAC2STR(sa), dialog_token); + wpabuf_free(buf); + tx_buf = gas_build_initial_resp( + dialog_token, WLAN_STATUS_UNSPECIFIED_FAILURE, + 0, 10); + if (tx_buf) + gas_serv_write_dpp_adv_proto(tx_buf); + } else { + di->prot = prot; + di->sd_resp = buf; + di->sd_resp_pos = 0; + tx_buf = gas_build_initial_resp( + dialog_token, WLAN_STATUS_SUCCESS, + comeback_delay, 10); + if (tx_buf) + gas_serv_write_dpp_adv_proto(tx_buf); + } + } else { + wpa_printf(MSG_DEBUG, + "DPP: GAS Initial response (no comeback)"); + tx_buf = gas_build_initial_resp( + dialog_token, WLAN_STATUS_SUCCESS, 0, + 10 + 2 + wpabuf_len(buf)); + if (tx_buf) { + gas_serv_write_dpp_adv_proto(tx_buf); + wpabuf_put_le16(tx_buf, wpabuf_len(buf)); + wpabuf_put_buf(tx_buf, buf); + hostapd_dpp_gas_status_handler(hapd, 1); + } + wpabuf_free(buf); + } + if (!tx_buf) + return; + if (prot) + convert_to_protected_dual(tx_buf); + hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, + wpabuf_head(tx_buf), + wpabuf_len(tx_buf)); + wpabuf_free(tx_buf); +} +#endif /* CONFIG_DPP */ + + static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, const u8 *sa, const u8 *data, size_t len, int prot, @@ -1252,6 +1599,9 @@ u16 slen; struct anqp_query_info qi; const u8 *adv_proto; +#ifdef CONFIG_DPP + int dpp = 0; +#endif /* CONFIG_DPP */ if (len < 1 + 2) return; @@ -1279,6 +1629,15 @@ next = pos + slen; pos++; /* skip QueryRespLenLimit and PAME-BI */ +#ifdef CONFIG_DPP + if (slen == 8 && *pos == WLAN_EID_VENDOR_SPECIFIC && + pos[1] == 5 && WPA_GET_BE24(&pos[2]) == OUI_WFA && + pos[5] == DPP_OUI_TYPE && pos[6] == 0x01) { + wpa_printf(MSG_DEBUG, "DPP: Configuration Request"); + dpp = 1; + } else +#endif /* CONFIG_DPP */ + if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { struct wpabuf *buf; wpa_msg(hapd->msg_ctx, MSG_DEBUG, @@ -1318,6 +1677,18 @@ return; end = pos + slen; +#ifdef CONFIG_DPP + if (dpp) { + struct wpabuf *msg; + + msg = hostapd_dpp_gas_req_handler(hapd, sa, pos, slen); + if (!msg) + return; + gas_serv_req_dpp_processing(hapd, sa, dialog_token, prot, msg); + return; + } +#endif /* CONFIG_DPP */ + /* ANQP Query Request */ while (pos < end) { u16 info_id, elen; @@ -1339,11 +1710,9 @@ case ANQP_QUERY_LIST: rx_anqp_query_list(hapd, pos, pos + elen, &qi); break; -#ifdef CONFIG_HS20 case ANQP_VENDOR_SPECIFIC: rx_anqp_vendor_specific(hapd, pos, pos + elen, &qi); break; -#endif /* CONFIG_HS20 */ default: wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query " "Request element %u", info_id); @@ -1393,8 +1762,8 @@ } frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos; - if (frag_len > hapd->gas_frag_limit) { - frag_len = hapd->gas_frag_limit; + if (frag_len > hapd->conf->gas_frag_limit) { + frag_len = hapd->conf->gas_frag_limit; more = 1; } wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u", @@ -1407,6 +1776,18 @@ gas_serv_dialog_clear(dialog); return; } +#ifdef CONFIG_DPP + if (dialog->dpp) { + tx_buf = gas_build_comeback_resp(dialog_token, + WLAN_STATUS_SUCCESS, + dialog->sd_frag_id, more, 0, + 10 + frag_len); + if (tx_buf) { + gas_serv_write_dpp_adv_proto(tx_buf); + wpabuf_put_buf(tx_buf, buf); + } + } else +#endif /* CONFIG_DPP */ tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token, WLAN_STATUS_SUCCESS, dialog->sd_frag_id, @@ -1430,6 +1811,10 @@ } else { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of " "SD response sent"); +#ifdef CONFIG_DPP + if (dialog->dpp) + hostapd_dpp_gas_status_handler(hapd, 1); +#endif /* CONFIG_DPP */ gas_serv_dialog_clear(dialog); gas_serv_free_dialogs(hapd, sa); } @@ -1495,9 +1880,6 @@ { 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 @@ -41,7 +41,7 @@ #define ANQP_REQ_EMERGENCY_NAI \ (1 << (ANQP_EMERGENCY_NAI - ANQP_QUERY_LIST)) /* - * First 16 Hotspot 2.0 vendor specific ANQP-elements can be included in the + * First 15 Hotspot 2.0 vendor specific ANQP-elements can be included in the * optimized bitmap. */ #define ANQP_REQ_HS_CAPABILITY_LIST \ @@ -60,6 +60,13 @@ (0x10000 << HS20_STYPE_OSU_PROVIDERS_LIST) #define ANQP_REQ_ICON_REQUEST \ (0x10000 << HS20_STYPE_ICON_REQUEST) +#define ANQP_REQ_OPERATOR_ICON_METADATA \ + (0x10000 << HS20_STYPE_OPERATOR_ICON_METADATA) +#define ANQP_REQ_OSU_PROVIDERS_NAI_LIST \ + (0x10000 << HS20_STYPE_OSU_PROVIDERS_NAI_LIST) +/* The first MBO ANQP-element can be included in the optimized bitmap. */ +#define ANQP_REQ_MBO_CELL_DATA_CONN_PREF \ + (BIT(29) << MBO_ANQP_SUBTYPE_CELL_CONN_PREF) struct gas_dialog_info { u8 valid; @@ -68,6 +75,7 @@ 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. @@ -31,6 +31,8 @@ #include "vlan_init.h" #include "wpa_auth.h" #include "wps_hostapd.h" +#include "dpp_hostapd.h" +#include "gas_query_ap.h" #include "hw_features.h" #include "wpa_auth_glue.h" #include "ap_drv_ops.h" @@ -45,6 +47,9 @@ #include "ndisc_snoop.h" #include "neighbor_db.h" #include "rrm.h" +#include "fils_hlp.h" +#include "acs.h" +#include "hs20.h" static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason); @@ -52,6 +57,8 @@ static int hostapd_broadcast_wep_clear(struct hostapd_data *hapd); static int setup_interface2(struct hostapd_iface *iface); static void channel_list_update_timeout(void *eloop_ctx, void *timeout_ctx); +static void hostapd_interface_setup_failure_handler(void *eloop_ctx, + void *timeout_ctx); int hostapd_for_each_interface(struct hapd_interfaces *interfaces, @@ -71,10 +78,26 @@ } +void hostapd_reconfig_encryption(struct hostapd_data *hapd) +{ + if (hapd->wpa_auth) + return; + + hostapd_set_privacy(hapd, 0); + hostapd_setup_encryption(hapd->conf->iface, hapd); +} + + static void hostapd_reload_bss(struct hostapd_data *hapd) { struct hostapd_ssid *ssid; + if (!hapd->started) + return; + + if (hapd->conf->wmm_enabled < 0) + hapd->conf->wmm_enabled = hapd->iconf->ieee80211n; + #ifndef CONFIG_NO_RADIUS radius_client_reconfig(hapd->radius, hapd->conf->radius); #endif /* CONFIG_NO_RADIUS */ @@ -153,8 +176,27 @@ } +static int hostapd_iface_conf_changed(struct hostapd_config *newconf, + struct hostapd_config *oldconf) +{ + size_t i; + + if (newconf->num_bss != oldconf->num_bss) + return 1; + + for (i = 0; i < newconf->num_bss; i++) { + if (os_strcmp(newconf->bss[i]->iface, + oldconf->bss[i]->iface) != 0) + return 1; + } + + return 0; +} + + int hostapd_reload_config(struct hostapd_iface *iface) { + struct hapd_interfaces *interfaces = iface->interfaces; struct hostapd_data *hapd = iface->bss[0]; struct hostapd_config *newconf, *oldconf; size_t j; @@ -177,6 +219,35 @@ hostapd_clear_old(iface); oldconf = hapd->iconf; + if (hostapd_iface_conf_changed(newconf, oldconf)) { + char *fname; + int res; + + wpa_printf(MSG_DEBUG, + "Configuration changes include interface/BSS modification - force full disable+enable sequence"); + fname = os_strdup(iface->config_fname); + if (!fname) { + hostapd_config_free(newconf); + return -1; + } + hostapd_remove_iface(interfaces, hapd->conf->iface); + iface = hostapd_init(interfaces, fname); + os_free(fname); + hostapd_config_free(newconf); + if (!iface) { + wpa_printf(MSG_ERROR, + "Failed to initialize interface on config reload"); + return -1; + } + iface->interfaces = interfaces; + interfaces->iface[interfaces->count] = iface; + interfaces->count++; + res = hostapd_enable_iface(iface); + if (res < 0) + wpa_printf(MSG_ERROR, + "Failed to enable interface on config reload"); + return res; + } iface->conf = newconf; for (j = 0; j < iface->num_bss; j++) { @@ -210,7 +281,7 @@ { int i; - if (!ifname) + if (!ifname || !hapd->drv_priv) return; for (i = 0; i < NUM_WEP_KEYS; i++) { if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE, NULL, i, @@ -277,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); @@ -297,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); @@ -341,6 +417,21 @@ #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 */ } @@ -355,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); } @@ -387,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); @@ -409,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); @@ -423,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); } @@ -484,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; @@ -572,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)", @@ -873,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 */ @@ -956,13 +1101,13 @@ if (conf->wmm_enabled < 0) conf->wmm_enabled = hapd->iconf->ieee80211n; -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (is_zero_ether_addr(conf->r1_key_holder)) os_memcpy(conf->r1_key_holder, hapd->own_addr, ETH_ALEN); -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_MESH - if (hapd->iface->mconf == NULL) + if ((hapd->conf->mesh & MESH_ENABLED) && hapd->iface->mconf == NULL) flush_old_stations = 0; #endif /* CONFIG_MESH */ @@ -1047,6 +1192,7 @@ conf->radius_das_require_message_authenticator; das_conf.ctx = hapd; das_conf.disconnect = hostapd_das_disconnect; + das_conf.coa = hostapd_das_coa; hapd->radius_das = radius_das_init(&das_conf); if (hapd->radius_das == NULL) { wpa_printf(MSG_ERROR, "RADIUS DAS initialization " @@ -1063,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; @@ -1150,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 */ @@ -1531,124 +1685,113 @@ #endif /* CONFIG_FST */ +#ifdef CONFIG_OWE -#ifdef NEED_AP_MLME -static enum nr_chan_width hostapd_get_nr_chan_width(struct hostapd_data *hapd, - int ht, int vht) +static int hostapd_owe_iface_iter(struct hostapd_iface *iface, void *ctx) { - 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 */ + struct hostapd_data *hapd = ctx; + size_t i; + for (i = 0; i < iface->num_bss; i++) { + struct hostapd_data *bss = iface->bss[i]; -static void hostapd_set_own_neighbor_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; - int center_freq1 = 0, center_freq2 = 0; - enum nr_chan_width width; - u32 bssid_info; - struct wpabuf *nr; + if (os_strcmp(hapd->conf->owe_transition_ifname, + bss->conf->iface) != 0) + continue; - if (!(hapd->conf->radio_measurements[0] & - WLAN_RRM_CAPS_NEIGHBOR_REPORT)) - return; + 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; - 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" */ + 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; + } - if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT) - bssid_info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT; + return 0; +} - bssid_info |= NEI_REP_BSSID_INFO_RM; /* RRM is supported */ - if (hapd->conf->wmm_enabled) { - bssid_info |= NEI_REP_BSSID_INFO_QOS; +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; - if (hapd->conf->wmm_uapsd && - (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD)) - bssid_info |= NEI_REP_BSSID_INFO_APSD; - } + /* 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); +} - 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; - } +static int hostapd_owe_iface_iter2(struct hostapd_iface *iface, void *ctx) +{ + size_t i; - /* TODO: Set NEI_REP_BSSID_INFO_MOBILITY_DOMAIN if MDE is set */ + for (i = 0; i < iface->num_bss; i++) { + struct hostapd_data *bss = iface->bss[i]; + int res; - ieee80211_freq_to_channel_ext(hapd->iface->freq, - hapd->iconf->secondary_channel, - hapd->iconf->vht_oper_chwidth, - &op_class, &channel); - width = hostapd_get_nr_chan_width(hapd, ht, vht); - if (vht) { - center_freq1 = ieee80211_chan_to_freq( - NULL, op_class, - hapd->iconf->vht_oper_centr_freq_seg0_idx); - if (width == NR_CHAN_WIDTH_80P80) - center_freq2 = ieee80211_chan_to_freq( - NULL, op_class, - hapd->iconf->vht_oper_centr_freq_seg1_idx); - } else if (ht) { - center_freq1 = hapd->iface->freq + - 10 * hapd->iconf->secondary_channel; + 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); } - ssid.ssid_len = hapd->conf->ssid.ssid_len; - os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len); + return 0; +} - /* - * 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; +#endif /* CONFIG_OWE */ - 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); - wpabuf_put_u8(nr, center_freq2); +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 */ +} - hostapd_neighbor_set(hapd, hapd->own_addr, &ssid, nr, hapd->iconf->lci, - hapd->iconf->civic); - wpabuf_free(nr); -#endif /* NEED_AP_MLME */ +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); } @@ -1741,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; @@ -1762,11 +1907,14 @@ 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 (is_zero_ether_addr(hapd->conf->bssid)) @@ -1827,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); @@ -1837,7 +1986,7 @@ iface->interfaces->terminate_on_error--; for (j = 0; j < iface->num_bss; j++) - hostapd_set_own_neighbor_report(iface->bss[j]); + hostapd_neighbor_set_own_report(iface->bss[j]); return 0; @@ -1851,8 +2000,19 @@ iface->fst = NULL; } #endif /* CONFIG_FST */ - if (iface->interfaces && iface->interfaces->terminate_on_error) + + if (iface->interfaces && iface->interfaces->terminate_on_error) { eloop_terminate(); + } else if (hapd->setup_complete_cb) { + /* + * Calling hapd->setup_complete_cb directly may cause iface + * deinitialization which may be accessed later by the caller. + */ + eloop_register_timeout(0, 0, + hostapd_interface_setup_failure_handler, + iface, NULL); + } + return -1; } @@ -1997,10 +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; } @@ -2011,7 +2180,7 @@ 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); @@ -2028,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; @@ -2044,11 +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 */ } @@ -2402,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]; @@ -2459,7 +2634,7 @@ if (conf == NULL) { wpa_printf(MSG_ERROR, "%s: Failed to allocate memory for " "configuration", __func__); - return NULL; + return NULL; } if (driver) { @@ -2612,6 +2787,7 @@ return -1; } } + hostapd_owe_update_trans(hapd_iface); return 0; } @@ -2829,12 +3005,24 @@ ieee802_1x_new_station(hapd, sta); if (reassoc) { if (sta->auth_alg != WLAN_AUTH_FT && + sta->auth_alg != WLAN_AUTH_FILS_SK && + sta->auth_alg != WLAN_AUTH_FILS_SK_PFS && + sta->auth_alg != WLAN_AUTH_FILS_PK && !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH); } else wpa_auth_sta_associated(hapd->wpa_auth, sta->wpa_sm); - if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) { + if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED) { + if (eloop_cancel_timeout(ap_handle_timer, hapd, sta) > 0) { + wpa_printf(MSG_DEBUG, + "%s: %s: canceled wired ap_handle_timer timeout for " + MACSTR, + hapd->conf->iface, __func__, + MAC2STR(sta->addr)); + } + } else if (!(hapd->iface->drv_flags & + WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) { wpa_printf(MSG_DEBUG, "%s: %s: reschedule ap_handle_timer timeout for " MACSTR " (%d seconds - ap_max_inactivity)", @@ -2928,60 +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); } @@ -3158,6 +3338,19 @@ } +void hostapd_chan_switch_vht_config(struct hostapd_data *hapd, int vht_enabled) +{ + if (vht_enabled) + hapd->iconf->ch_switch_vht_config |= CH_SWITCH_VHT_ENABLED; + else + hapd->iconf->ch_switch_vht_config |= CH_SWITCH_VHT_DISABLED; + + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "CHAN_SWITCH VHT CONFIG 0x%x", + hapd->iconf->ch_switch_vht_config); +} + + int hostapd_switch_channel(struct hostapd_data *hapd, struct csa_settings *settings) { @@ -3192,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"); @@ -3234,10 +3426,8 @@ /* * 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); } --- 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; @@ -53,7 +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 { @@ -76,6 +90,7 @@ }; struct hostapd_frame_info { + unsigned int freq; u32 channel; u32 datarate; int ssi_signal; /* dBm */ @@ -109,8 +124,16 @@ 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 */ @@ -184,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; @@ -242,9 +276,6 @@ unsigned int cs_c_off_ecsa_beacon; unsigned int cs_c_off_ecsa_proberesp; - /* BSS Load */ - unsigned int bss_load_update_timeout; - #ifdef CONFIG_P2P struct p2p_data *p2p; struct p2p_group *p2p_group; @@ -259,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; @@ -284,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 @@ -292,6 +323,18 @@ unsigned int ext_eapol_frame_io:1; struct l2_packet_data *l2_test; + + enum wpa_alg last_gtk_alg; + int last_gtk_key_idx; + u8 last_gtk[WPA_GTK_MAX_LEN]; + size_t last_gtk_len; + +#ifdef CONFIG_IEEE80211W + enum wpa_alg last_igtk_alg; + int last_igtk_key_idx; + u8 last_igtk[WPA_IGTK_MAX_LEN]; + size_t last_igtk_len; +#endif /* CONFIG_IEEE80211W */ #endif /* CONFIG_TESTING_OPTIONS */ #ifdef CONFIG_MBO @@ -300,10 +343,42 @@ struct dl_list nr_db; + u8 beacon_req_token; u8 lci_req_token; u8 range_req_token; unsigned int lci_req_active:1; unsigned int range_req_active:1; + + int dhcp_sock; /* UDP socket used with the DHCP server */ + +#ifdef CONFIG_DPP + int dpp_init_done; + struct dpp_authentication *dpp_auth; + u8 dpp_allowed_roles; + int dpp_qr_mutual; + int dpp_auth_ok_on_ack; + int dpp_in_response_listen; + struct gas_query_ap *gas; + struct dpp_pkex *dpp_pkex; + struct dpp_bootstrap_info *dpp_pkex_bi; + char *dpp_pkex_code; + char *dpp_pkex_identifier; + char *dpp_pkex_auth_cmd; + char *dpp_configurator_params; + struct os_reltime dpp_last_init; + struct os_reltime dpp_init_iter_start; + unsigned int dpp_init_max_tries; + unsigned int dpp_init_retry_time; + unsigned int dpp_resp_wait_time; + unsigned int dpp_resp_max_tries; + unsigned int dpp_resp_retry_time; +#ifdef CONFIG_TESTING_OPTIONS + char *dpp_config_obj_override; + char *dpp_discovery_override; + char *dpp_groups_override; + unsigned int dpp_ignore_netaccesskey_mismatch:1; +#endif /* CONFIG_TESTING_OPTIONS */ +#endif /* CONFIG_DPP */ }; @@ -311,6 +386,7 @@ struct dl_list list; u8 addr[ETH_ALEN]; struct os_reltime last_seen; + int ssi_signal; #ifdef CONFIG_TAXONOMY struct wpabuf *probe_ie_taxonomy; #endif /* CONFIG_TAXONOMY */ @@ -440,6 +516,10 @@ u64 last_channel_time_busy; u8 channel_utilization; + unsigned int chan_util_samples_sum; + unsigned int chan_util_num_sample_periods; + unsigned int chan_util_average; + /* eCSA IE will be added only if operating class is specified */ u8 cs_oper_class; @@ -459,6 +539,8 @@ struct dl_list sta_seen; /* struct hostapd_sta_info */ unsigned int num_sta_seen; + + u8 dfs_domain; }; /* hostapd.c */ @@ -466,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, @@ -492,6 +575,7 @@ void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s); const char * hostapd_state_text(enum hostapd_iface_state s); int hostapd_csa_in_progress(struct hostapd_iface *iface); +void hostapd_chan_switch_vht_config(struct hostapd_data *hapd, int vht_enabled); int hostapd_switch_channel(struct hostapd_data *hapd, struct csa_settings *settings); void @@ -499,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, @@ -510,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); @@ -533,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,9 @@ res = ieee80211n_allowed_ht40_channel_pair(iface); if (!res) { iface->conf->secondary_channel = 0; + iface->conf->vht_oper_centr_freq_seg0_idx = 0; + iface->conf->vht_oper_centr_freq_seg1_idx = 0; + iface->conf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT; res = 1; wpa_printf(MSG_INFO, "Fallback to 20 MHz"); } @@ -621,41 +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; @@ -683,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 */ @@ -746,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); @@ -763,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; } @@ -791,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; @@ -797,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; } @@ -978,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/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" @@ -45,8 +49,40 @@ #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; @@ -262,7 +298,7 @@ static int send_auth_reply(struct hostapd_data *hapd, const u8 *dst, const u8 *bssid, u16 auth_alg, u16 auth_transaction, u16 resp, - const u8 *ies, size_t ies_len) + const u8 *ies, size_t ies_len, const char *dbg) { struct ieee80211_mgmt *reply; u8 *buf; @@ -289,9 +325,9 @@ os_memcpy(reply->u.auth.variable, ies, ies_len); wpa_printf(MSG_DEBUG, "authentication reply: STA=" MACSTR - " auth_alg=%d auth_transaction=%d resp=%d (IE len=%lu)", + " auth_alg=%d auth_transaction=%d resp=%d (IE len=%lu) (dbg=%s)", MAC2STR(dst), auth_alg, auth_transaction, - resp, (unsigned long) ies_len); + resp, (unsigned long) ies_len, dbg); if (hostapd_drv_send_mlme(hapd, reply, rlen, 0) < 0) wpa_printf(MSG_INFO, "send_auth_reply: send failed"); else @@ -303,7 +339,7 @@ } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid, u16 auth_transaction, u16 status, const u8 *ies, size_t ies_len) @@ -313,7 +349,8 @@ int reply_res; reply_res = send_auth_reply(hapd, dst, bssid, WLAN_AUTH_FT, - auth_transaction, status, ies, ies_len); + auth_transaction, status, ies, ies_len, + "auth-ft-finish"); sta = ap_get_sta(hapd, dst); if (sta == NULL) @@ -334,12 +371,19 @@ 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, @@ -346,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; } @@ -354,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; } @@ -394,12 +467,14 @@ int reply_res; data = auth_build_sae_commit(hapd, sta, update); + if (!data && sta->sae->tmp && sta->sae->tmp->pw_id) + return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER; if (data == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; reply_res = send_auth_reply(hapd, sta->addr, bssid, WLAN_AUTH_SAE, 1, WLAN_STATUS_SUCCESS, wpabuf_head(data), - wpabuf_len(data)); + wpabuf_len(data), "sae-send-commit"); wpabuf_free(data); @@ -420,7 +495,7 @@ reply_res = send_auth_reply(hapd, sta->addr, bssid, WLAN_AUTH_SAE, 2, WLAN_STATUS_SUCCESS, wpabuf_head(data), - wpabuf_len(data)); + wpabuf_len(data), "sae-send-confirm"); wpabuf_free(data); @@ -447,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; } @@ -473,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; @@ -483,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); @@ -491,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; } @@ -516,12 +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=%d)", - MAC2STR(sta->addr), sta->sae->sync, sta->sae->state); + " (sync=%d state=%s)", + MAC2STR(sta->addr), sta->sae->sync, + sae_state_txt(sta->sae->state)); switch (sta->sae->state) { case SAE_COMMITTED: @@ -564,33 +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); - sta->sae->state = SAE_ACCEPTED; + sae_set_state(sta, SAE_ACCEPTED, "Accept Confirm"); wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr, sta->sae->pmk, sta->sae->pmkid); + 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; @@ -612,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 @@ -641,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) { @@ -649,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++; @@ -668,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 @@ -675,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++; @@ -698,18 +889,32 @@ sae_set_retransmit_timer(hapd, sta); } else { + sta->sae->send_confirm = 0xffff; sae_accept_sta(hapd, sta); } break; case SAE_ACCEPTED: - if (auth_transaction == 1) { + if (auth_transaction == 1 && + (hapd->conf->mesh & MESH_ENABLED)) { wpa_printf(MSG_DEBUG, "SAE: remove the STA (" MACSTR ") doing reauthentication", MAC2STR(sta->addr)); + wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr); ap_free_sta(hapd, sta); - wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr); + *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++; @@ -732,6 +937,7 @@ { struct sae_data *sae = sta->sae; int i, *groups = hapd->conf->sae_groups; + int default_groups[] = { 19, 0 }; if (sae->state != SAE_COMMITTED) return; @@ -738,12 +944,14 @@ wpa_printf(MSG_DEBUG, "SAE: Previously selected group: %d", sae->group); - for (i = 0; groups && groups[i] > 0; i++) { + if (!groups) + groups = default_groups; + for (i = 0; groups[i] > 0; i++) { if (sae->group == groups[i]) break; } - if (!groups || groups[i] <= 0) { + if (groups[i] <= 0) { wpa_printf(MSG_DEBUG, "SAE: Previously selected group not found from the current configuration"); return; @@ -772,7 +980,35 @@ { 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) { @@ -784,7 +1020,7 @@ resp = -1; goto remove_sta; } - sta->sae->state = SAE_NOTHING; + sae_set_state(sta, SAE_NOTHING, "Init"); sta->sae->sync = 0; } @@ -796,8 +1032,10 @@ } 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)", @@ -814,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, @@ -847,7 +1084,8 @@ "SAE: Failed to send commit message"); goto remove_sta; } - sta->sae->state = SAE_COMMITTED; + sae_set_state(sta, SAE_COMMITTED, + "Sent Commit (anti-clogging token case in mesh)"); sta->sae->sync = 0; sae_set_retransmit_timer(hapd, sta); return; @@ -866,10 +1104,37 @@ if (status_code != WLAN_STATUS_SUCCESS) goto remove_sta; + if (!(hapd->conf->mesh & MESH_ENABLED) && + sta->sae->state == SAE_COMMITTED) { + /* This is needed in the infrastructure BSS case to + * address a sequence where a STA entry may remain in + * hostapd across two attempts to do SAE authentication + * by the same STA. The second attempt may end up trying + * to use a different group and that would not be + * allowed if we remain in Committed state with the + * previously set parameters. */ + 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", @@ -876,6 +1141,17 @@ MAC2STR(sta->addr)); goto remove_sta; } + + if (resp == WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER) { + wpa_msg(hapd->msg_ctx, MSG_INFO, + WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER + MACSTR, MAC2STR(sta->addr)); + sae_clear_retransmit_timer(hapd, sta); + sae_set_state(sta, SAE_NOTHING, + "Unknown Password Identifier"); + goto remove_sta; + } + if (token && check_sae_token(hapd, sta->addr, token, token_len) < 0) { wpa_printf(MSG_DEBUG, "SAE: Drop commit message with " @@ -888,7 +1164,7 @@ 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)); @@ -896,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, @@ -910,14 +1188,39 @@ 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, @@ -929,16 +1232,27 @@ } 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->added_unassoc && (resp != WLAN_STATUS_SUCCESS || - status_code != WLAN_STATUS_SUCCESS)) { + 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; } @@ -970,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); @@ -977,11 +1291,740 @@ 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; @@ -998,8 +2041,6 @@ char *radius_cui = NULL; u16 seq_ctrl; - os_memset(&vlan_id, 0, sizeof(vlan_id)); - if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)", (unsigned long) len); @@ -1030,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) { @@ -1047,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)", @@ -1139,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 && @@ -1203,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; } @@ -1209,48 +2272,22 @@ } 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.notempty && - !hostapd_vlan_valid(hapd->conf->vlan, &vlan_id)) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_INFO, - "Invalid VLAN %d%s received from RADIUS server", - vlan_id.untagged, - vlan_id.tagged[0] ? "+" : ""); + res = ieee802_11_set_radius_info( + hapd, sta, res, session_timeout, acct_interim_interval, + &vlan_id, &psk, &identity, &radius_cui); + if (res) { + wpa_printf(MSG_DEBUG, "ieee802_11_set_radius_info() failed"); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } - if (ap_sta_set_vlan(hapd, sta, &vlan_id) < 0) { - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto fail; - } - if (sta->vlan_id) - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id); - hostapd_free_psk_list(sta->psk); - if (hapd->conf->wpa_psk_radius != PSK_RADIUS_IGNORED) { - sta->psk = psk; - psk = NULL; - } else { - sta->psk = NULL; - } - - sta->identity = identity; - identity = NULL; - sta->radius_cui = radius_cui; - radius_cui = NULL; - sta->flags &= ~WLAN_STA_PREAUTH; ieee802_1x_notify_pre_auth(sta->eapol_sm, 0); - if (hapd->conf->acct_interim_interval == 0 && acct_interim_interval) - sta->acct_interim_interval = acct_interim_interval; - if (res == HOSTAPD_ACL_ACCEPT_TIMEOUT) - ap_sta_session_timeout(hapd, sta, session_timeout); - else - ap_sta_no_session_timeout(hapd, sta); - /* * If the driver supports full AP client state, add a station to the * driver before sending authentication reply to make sure the driver @@ -1263,8 +2300,15 @@ * * In mesh mode, the station was already added to the driver when the * NEW_PEER_CANDIDATE event is received. + * + * If PMF was negotiated for the existing association, skip this to + * avoid dropping the STA entry and the associated keys. This is needed + * to allow the original connection work until the attempt can complete + * (re)association, so that unprotected Authentication frame cannot be + * used to bypass PMF protection. */ if (FULL_AP_CLIENT_STATE_SUPP(hapd->iface->drv_flags) && + (!(sta->flags & WLAN_STA_MFP) || !ap_sta_is_authorized(sta)) && !(hapd->conf->mesh & MESH_ENABLED) && !(sta->added_unassoc)) { /* @@ -1274,6 +2318,7 @@ * updated. To handle this, station's added_unassoc flag is * cleared once the station has completed association. */ + ap_sta_set_authorized(hapd, sta, 0); hostapd_drv_sta_remove(hapd, sta->addr); sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_AUTH | WLAN_STA_AUTHORIZED); @@ -1305,6 +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) { @@ -1316,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) @@ -1335,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 @@ -1357,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: @@ -1366,7 +2423,7 @@ reply_res = send_auth_reply(hapd, mgmt->sa, mgmt->bssid, auth_alg, auth_transaction + 1, resp, resp_ies, - resp_ies_len); + resp_ies_len, "handle-auth"); if (sta && sta->added_unassoc && (resp != WLAN_STATUS_SUCCESS || reply_res != WLAN_STATUS_SUCCESS)) { @@ -1455,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, @@ -1496,13 +2611,188 @@ } #endif /* CONFIG_INTERWORKING */ - if (ext_capab_ie_len > 0) + if (ext_capab_ie_len > 0) { sta->ecsa_supported = !!(ext_capab_ie[0] & BIT(2)); + os_free(sta->ext_capability); + sta->ext_capability = os_malloc(1 + ext_capab_ie_len); + if (sta->ext_capability) { + sta->ext_capability[0] = ext_capab_ie_len; + os_memcpy(sta->ext_capability + 1, ext_capab_ie, + ext_capab_ie_len); + } + } return WLAN_STATUS_SUCCESS; } +#ifdef CONFIG_OWE + +static int owe_group_supported(struct hostapd_data *hapd, u16 group) +{ + int i; + int *groups = hapd->conf->owe_groups; + + if (group != 19 && group != 20 && group != 21) + return 0; + + if (!groups) + return 1; + + for (i = 0; groups[i] > 0; i++) { + if (groups[i] == group) + return 1; + } + + return 0; +} + + +static u16 owe_process_assoc_req(struct hostapd_data *hapd, + struct sta_info *sta, const u8 *owe_dh, + u8 owe_dh_len) +{ + struct wpabuf *secret, *pub, *hkey; + int res; + u8 prk[SHA512_MAC_LEN], pmkid[SHA512_MAC_LEN]; + const char *info = "OWE Key Generation"; + const u8 *addr[2]; + size_t len[2]; + u16 group; + size_t hash_len, prime_len; + + if (wpa_auth_sta_get_pmksa(sta->wpa_sm)) { + wpa_printf(MSG_DEBUG, "OWE: Using PMKSA caching"); + return WLAN_STATUS_SUCCESS; + } + + group = WPA_GET_LE16(owe_dh); + if (!owe_group_supported(hapd, group)) { + wpa_printf(MSG_DEBUG, "OWE: Unsupported DH group %u", group); + return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + } + if (group == 19) + prime_len = 32; + else if (group == 20) + prime_len = 48; + else if (group == 21) + prime_len = 66; + else + return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + + crypto_ecdh_deinit(sta->owe_ecdh); + sta->owe_ecdh = crypto_ecdh_init(group); + if (!sta->owe_ecdh) + return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + sta->owe_group = group; + + secret = crypto_ecdh_set_peerkey(sta->owe_ecdh, 0, owe_dh + 2, + owe_dh_len - 2); + secret = wpabuf_zeropad(secret, prime_len); + if (!secret) { + wpa_printf(MSG_DEBUG, "OWE: Invalid peer DH public key"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + wpa_hexdump_buf_key(MSG_DEBUG, "OWE: DH shared secret", secret); + + /* prk = HKDF-extract(C | A | group, z) */ + + pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0); + if (!pub) { + wpabuf_clear_free(secret); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + /* PMKID = Truncate-128(Hash(C | A)) */ + addr[0] = owe_dh + 2; + len[0] = owe_dh_len - 2; + addr[1] = wpabuf_head(pub); + len[1] = wpabuf_len(pub); + if (group == 19) { + res = sha256_vector(2, addr, len, pmkid); + hash_len = SHA256_MAC_LEN; + } else if (group == 20) { + res = sha384_vector(2, addr, len, pmkid); + hash_len = SHA384_MAC_LEN; + } else if (group == 21) { + res = sha512_vector(2, addr, len, pmkid); + hash_len = SHA512_MAC_LEN; + } else { + wpabuf_free(pub); + wpabuf_clear_free(secret); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + pub = wpabuf_zeropad(pub, prime_len); + if (res < 0 || !pub) { + wpabuf_free(pub); + wpabuf_clear_free(secret); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + hkey = wpabuf_alloc(owe_dh_len - 2 + wpabuf_len(pub) + 2); + if (!hkey) { + wpabuf_free(pub); + wpabuf_clear_free(secret); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + wpabuf_put_data(hkey, owe_dh + 2, owe_dh_len - 2); /* C */ + wpabuf_put_buf(hkey, pub); /* A */ + wpabuf_free(pub); + wpabuf_put_le16(hkey, group); /* group */ + if (group == 19) + res = hmac_sha256(wpabuf_head(hkey), wpabuf_len(hkey), + wpabuf_head(secret), wpabuf_len(secret), prk); + else if (group == 20) + res = hmac_sha384(wpabuf_head(hkey), wpabuf_len(hkey), + wpabuf_head(secret), wpabuf_len(secret), prk); + else if (group == 21) + res = hmac_sha512(wpabuf_head(hkey), wpabuf_len(hkey), + wpabuf_head(secret), wpabuf_len(secret), prk); + wpabuf_clear_free(hkey); + wpabuf_clear_free(secret); + if (res < 0) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + wpa_hexdump_key(MSG_DEBUG, "OWE: prk", prk, hash_len); + + /* PMK = HKDF-expand(prk, "OWE Key Generation", n) */ + + os_free(sta->owe_pmk); + sta->owe_pmk = os_malloc(hash_len); + if (!sta->owe_pmk) { + os_memset(prk, 0, SHA512_MAC_LEN); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + if (group == 19) + res = hmac_sha256_kdf(prk, hash_len, NULL, (const u8 *) info, + os_strlen(info), sta->owe_pmk, hash_len); + else if (group == 20) + res = hmac_sha384_kdf(prk, hash_len, NULL, (const u8 *) info, + os_strlen(info), sta->owe_pmk, hash_len); + else if (group == 21) + res = hmac_sha512_kdf(prk, hash_len, NULL, (const u8 *) info, + os_strlen(info), sta->owe_pmk, hash_len); + os_memset(prk, 0, SHA512_MAC_LEN); + if (res < 0) { + os_free(sta->owe_pmk); + sta->owe_pmk = NULL; + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + sta->owe_pmk_len = hash_len; + + wpa_hexdump_key(MSG_DEBUG, "OWE: PMK", sta->owe_pmk, sta->owe_pmk_len); + wpa_hexdump(MSG_DEBUG, "OWE: PMKID", pmkid, PMKID_LEN); + wpa_auth_pmksa_add2(hapd->wpa_auth, sta->addr, sta->owe_pmk, + sta->owe_pmk_len, pmkid, 0, WPA_KEY_MGMT_OWE); + + return WLAN_STATUS_SUCCESS; +} + +#endif /* CONFIG_OWE */ + + static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ies, size_t ies_len, int reassoc) { @@ -1531,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) @@ -1550,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; @@ -1642,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 @@ -1690,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 " @@ -1705,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; @@ -1731,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) { @@ -1775,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 @@ -1801,6 +3154,35 @@ } #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); @@ -1810,6 +3192,14 @@ os_memcpy(sta->rrm_enabled_capa, elems.rrm_enabled, sizeof(sta->rrm_enabled_capa)); + if (elems.power_capab) { + sta->min_tx_power = elems.power_capab[0]; + sta->max_tx_power = elems.power_capab[1]; + sta->power_capab = 1; + } else { + sta->power_capab = 0; + } + return WLAN_STATUS_SUCCESS; } @@ -1837,7 +3227,7 @@ static int add_associated_sta(struct hostapd_data *hapd, - struct sta_info *sta) + struct sta_info *sta, int reassoc) { struct ieee80211_ht_capabilities ht_cap; struct ieee80211_vht_capabilities vht_cap; @@ -1853,13 +3243,36 @@ * 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) || - !wpa_auth_sta_ft_tk_already_set(sta->wpa_sm))) { + (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 @@ -1904,21 +3317,40 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, - u16 status_code, int reassoc, const u8 *ies, - size_t ies_len) + const u8 *addr, u16 status_code, int reassoc, + const u8 *ies, size_t ies_len, int 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); @@ -1927,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 */ @@ -1957,7 +3415,7 @@ if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) { u32 nsts = 0, sta_nsts; - if (hapd->conf->use_sta_nsts && sta->vht_capabilities) { + if (sta && hapd->conf->use_sta_nsts && sta->vht_capabilities) { struct ieee80211_vht_capabilities *capa; nsts = (hapd->iface->conf->vht_capab >> @@ -1978,7 +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 @@ -1989,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)); @@ -2009,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 && hapd->p2p_group) { + if (sta && sta->p2p_ie && hapd->p2p_group) { struct wpabuf *p2p_resp_ie; enum p2p_status_code status; switch (status_code) { @@ -2039,10 +3534,10 @@ p = hostapd_eid_p2p_manage(hapd, p); #endif /* CONFIG_P2P_MANAGER */ - p = hostapd_eid_mbo(hapd, p, buf + sizeof(buf) - p); + p = hostapd_eid_mbo(hapd, p, buf + buflen - p); if (hapd->conf->assocresp_elements && - (size_t) (buf + sizeof(buf) - p) >= + (size_t) (buf + buflen - p) >= wpabuf_len(hapd->conf->assocresp_elements)) { os_memcpy(p, wpabuf_head(hapd->conf->assocresp_elements), wpabuf_len(hapd->conf->assocresp_elements)); @@ -2051,19 +3546,155 @@ send_len += p - reply->u.assoc_resp.variable; +#ifdef CONFIG_FILS + if (sta && + (sta->auth_alg == WLAN_AUTH_FILS_SK || + sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sta->auth_alg == WLAN_AUTH_FILS_PK) && + status_code == WLAN_STATUS_SUCCESS) { + struct ieee802_11_elems elems; + + if (ieee802_11_parse_elems(ies, ies_len, &elems, 0) == + ParseFailed || !elems.fils_session) { + res = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto done; + } + + /* FILS Session */ + *p++ = WLAN_EID_EXTENSION; /* Element ID */ + *p++ = 1 + FILS_SESSION_LEN; /* Length */ + *p++ = WLAN_EID_EXT_FILS_SESSION; /* Element ID Extension */ + os_memcpy(p, elems.fils_session, FILS_SESSION_LEN); + send_len += 2 + 1 + FILS_SESSION_LEN; + + send_len = fils_encrypt_assoc(sta->wpa_sm, buf, send_len, + buflen, sta->fils_hlp_resp); + if (send_len < 0) { + res = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto done; + } + } +#endif /* CONFIG_FILS */ + if (hostapd_drv_send_mlme(hapd, reply, send_len, 0) < 0) { wpa_printf(MSG_INFO, "Failed to send assoc resp: %s", strerror(errno)); - return WLAN_STATUS_UNSPECIFIED_FAILURE; + res = WLAN_STATUS_UNSPECIFIED_FAILURE; } - return WLAN_STATUS_SUCCESS; +done: + os_free(buf); + return res; } +#ifdef CONFIG_OWE +u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *owe_dh, u8 owe_dh_len, + u8 *owe_buf, size_t owe_buf_len, u16 *reason) +{ +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->conf->own_ie_override) { + wpa_printf(MSG_DEBUG, "OWE: Using IE override"); + *reason = WLAN_STATUS_SUCCESS; + return wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf, + owe_buf_len, NULL, 0); + } +#endif /* CONFIG_TESTING_OPTIONS */ + + if (wpa_auth_sta_get_pmksa(sta->wpa_sm)) { + wpa_printf(MSG_DEBUG, "OWE: Using PMKSA caching"); + owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf, + owe_buf_len, NULL, 0); + *reason = WLAN_STATUS_SUCCESS; + return owe_buf; + } + + *reason = owe_process_assoc_req(hapd, sta, owe_dh, owe_dh_len); + if (*reason != WLAN_STATUS_SUCCESS) + return NULL; + + owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf, + owe_buf_len, NULL, 0); + + if (sta->owe_ecdh && owe_buf) { + struct wpabuf *pub; + + pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0); + if (!pub) { + *reason = WLAN_STATUS_UNSPECIFIED_FAILURE; + return owe_buf; + } + + /* OWE Diffie-Hellman Parameter element */ + *owe_buf++ = WLAN_EID_EXTENSION; /* Element ID */ + *owe_buf++ = 1 + 2 + wpabuf_len(pub); /* Length */ + *owe_buf++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension + */ + WPA_PUT_LE16(owe_buf, sta->owe_group); + owe_buf += 2; + os_memcpy(owe_buf, wpabuf_head(pub), wpabuf_len(pub)); + owe_buf += wpabuf_len(pub); + wpabuf_free(pub); + } + + return owe_buf; +} +#endif /* CONFIG_OWE */ + + +#ifdef CONFIG_FILS + +void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta) +{ + u16 reply_res; + + wpa_printf(MSG_DEBUG, "FILS: Finish association with " MACSTR, + MAC2STR(sta->addr)); + eloop_cancel_timeout(fils_hlp_timeout, hapd, sta); + if (!sta->fils_pending_assoc_req) + return; + reply_res = send_assoc_resp(hapd, sta, sta->addr, WLAN_STATUS_SUCCESS, + sta->fils_pending_assoc_is_reassoc, + sta->fils_pending_assoc_req, + sta->fils_pending_assoc_req_len, 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, reply_res; @@ -2070,6 +3701,13 @@ const u8 *pos; int left, i; struct sta_info *sta; + u8 *tmp = NULL; + struct hostapd_sta_wpa_psk_short *psk = NULL; + char *identity = NULL; + char *radius_cui = NULL; +#ifdef CONFIG_FILS + int delay_assoc = 0; +#endif /* CONFIG_FILS */ if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) : sizeof(mgmt->u.assoc_req))) { @@ -2127,7 +3765,7 @@ } sta = ap_get_sta(hapd, mgmt->sa); -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (sta && sta->auth_alg == WLAN_AUTH_FT && (sta->flags & WLAN_STA_AUTH) == 0) { wpa_printf(MSG_DEBUG, "FT: Allow STA " MACSTR " to associate " @@ -2140,24 +3778,76 @@ */ sta->flags |= WLAN_STA_AUTH; } else -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) { - hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_INFO, "Station tried to " - "associate before authentication " - "(aid=%d flags=0x%x)", - sta ? sta->aid : -1, - sta ? sta->flags : 0); - send_deauth(hapd, mgmt->sa, - WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA); - return; + if (hapd->iface->current_mode && + hapd->iface->current_mode->mode == + HOSTAPD_MODE_IEEE80211AD) { + int acl_res; + u32 session_timeout, acct_interim_interval; + struct vlan_description vlan_id; + + acl_res = ieee802_11_allowed_address( + hapd, mgmt->sa, (const u8 *) mgmt, len, + &session_timeout, &acct_interim_interval, + &vlan_id, &psk, &identity, &radius_cui, 0); + if (acl_res == HOSTAPD_ACL_REJECT) { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, + "Ignore Association Request frame from " + MACSTR " due to ACL reject", + MAC2STR(mgmt->sa)); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + if (acl_res == HOSTAPD_ACL_PENDING) + return; + + /* DMG/IEEE 802.11ad does not use authentication. + * Allocate sta entry upon association. */ + sta = ap_sta_add(hapd, mgmt->sa); + if (!sta) { + hostapd_logger(hapd, mgmt->sa, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, + "Failed to add STA"); + resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; + goto fail; + } + + acl_res = ieee802_11_set_radius_info( + hapd, sta, acl_res, session_timeout, + acct_interim_interval, &vlan_id, &psk, + &identity, &radius_cui); + if (acl_res) { + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "Skip authentication for DMG/IEEE 802.11ad"); + sta->flags |= WLAN_STA_AUTH; + wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); + sta->auth_alg = WLAN_AUTH_OPEN; + } else { + hostapd_logger(hapd, mgmt->sa, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, + "Station tried to associate before authentication (aid=%d flags=0x%x)", + sta ? sta->aid : -1, + sta ? sta->flags : 0); + send_deauth(hapd, mgmt->sa, + WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA); + return; + } } if ((fc & WLAN_FC_RETRY) && sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ && sta->last_seq_ctrl == seq_ctrl && - sta->last_subtype == reassoc ? WLAN_FC_STYPE_REASSOC_REQ : - WLAN_FC_STYPE_ASSOC_REQ) { + sta->last_subtype == (reassoc ? WLAN_FC_STYPE_REASSOC_REQ : + WLAN_FC_STYPE_ASSOC_REQ)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "Drop repeated association frame seq_ctrl=0x%x", @@ -2169,7 +3859,7 @@ WLAN_FC_STYPE_ASSOC_REQ; if (hapd->tkip_countermeasures) { - resp = WLAN_REASON_MICHAEL_MIC_FAILURE; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } @@ -2187,6 +3877,14 @@ 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 */ /* @@ -2195,6 +3893,32 @@ */ sta->capability = capab_info; +#ifdef CONFIG_FILS + if (sta->auth_alg == WLAN_AUTH_FILS_SK || + sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sta->auth_alg == WLAN_AUTH_FILS_PK) { + int res; + + /* The end of the payload is encrypted. Need to decrypt it + * before parsing. */ + + tmp = os_memdup(pos, left); + if (!tmp) { + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + res = fils_decrypt_assoc(sta->wpa_sm, sta->fils_session, mgmt, + len, tmp, left); + if (res < 0) { + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + pos = tmp; + left = res; + } +#endif /* CONFIG_FILS */ + /* followed by SSID and Supported rates; and HT capabilities if 802.11n * is used */ resp = check_assoc_ies(hapd, sta, pos, left, reassoc); @@ -2210,7 +3934,8 @@ sta->listen_interval = listen_interval; - if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) + if (hapd->iface->current_mode && + hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) sta->flags |= WLAN_STA_NONERP; for (i = 0; i < sta->supported_rates_len; i++) { if ((sta->supported_rates[i] & 0x7f) > 22) { @@ -2229,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); @@ -2244,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); } @@ -2281,7 +4008,22 @@ taxonomy_sta_info_assoc_req(hapd, sta, pos, left); #endif /* CONFIG_TAXONOMY */ + sta->pending_wds_enable = 0; + +#ifdef CONFIG_FILS + if (sta->auth_alg == WLAN_AUTH_FILS_SK || + sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sta->auth_alg == WLAN_AUTH_FILS_PK) { + if (fils_process_hlp(hapd, sta, pos, left) > 0) + delay_assoc = 1; + } +#endif /* CONFIG_FILS */ + fail: + os_free(identity); + os_free(radius_cui); + hostapd_free_psk_list(psk); + /* * In case of a successful response, add the station to the driver. * Otherwise, the kernel may ignore Data frames before we process the @@ -2300,18 +4042,58 @@ * issues with processing other non-Data Class 3 frames during this * window. */ - if (resp == WLAN_STATUS_SUCCESS && add_associated_sta(hapd, sta)) + if (resp == WLAN_STATUS_SUCCESS && sta && + add_associated_sta(hapd, sta, reassoc)) resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; - reply_res = send_assoc_resp(hapd, sta, resp, reassoc, pos, left); +#ifdef CONFIG_FILS + if (sta && 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 ((reply_res != WLAN_STATUS_SUCCESS && - resp == WLAN_STATUS_SUCCESS) || sta->added_unassoc) { + if (sta && ((reply_res != WLAN_STATUS_SUCCESS && + resp == WLAN_STATUS_SUCCESS) || sta->added_unassoc)) { hostapd_drv_sta_remove(hapd, sta->addr); sta->added_unassoc = 0; } @@ -2368,6 +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); + } } @@ -2431,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 && @@ -2462,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)", @@ -2475,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; } @@ -2516,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, @@ -2523,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) @@ -2551,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, @@ -2600,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); @@ -2639,10 +4448,17 @@ struct ieee80211_mgmt *mgmt; u16 fc, stype; int ret = 0; + unsigned int freq; + int ssi_signal = fi ? fi->ssi_signal : 0; if (len < 24) return 0; + if (fi && fi->freq) + freq = fi->freq; + else + freq = hapd->iface->freq; + mgmt = (struct ieee80211_mgmt *) buf; fc = le_to_host16(mgmt->frame_control); stype = WLAN_FC_GET_STYPE(fc); @@ -2669,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", @@ -2682,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: @@ -2712,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, @@ -2734,7 +4552,8 @@ sta = ap_get_sta(hapd, mgmt->da); if (!sta) { - wpa_printf(MSG_INFO, "handle_auth_cb: STA " MACSTR " not found", + wpa_printf(MSG_DEBUG, "handle_auth_cb: STA " MACSTR + " not found", MAC2STR(mgmt->da)); return; } @@ -2856,11 +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); } @@ -2874,16 +4697,6 @@ sta->sa_query_timed_out = 0; #endif /* CONFIG_IEEE80211W */ - if (sta->flags & WLAN_STA_WDS) { - int ret; - char ifname_wds[IFNAMSIZ + 1]; - - ret = hostapd_set_wds_sta(hapd, ifname_wds, sta->addr, - sta->aid, 1); - if (!ret) - hostapd_set_wds_encryption(hapd, sta, ifname_wds); - } - if (sta->eapol_sm == NULL) { /* * This STA does not use RADIUS server for EAP authentication, @@ -2900,6 +4713,27 @@ 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 @@ -2907,6 +4741,18 @@ hapd->new_assoc_sta_cb(hapd, sta, !new_assoc); ieee802_1x_notify_port_enabled(sta->eapol_sm, 1); +#ifdef CONFIG_FILS + if ((sta->auth_alg == WLAN_AUTH_FILS_SK || + sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sta->auth_alg == WLAN_AUTH_FILS_PK) && + fils_set_tk(sta->wpa_sm) < 0) { + wpa_printf(MSG_DEBUG, "FILS: TK configuration failed"); + ap_sta_disconnect(hapd, sta, sta->addr, + WLAN_REASON_UNSPECIFIED); + return; + } +#endif /* CONFIG_FILS */ + if (sta->pending_eapol_rx) { struct os_reltime now, age; @@ -2976,6 +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 @@ -2993,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 */ @@ -3025,6 +4938,7 @@ break; case WLAN_FC_STYPE_ACTION: wpa_printf(MSG_DEBUG, "mgmt::action cb ok=%d", ok); + handle_action_cb(hapd, mgmt, len, ok); break; default: wpa_printf(MSG_INFO, "unknown mgmt cb frame subtype %d", stype); @@ -3139,10 +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]; --- 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); @@ -55,6 +57,9 @@ 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, @@ -76,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, @@ -87,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); @@ -116,6 +123,9 @@ 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, @@ -135,4 +145,36 @@ 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 @@ -244,6 +244,7 @@ * @psk: Linked list buffer for returning WPA PSK * @identity: Buffer for returning identity (from RADIUS) * @radius_cui: Buffer for returning CUI (from RADIUS) + * @is_probe_req: Whether this query for a Probe Request frame * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING * * The caller is responsible for freeing the returned *identity and *radius_cui @@ -254,7 +255,8 @@ u32 *acct_interim_interval, struct vlan_description *vlan_id, struct hostapd_sta_wpa_psk_short **psk, - char **identity, char **radius_cui) + char **identity, char **radius_cui, + int is_probe_req) { int res; @@ -281,6 +283,15 @@ #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 */ res = hostapd_acl_cache_get(hapd, addr, session_timeout, acct_interim_interval, vlan_id, psk, @@ -327,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."); @@ -334,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; @@ -509,7 +519,6 @@ struct hostapd_acl_query_data *query, *prev; struct hostapd_cached_radius_acl *cache; struct radius_hdr *hdr = radius_msg_get_hdr(msg); - int *untagged, *tagged, *notempty; query = hapd->acl_queries; prev = NULL; @@ -567,12 +576,10 @@ cache->acct_interim_interval = 0; } - notempty = &cache->vlan_id.notempty; - untagged = &cache->vlan_id.untagged; - tagged = cache->vlan_id.tagged; - *notempty = !!radius_msg_get_vlanid(msg, untagged, - MAX_NUM_TAGGED_VLAN, - tagged); + 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); @@ -665,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; --- contrib/wpa/src/ap/ieee802_11_auth.h.orig +++ contrib/wpa/src/ap/ieee802_11_auth.h @@ -23,7 +23,8 @@ u32 *acct_interim_interval, struct vlan_description *vlan_id, struct hostapd_sta_wpa_psk_short **psk, - char **identity, char **radius_cui); + char **identity, char **radius_cui, + int is_probe_req); int hostapd_acl_init(struct hostapd_data *hapd); void hostapd_acl_deinit(struct hostapd_data *hapd); void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk); --- 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 @@ -236,17 +236,29 @@ int i; const u8 *start = (const u8 *) mgmt; const u8 *data = start + IEEE80211_HDRLEN + 2; + struct sta_info *sta; + wpa_printf(MSG_DEBUG, + "HT: Received 20/40 BSS Coexistence Management frame from " + MACSTR, MAC2STR(mgmt->sa)); + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "hostapd_public_action - action=%d", mgmt->u.action.u.public_action.action); - if (!(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) + if (!(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) { + wpa_printf(MSG_DEBUG, + "Ignore 20/40 BSS Coexistence Management frame since 40 MHz capability is not enabled"); return; + } - if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie)) + if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie)) { + wpa_printf(MSG_DEBUG, + "Ignore too short 20/40 BSS Coexistence Management frame"); return; + } + /* 20/40 BSS Coexistence element */ bc_ie = (struct ieee80211_2040_bss_coex_ie *) data; if (bc_ie->element_id != WLAN_EID_20_40_BSS_COEXISTENCE || bc_ie->length < 1) { @@ -254,13 +266,35 @@ bc_ie->element_id, bc_ie->length); return; } - if (len < IEEE80211_HDRLEN + 2 + 2 + bc_ie->length) + if (len < IEEE80211_HDRLEN + 2 + 2 + bc_ie->length) { + wpa_printf(MSG_DEBUG, + "Truncated 20/40 BSS Coexistence element"); return; + } data += 2 + bc_ie->length; - wpa_printf(MSG_DEBUG, "20/40 BSS Coexistence Information field: 0x%x", - bc_ie->coex_param); + wpa_printf(MSG_DEBUG, + "20/40 BSS Coexistence Information field: 0x%x (%s%s%s%s%s%s)", + bc_ie->coex_param, + (bc_ie->coex_param & BIT(0)) ? "[InfoReq]" : "", + (bc_ie->coex_param & BIT(1)) ? "[40MHzIntolerant]" : "", + (bc_ie->coex_param & BIT(2)) ? "[20MHzBSSWidthReq]" : "", + (bc_ie->coex_param & BIT(3)) ? "[OBSSScanExemptionReq]" : "", + (bc_ie->coex_param & BIT(4)) ? + "[OBSSScanExemptionGrant]" : "", + (bc_ie->coex_param & (BIT(5) | BIT(6) | BIT(7))) ? + "[Reserved]" : ""); + if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ) { + /* Intra-BSS communication prohibiting 20/40 MHz BSS operation + */ + sta = ap_get_sta(hapd, mgmt->sa); + if (!sta || !(sta->flags & WLAN_STA_ASSOC)) { + wpa_printf(MSG_DEBUG, + "Ignore intra-BSS 20/40 BSS Coexistence Management frame from not-associated STA"); + return; + } + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, @@ -269,6 +303,8 @@ } if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_40MHZ_INTOL) { + /* Inter-BSS communication prohibiting 20/40 MHz BSS operation + */ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, @@ -276,12 +312,16 @@ is_ht40_allowed = 0; } - if (start + len - data >= 3 && - data[0] == WLAN_EID_20_40_BSS_INTOLERANT && data[1] >= 1) { + /* 20/40 BSS Intolerant Channel Report element (zero or more times) */ + while (start + len - data >= 3 && + data[0] == WLAN_EID_20_40_BSS_INTOLERANT && data[1] >= 1) { u8 ielen = data[1]; - if (ielen > start + len - data - 2) + if (ielen > start + len - data - 2) { + wpa_printf(MSG_DEBUG, + "Truncated 20/40 BSS Intolerant Channel Report element"); return; + } ic_report = (struct ieee80211_2040_intol_chan_report *) data; wpa_printf(MSG_DEBUG, "20/40 BSS Intolerant Channel Report: Operating Class %u", @@ -292,8 +332,10 @@ for (i = 0; i < ielen - 1; i++) { u8 chan = ic_report->variable[i]; + if (chan == iface->conf->channel) + continue; /* matching own primary channel */ if (is_40_allowed(iface, chan)) - continue; + continue; /* not within affected channels */ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, @@ -301,6 +343,8 @@ chan); is_ht40_allowed = 0; } + + data += 2 + ielen; } wpa_printf(MSG_DEBUG, "is_ht40_allowed=%d num_sta_ht40_intolerant=%d", is_ht40_allowed, iface->num_sta_ht40_intolerant); @@ -340,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"); @@ -178,6 +321,10 @@ case 1: /* Bits 8-15 */ if (hapd->conf->proxy_arp) *pos |= 0x10; /* Bit 12 - Proxy ARP */ + if (hapd->conf->coloc_intf_reporting) { + /* Bit 13 - Collocated Interference Reporting */ + *pos |= 0x20; + } break; case 2: /* Bits 16-23 */ if (hapd->conf->wnm_sleep_mode) @@ -186,9 +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) @@ -218,6 +365,8 @@ 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 */ @@ -224,6 +373,28 @@ 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; } } @@ -246,10 +417,10 @@ if (len < 9 && (hapd->conf->ftm_initiator || hapd->conf->ftm_responder)) len = 9; -#ifdef CONFIG_WNM +#ifdef CONFIG_WNM_AP if (len < 4) len = 4; -#endif /* CONFIG_WNM */ +#endif /* CONFIG_WNM_AP */ #ifdef CONFIG_HS20 if (hapd->conf->hs20 && len < 6) len = 6; @@ -258,6 +429,17 @@ 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) @@ -432,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); @@ -503,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; @@ -521,7 +703,7 @@ pos += 2; *pos++ = 0x00; /* TODO: Protected Keep-Alive Required */ } -#endif /* CONFIG_WNM */ +#endif /* CONFIG_WNM_AP */ return pos; } @@ -529,25 +711,56 @@ #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[6], *mbo_pos = mbo; + u8 mbo[9], *mbo_pos = mbo; u8 *pos = eid; - if (!hapd->conf->mbo_enabled) + if (!hapd->conf->mbo_enabled && + !OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd)) return eid; - *mbo_pos++ = MBO_ATTR_ID_AP_CAPA_IND; - *mbo_pos++ = 1; - /* Not Cellular aware */ - *mbo_pos++ = 0; + if (hapd->conf->mbo_enabled) { + *mbo_pos++ = MBO_ATTR_ID_AP_CAPA_IND; + *mbo_pos++ = 1; + /* Not Cellular aware */ + *mbo_pos++ = 0; + } - if (hapd->mbo_assoc_disallow) { + if (hapd->conf->mbo_enabled && hapd->mbo_assoc_disallow) { *mbo_pos++ = MBO_ATTR_ID_ASSOC_DISALLOW; *mbo_pos++ = 1; *mbo_pos++ = hapd->mbo_assoc_disallow; } + if (OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd)) { + u8 ctrl; + + ctrl = OCE_RELEASE; + if (OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd)) + ctrl |= OCE_IS_STA_CFON; + + *mbo_pos++ = OCE_ATTR_ID_CAPA_IND; + *mbo_pos++ = 1; + *mbo_pos++ = ctrl; + } + pos += mbo_add_ie(pos, len, mbo, mbo_pos - mbo); return pos; @@ -556,7 +769,10 @@ u8 hostapd_mbo_ie_len(struct hostapd_data *hapd) { - if (!hapd->conf->mbo_enabled) + u8 len; + + if (!hapd->conf->mbo_enabled && + !OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd)) return 0; /* @@ -563,12 +779,81 @@ * MBO IE header (6) + Capability Indication attribute (3) + * Association Disallowed attribute (3) = 12 */ - return 6 + 3 + (hapd->mbo_assoc_disallow ? 3 : 0); + len = 6; + if (hapd->conf->mbo_enabled) + len += 3 + (hapd->mbo_assoc_disallow ? 3 : 0); + + /* OCE capability indication attribute (3) */ + if (OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd)) + len += 3; + + return len; } #endif /* CONFIG_MBO */ +#ifdef CONFIG_OWE +static int hostapd_eid_owe_trans_enabled(struct hostapd_data *hapd) +{ + return hapd->conf->owe_transition_ssid_len > 0 && + !is_zero_ether_addr(hapd->conf->owe_transition_bssid); +} +#endif /* CONFIG_OWE */ + + +size_t hostapd_eid_owe_trans_len(struct hostapd_data *hapd) +{ +#ifdef CONFIG_OWE + if (!hostapd_eid_owe_trans_enabled(hapd)) + return 0; + return 6 + ETH_ALEN + 1 + hapd->conf->owe_transition_ssid_len; +#else /* CONFIG_OWE */ + return 0; +#endif /* CONFIG_OWE */ +} + + +u8 * hostapd_eid_owe_trans(struct hostapd_data *hapd, u8 *eid, + size_t len) +{ +#ifdef CONFIG_OWE + u8 *pos = eid; + size_t elen; + + if (hapd->conf->owe_transition_ifname[0] && + !hostapd_eid_owe_trans_enabled(hapd)) + hostapd_owe_trans_get_info(hapd); + + if (!hostapd_eid_owe_trans_enabled(hapd)) + return pos; + + elen = hostapd_eid_owe_trans_len(hapd); + if (len < elen) { + wpa_printf(MSG_DEBUG, + "OWE: Not enough room in the buffer for OWE IE"); + return pos; + } + + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + *pos++ = elen - 2; + WPA_PUT_BE24(pos, OUI_WFA); + pos += 3; + *pos++ = OWE_OUI_TYPE; + os_memcpy(pos, hapd->conf->owe_transition_bssid, ETH_ALEN); + pos += ETH_ALEN; + *pos++ = hapd->conf->owe_transition_ssid_len; + os_memcpy(pos, hapd->conf->owe_transition_ssid, + hapd->conf->owe_transition_ssid_len); + pos += hapd->conf->owe_transition_ssid_len; + + return pos; +#else /* CONFIG_OWE */ + return eid; +#endif /* CONFIG_OWE */ +} + + void ap_copy_sta_supp_op_classes(struct sta_info *sta, const u8 *supp_op_classes, size_t supp_op_classes_len) @@ -584,3 +869,134 @@ 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 @@ -334,7 +334,7 @@ { /* 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); @@ -357,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) { --- 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,6 +31,8 @@ #include "ap_drv_ops.h" #include "wps_hostapd.h" #include "hs20.h" +/* FIX: Not really a good thing to require ieee802_11.h here.. (FILS) */ +#include "ieee802_11.h" #include "ieee802_1x.h" @@ -316,6 +318,7 @@ hdr->code != EAP_CODE_INITIATE)) return; + eap_erp_update_identity(sm->eap, eap, len); identity = eap_get_identity(sm->eap, &identity_len); if (identity == NULL) return; @@ -472,7 +475,7 @@ } } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) && sta->wpa_sm && (wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm)) || @@ -485,7 +488,7 @@ wpa_printf(MSG_ERROR, "Could not add Mobility-Domain-Id"); return -1; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ if ((hapd->conf->wpa || hapd->conf->osen) && sta->wpa_sm && add_common_radius_sta_attr_rsn(hapd, req_attr, sta, msg) < 0) @@ -588,9 +591,9 @@ } -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; @@ -679,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)) { @@ -709,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 */ @@ -845,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; @@ -970,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; @@ -1113,7 +1154,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 STA - using PSK"); /* * Clear any possible EAPOL authenticator state to support @@ -1154,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, @@ -1170,11 +1213,34 @@ 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, @@ -1395,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++; } @@ -1559,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 */ @@ -1606,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 */ @@ -1645,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 @@ -1663,16 +1800,11 @@ struct sta_info *sta; u32 session_timeout = 0, termination_action, acct_interim_interval; int session_timeout_set; + u32 reason_code; struct eapol_state_machine *sm; int override_eapReq = 0; struct radius_hdr *hdr = radius_msg_get_hdr(msg); - struct vlan_description vlan_desc; -#ifndef CONFIG_NO_VLAN - int *untagged, *tagged, *notempty; -#endif /* CONFIG_NO_VLAN */ - os_memset(&vlan_desc, 0, sizeof(vlan_desc)); - sm = ieee802_1x_search_radius_identifier(hapd, hdr->identifier); if (sm == NULL) { wpa_printf(MSG_DEBUG, "IEEE 802.1X: Could not find matching " @@ -1736,45 +1868,10 @@ switch (hdr->code) { case RADIUS_CODE_ACCESS_ACCEPT: #ifndef CONFIG_NO_VLAN - if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED) { - notempty = &vlan_desc.notempty; - untagged = &vlan_desc.untagged; - tagged = vlan_desc.tagged; - *notempty = !!radius_msg_get_vlanid(msg, untagged, - MAX_NUM_TAGGED_VLAN, - 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); + if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED && + ieee802_1x_update_vlan(msg, hapd, sta) < 0) break; - } - 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"); - break; - } -#endif /* CONFIG_NO_VLAN */ - - if (ap_sta_set_vlan(hapd, sta, &vlan_desc) < 0) - break; - -#ifndef CONFIG_NO_VLAN if (sta->vlan_id > 0) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, @@ -1781,21 +1878,24 @@ HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id); } -#endif /* CONFIG_NO_VLAN */ 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; @@ -1811,6 +1911,13 @@ case RADIUS_CODE_ACCESS_REJECT: sm->eap_if->aaaFail = TRUE; override_eapReq = 1; + if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_WLAN_REASON_CODE, + &reason_code) == 0) { + wpa_printf(MSG_DEBUG, + "RADIUS server indicated WLAN-Reason-Code %u in Access-Reject for " + MACSTR, reason_code, MAC2STR(sta->addr)); + sta->disconnect_reason_code = reason_code; + } break; case RADIUS_CODE_ACCESS_CHALLENGE: sm->eap_if->aaaEapReq = TRUE; @@ -1837,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; @@ -1924,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 " @@ -2034,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; @@ -2190,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; @@ -2326,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; @@ -2458,6 +2595,7 @@ struct os_reltime diff; const char *name1; const char *name2; + char *identity_buf = NULL; if (sm == NULL) return 0; @@ -2573,6 +2711,14 @@ /* 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 */ @@ -2588,7 +2734,9 @@ 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; @@ -2642,6 +2790,15 @@ hs20_send_wnm_notification_deauth_req(hapd, sta->addr, sta->hs20_deauth_req); } + + if (sta->hs20_t_c_filtering) { + wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to " + MACSTR " to indicate Terms and Conditions filtering", + MAC2STR(sta->addr)); + hs20_send_wnm_notification_t_c(hapd, sta->addr, sta->t_c_url); + os_free(sta->t_c_url); + sta->t_c_url = NULL; + } } #endif /* CONFIG_HS20 */ @@ -2655,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) { @@ -2665,7 +2823,8 @@ sta->remediation_method = 1; /* SOAP-XML SPP */ } - if (success && (sta->remediation || sta->hs20_deauth_req)) { + if (success && (sta->remediation || sta->hs20_deauth_req || + sta->hs20_t_c_filtering)) { wpa_printf(MSG_DEBUG, "HS 2.0: Schedule WNM-Notification to " MACSTR " in 100 ms", MAC2STR(sta->addr)); eloop_cancel_timeout(ieee802_1x_wnm_notif_send, hapd, sta); @@ -2675,10 +2834,13 @@ #endif /* CONFIG_HS20 */ key = ieee802_1x_get_key(sta->eapol_sm, &len); - if (sta->session_timeout_set) - session_timeout = sta->session_timeout; - else + if (sta->session_timeout_set) { + os_get_reltime(&now); + os_reltime_sub(&sta->session_timeout, &now, &remaining); + session_timeout = (remaining.sec > 0) ? remaining.sec : 1; + } else { session_timeout = dot11RSNAConfigPMKLifetime; + } if (success && key && len >= PMK_LEN && !sta->remediation && !sta->hs20_deauth_requested && wpa_auth_pmksa_add(sta->wpa_sm, key, len, session_timeout, @@ -2699,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 @@ -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/ndisc_snoop.c.orig +++ contrib/wpa/src/ap/ndisc_snoop.c @@ -182,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 @@ -11,6 +11,7 @@ #include "utils/common.h" #include "hostapd.h" +#include "ieee802_11.h" #include "neighbor_db.h" @@ -43,6 +44,7 @@ nr->civic = NULL; os_memset(nr->bssid, 0, sizeof(nr->bssid)); os_memset(&nr->ssid, 0, sizeof(nr->ssid)); + nr->stationary = 0; } @@ -64,7 +66,7 @@ int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid, const struct wpa_ssid_value *ssid, const struct wpabuf *nr, const struct wpabuf *lci, - const struct wpabuf *civic) + const struct wpabuf *civic, int stationary) { struct hostapd_neighbor_entry *entry; @@ -83,18 +85,20 @@ if (!entry->nr) goto fail; - if (lci) { + if (lci && wpabuf_len(lci)) { entry->lci = wpabuf_dup(lci); if (!entry->lci || os_get_time(&entry->lci_date)) goto fail; } - if (civic) { + if (civic && wpabuf_len(civic)) { entry->civic = wpabuf_dup(civic); if (!entry->civic) goto fail; } + entry->stationary = stationary; + return 0; fail: @@ -120,7 +124,7 @@ } -void hostpad_free_neighbor_db(struct hostapd_data *hapd) +void hostapd_free_neighbor_db(struct hostapd_data *hapd) { struct hostapd_neighbor_entry *nr, *prev; @@ -131,3 +135,123 @@ 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 @@ -16,9 +16,10 @@ int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid, const struct wpa_ssid_value *ssid, const struct wpabuf *nr, const struct wpabuf *lci, - const struct wpabuf *civic); + const struct wpabuf *civic, int stationary); +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 hostpad_free_neighbor_db(struct hostapd_data *hapd); +void hostapd_free_neighbor_db(struct hostapd_data *hapd); #endif /* NEIGHBOR_DB_H */ --- contrib/wpa/src/ap/pmksa_cache_auth.c.orig +++ contrib/wpa/src/ap/pmksa_cache_auth.c @@ -282,7 +282,42 @@ const u8 *aa, const u8 *spa, int session_timeout, struct eapol_state_machine *eapol, int akmp) { - struct rsn_pmksa_cache_entry *entry, *pos; + struct rsn_pmksa_cache_entry *entry; + + entry = pmksa_cache_auth_create_entry(pmk, pmk_len, pmkid, kck, kck_len, + aa, spa, session_timeout, eapol, + akmp); + + if (pmksa_cache_auth_add_entry(pmksa, entry) < 0) + return NULL; + + return entry; +} + + +/** + * pmksa_cache_auth_create_entry - Create a PMKSA cache entry + * @pmk: The new pairwise master key + * @pmk_len: PMK length in bytes, usually PMK_LEN (32) + * @pmkid: Calculated PMKID + * @kck: Key confirmation key or %NULL if not yet derived + * @kck_len: KCK length in bytes + * @aa: Authenticator address + * @spa: Supplicant address + * @session_timeout: Session timeout + * @eapol: Pointer to EAPOL state machine data + * @akmp: WPA_KEY_MGMT_* used in key derivation + * Returns: Pointer to the added PMKSA cache entry or %NULL on error + * + * This function creates a PMKSA entry. + */ +struct rsn_pmksa_cache_entry * +pmksa_cache_auth_create_entry(const u8 *pmk, size_t pmk_len, const u8 *pmkid, + const u8 *kck, size_t kck_len, const u8 *aa, + const u8 *spa, int session_timeout, + struct eapol_state_machine *eapol, int akmp) +{ + struct rsn_pmksa_cache_entry *entry; struct os_reltime now; if (pmk_len > PMK_LEN_MAX) @@ -303,8 +338,7 @@ else if (wpa_key_mgmt_suite_b(akmp)) rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid); else - rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, - wpa_key_mgmt_sha256(akmp)); + rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, akmp); os_get_reltime(&now); entry->expiration = now.sec; if (session_timeout > 0) @@ -315,9 +349,30 @@ os_memcpy(entry->spa, spa, ETH_ALEN); pmksa_cache_from_eapol_data(entry, eapol); + return entry; +} + + +/** + * pmksa_cache_auth_add_entry - Add a PMKSA cache entry + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() + * @entry: Pointer to PMKSA cache entry + * + * This function adds PMKSA cache entry to the PMKSA cache. If an old entry is + * already in the cache for the same Supplicant, this entry will be replaced + * with the new entry. PMKID will be calculated based on the PMK. + */ +int pmksa_cache_auth_add_entry(struct rsn_pmksa_cache *pmksa, + struct rsn_pmksa_cache_entry *entry) +{ + struct rsn_pmksa_cache_entry *pos; + + if (entry == NULL) + return -1; + /* Replace an old entry for the same STA (if found) with the new entry */ - pos = pmksa_cache_auth_get(pmksa, spa, NULL); + pos = pmksa_cache_auth_get(pmksa, entry->spa, NULL); if (pos) pmksa_cache_free_entry(pmksa, pos); @@ -331,7 +386,7 @@ pmksa_cache_link_entry(pmksa, entry); - return entry; + return 0; } @@ -462,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; } @@ -605,3 +660,70 @@ } 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 @@ -35,6 +35,7 @@ }; struct rsn_pmksa_cache; +struct radius_das_attrs; struct rsn_pmksa_cache * pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, @@ -53,6 +54,13 @@ const u8 *aa, const u8 *spa, int session_timeout, struct eapol_state_machine *eapol, int akmp); struct rsn_pmksa_cache_entry * +pmksa_cache_auth_create_entry(const u8 *pmk, size_t pmk_len, const u8 *pmkid, + const u8 *kck, size_t kck_len, const u8 *aa, + const u8 *spa, int session_timeout, + struct eapol_state_machine *eapol, int akmp); +int pmksa_cache_auth_add_entry(struct rsn_pmksa_cache *pmksa, + struct rsn_pmksa_cache_entry *entry); +struct rsn_pmksa_cache_entry * pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa, const struct rsn_pmksa_cache_entry *old_entry, const u8 *aa, const u8 *pmkid); @@ -65,5 +73,7 @@ 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 @@ -2,6 +2,7 @@ * hostapd / Radio Measurement (RRM) * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH. * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved. + * Copyright (c) 2016-2017, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -10,6 +11,7 @@ #include "utils/includes.h" #include "utils/common.h" +#include "common/wpa_ctrl.h" #include "hostapd.h" #include "ap_drv_ops.h" #include "sta_info.h" @@ -69,12 +71,33 @@ } +static void hostapd_handle_beacon_report(struct hostapd_data *hapd, + const u8 *addr, u8 token, u8 rep_mode, + const u8 *pos, size_t len) +{ + char report[2 * 255 + 1]; + + wpa_printf(MSG_DEBUG, "Beacon report token %u len %zu from " MACSTR, + token, len, MAC2STR(addr)); + /* Skip to the beginning of the Beacon report */ + if (len < 3) + return; + pos += 3; + len -= 3; + report[0] = '\0'; + if (wpa_snprintf_hex(report, sizeof(report), pos, len) < 0) + return; + wpa_msg(hapd->msg_ctx, MSG_INFO, BEACON_RESP_RX MACSTR " %u %02x %s", + MAC2STR(addr), token, rep_mode, report); +} + + static void hostapd_handle_radio_msmt_report(struct hostapd_data *hapd, const u8 *buf, size_t len) { const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf; const u8 *pos, *ie, *end; - u8 token; + u8 token, rep_mode; end = buf + len; token = mgmt->u.action.u.rrm.dialog_token; @@ -81,12 +104,14 @@ pos = mgmt->u.action.u.rrm.variable; while ((ie = get_ie(pos, end - pos, WLAN_EID_MEASURE_REPORT))) { - if (ie[1] < 5) { + if (ie[1] < 3) { wpa_printf(MSG_DEBUG, "Bad Measurement Report element"); break; } - wpa_printf(MSG_DEBUG, "Measurement report type %u", ie[4]); + rep_mode = ie[3]; + wpa_printf(MSG_DEBUG, "Measurement report mode 0x%x type %u", + rep_mode, ie[4]); switch (ie[4]) { case MEASURE_TYPE_LCI: @@ -95,6 +120,10 @@ case MEASURE_TYPE_FTM_RANGE: hostapd_handle_range_report(hapd, token, ie + 2, ie[1]); break; + case MEASURE_TYPE_BEACON: + hostapd_handle_beacon_report(hapd, mgmt->sa, token, + rep_mode, ie + 2, ie[1]); + break; default: wpa_printf(MSG_DEBUG, "Measurement report type %u is not supported", @@ -118,7 +147,7 @@ /* Subelements are arranged as IEs */ subelem = get_ie(buf + 4, len - 4, LCI_REQ_SUBELEM_MAX_AGE); if (subelem && subelem[1] == 2) - return *(u16 *) (subelem + 2); + return WPA_GET_LE16(subelem + 2); return 0; } @@ -129,12 +158,12 @@ struct os_time curr, diff; unsigned long diff_l; + if (nr->stationary || max_age == 0xffff) + return 1; + if (!max_age) return 0; - if (max_age == 0xffff) - return 1; - if (os_get_time(&curr)) return 0; @@ -341,14 +370,8 @@ struct sta_info *sta = ap_get_sta(hapd, addr); int ret; - if (!sta) { + if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) { wpa_printf(MSG_INFO, - "Request LCI: Destination address is not in station list"); - return -1; - } - - if (!(sta->flags & WLAN_STA_AUTHORIZED)) { - wpa_printf(MSG_INFO, "Request LCI: Destination address is not connected"); return -1; } @@ -450,9 +473,8 @@ wpa_printf(MSG_DEBUG, "Request range: Range request is already in process; overriding"); hapd->range_req_active = 0; - eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0, - hostapd_range_rep_timeout_handler, hapd, - NULL); + eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, + NULL); } /* Action + measurement type + token + reps + EID + len = 7 */ @@ -536,9 +558,117 @@ void hostapd_clean_rrm(struct hostapd_data *hapd) { - hostpad_free_neighbor_db(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 @@ -24,5 +24,10 @@ 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" @@ -36,6 +38,7 @@ #include "ndisc_snoop.h" #include "sta_info.h" #include "vlan.h" +#include "wps_hostapd.h" static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd, struct sta_info *sta); @@ -47,6 +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, @@ -163,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) @@ -194,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++; } @@ -202,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++; } @@ -316,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 */ @@ -322,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); @@ -337,6 +346,36 @@ 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); } @@ -468,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", @@ -597,7 +643,7 @@ static void ap_handle_session_warning_timer(void *eloop_ctx, void *timeout_ctx) { -#ifdef CONFIG_WNM +#ifdef CONFIG_WNM_AP struct hostapd_data *hapd = eloop_ctx; struct sta_info *sta = timeout_ctx; @@ -608,7 +654,7 @@ wnm_send_ess_disassoc_imminent(hapd, sta, sta->hs20_session_info_url, sta->hs20_disassoc_timer); -#endif /* CONFIG_WNM */ +#endif /* CONFIG_WNM_AP */ } @@ -745,9 +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)", @@ -758,6 +812,8 @@ ap_handle_timer, hapd, sta); accounting_sta_stop(hapd, 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; @@ -783,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; @@ -848,9 +912,6 @@ struct hostapd_vlan *vlan = NULL, *wildcard_vlan = NULL; int old_vlan_id, vlan_id = 0, ret = 0; - if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED) - vlan_desc = NULL; - /* Check if there is something to do */ if (hapd->conf->ssid.per_sta_vif && !sta->vlan_id) { /* This sta is lacking its own vif */ @@ -1117,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) { @@ -1155,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) { @@ -1166,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); @@ -1229,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); @@ -1275,6 +1386,15 @@ "%s: Removed ap_sta_disassoc_cb_timeout timeout for " MACSTR, hapd->conf->iface, MAC2STR(sta->addr)); + if (eloop_cancel_timeout(ap_sta_delayed_1x_auth_fail_cb, hapd, sta) > 0) + { + wpa_printf(MSG_DEBUG, + "%s: Removed ap_sta_delayed_1x_auth_fail_cb timeout for " + MACSTR, + hapd->conf->iface, MAC2STR(sta->addr)); + if (sta->flags & WLAN_STA_WPS) + hostapd_wps_eap_completed(hapd); + } } @@ -1283,7 +1403,7 @@ int res; buf[0] = '\0'; - res = os_snprintf(buf, buflen, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + res = os_snprintf(buf, buflen, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", (flags & WLAN_STA_AUTH ? "[AUTH]" : ""), (flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""), (flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" : ""), @@ -1300,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 ? @@ -1309,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,14 +9,11 @@ #ifndef STA_INFO_H #define STA_INFO_H -#ifdef CONFIG_MESH -/* needed for mesh_plink_state enum */ #include "common/defs.h" -#include "common/wpa_common.h" -#endif /* CONFIG_MESH */ - #include "list.h" #include "vlan.h" +#include "common/wpa_common.h" +#include "common/ieee802_11_defs.h" /* STA flags */ #define WLAN_STA_AUTH BIT(0) @@ -38,6 +35,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) @@ -46,6 +45,7 @@ * Supported Rates IEs). */ #define WLAN_SUPP_RATES_MAX 32 +struct hostapd_data; struct mbo_non_pref_chan_info { struct mbo_non_pref_chan_info *next; @@ -68,6 +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 */ @@ -113,6 +114,11 @@ 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; @@ -158,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 @@ -170,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; @@ -195,7 +205,8 @@ unsigned int mesh_sae_pmksa_caching:1; #endif /* CONFIG_SAE */ - u32 session_timeout; /* valid only if session_timeout_set == 1 */ + /* valid only if session_timeout_set == 1 */ + struct os_reltime session_timeout; /* Last Authentication/(Re)Association Request/Action frame sequence * control */ @@ -207,6 +218,7 @@ 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 @@ -214,10 +226,55 @@ 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 */ }; @@ -237,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), @@ -273,6 +328,8 @@ 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); @@ -289,5 +346,9 @@ 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 @@ -21,6 +21,7 @@ #include "common/wpa_ctrl.h" #include "hostapd.h" #include "sta_info.h" +#include "taxonomy.h" /* Copy a string with no funny schtuff allowed; only alphanumerics. */ --- 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_full.c.orig +++ contrib/wpa/src/ap/vlan_full.c @@ -16,6 +16,7 @@ #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" @@ -143,6 +144,9 @@ 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) { @@ -168,6 +172,7 @@ return -1; } +done: close(fd); return 0; } @@ -194,6 +199,14 @@ 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) { @@ -224,6 +237,7 @@ return -1; } +done: close(fd); return 0; } @@ -241,6 +255,9 @@ return -1; } + if (linux_br_del(fd, br_name) == 0) + goto done; + arg[0] = BRCTL_DEL_BRIDGE; arg[1] = (unsigned long) br_name; @@ -252,6 +269,7 @@ return -1; } +done: close(fd); return 0; } @@ -277,11 +295,19 @@ 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) { + if (errno == EEXIST) { /* The bridge is already added. */ close(fd); return 1; @@ -294,6 +320,7 @@ } } +done: /* Decrease forwarding delay to avoid EAPOL timeouts. */ os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, br_name, IFNAMSIZ); @@ -363,12 +390,18 @@ { char vlan_ifname[IFNAMSIZ]; int clean; + int ret; if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE) - os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d", - tagged_interface, vid); + ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d", + tagged_interface, vid); else - os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d", vid); + 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); @@ -384,19 +417,28 @@ } -static void vlan_bridge_name(char *br_name, struct hostapd_data *hapd, int vid) +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 (hapd->conf->vlan_bridge[0]) { - os_snprintf(br_name, IFNAMSIZ, "%s%d", - hapd->conf->vlan_bridge, vid); + 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) { - os_snprintf(br_name, IFNAMSIZ, "br%s.%d", - tagged_interface, vid); + ret = os_snprintf(br_name, IFNAMSIZ, "br%s.%d", + tagged_interface, vid); } else { - os_snprintf(br_name, IFNAMSIZ, "brvlan%d", vid); + 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); } @@ -445,7 +487,7 @@ !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, untagged); + vlan_bridge_name(br_name, hapd, vlan, untagged); vlan_get_bridge(br_name, hapd, untagged); @@ -458,7 +500,7 @@ tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID || (i > 0 && tagged[i] == tagged[i - 1])) continue; - vlan_bridge_name(br_name, hapd, tagged[i]); + 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); @@ -474,13 +516,20 @@ { char vlan_ifname[IFNAMSIZ]; int clean; + int ret; if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE) - os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d", - tagged_interface, vid); + ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d", + tagged_interface, vid); else - os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d", vid); + 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) @@ -543,7 +592,7 @@ tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID || (i > 0 && tagged[i] == tagged[i - 1])) continue; - vlan_bridge_name(br_name, hapd, tagged[i]); + 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]); @@ -555,7 +604,7 @@ (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, untagged); + vlan_bridge_name(br_name, hapd, vlan, untagged); if (vlan->clean & DVLAN_CLEAN_WLAN_PORT) br_delif(br_name, vlan->ifname); --- contrib/wpa/src/ap/vlan_init.c.orig +++ contrib/wpa/src/ap/vlan_init.c @@ -138,6 +138,8 @@ !hapd->conf->vlan) { /* dynamic vlans enabled but no (or empty) vlan_file given */ struct hostapd_vlan *vlan; + int ret; + vlan = os_zalloc(sizeof(*vlan)); if (vlan == NULL) { wpa_printf(MSG_ERROR, "Out of memory while assigning " @@ -146,8 +148,16 @@ } 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; } @@ -177,6 +187,7 @@ { struct hostapd_vlan *n; char ifname[IFNAMSIZ + 1], *pos; + int ret; if (vlan == NULL || vlan->vlan_id != VLAN_ID_WILDCARD) return NULL; @@ -198,8 +209,13 @@ n->vlan_desc = *vlan_desc; n->dynamic_vlan = 1; - os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", ifname, vlan_id, - pos); + 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); + return NULL; + } + os_strlcpy(n->bridge, vlan->bridge, sizeof(n->bridge)); n->next = hapd->conf->vlan; hapd->conf->vlan = n; --- 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,6 +12,7 @@ #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" @@ -54,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; @@ -88,15 +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"); - os_free(wnmtfs_ie); - return -1; + res = -1; + goto fail; } os_memcpy(mgmt->da, addr, ETH_ALEN); os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); @@ -109,6 +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 { @@ -118,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", @@ -136,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 */ @@ -176,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 @@ -184,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; } @@ -201,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); @@ -224,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) { @@ -251,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); @@ -279,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 " @@ -307,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)); @@ -326,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) @@ -336,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)); @@ -354,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 @@ -368,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); @@ -401,6 +533,48 @@ } +static void ieee802_11_rx_wnm_coloc_intf_report(struct hostapd_data *hapd, + const u8 *addr, const u8 *buf, + size_t len) +{ + u8 dialog_token; + char *hex; + size_t hex_len; + + if (!hapd->conf->coloc_intf_reporting) { + wpa_printf(MSG_DEBUG, + "WNM: Ignore unexpected Collocated Interference Report from " + MACSTR, MAC2STR(addr)); + return; + } + + if (len < 1) { + wpa_printf(MSG_DEBUG, + "WNM: Ignore too short Collocated Interference Report from " + MACSTR, MAC2STR(addr)); + return; + } + dialog_token = *buf++; + len--; + + wpa_printf(MSG_DEBUG, + "WNM: Received Collocated Interference Report frame from " + MACSTR " (dialog_token=%u)", + MAC2STR(addr), dialog_token); + wpa_hexdump(MSG_MSGDUMP, "WNM: Collocated Interference Report Elements", + buf, len); + + hex_len = 2 * len + 1; + hex = os_malloc(hex_len); + if (!hex) + return; + wpa_snprintf_hex(hex, hex_len, buf, len); + wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, COLOC_INTF_REPORT MACSTR " %d %s", + MAC2STR(addr), dialog_token, hex); + os_free(hex); +} + + int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len) { @@ -431,6 +605,10 @@ ieee802_11_rx_wnm_notification_req(hapd, mgmt->sa, payload, plen); return 0; + case WNM_COLLOCATED_INTERFERENCE_REPORT: + ieee802_11_rx_wnm_coloc_intf_report(hapd, mgmt->sa, payload, + plen); + return 0; } wpa_printf(MSG_DEBUG, "WNM: Unsupported WNM Action %u from " MACSTR, @@ -629,3 +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 @@ -23,5 +23,8 @@ const u8 *bss_term_dur, const char *url, const u8 *nei_rep, size_t nei_rep_len, const u8 *mbo_attrs, size_t mbo_len); +void ap_sta_reset_steer_flag_timer(void *eloop_ctx, void *timeout_ctx); +int wnm_send_coloc_intf_req(struct hostapd_data *hapd, struct sta_info *sta, + unsigned int auto_report, unsigned int timeout); #endif /* WNM_AP_H */ --- 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); @@ -52,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; @@ -68,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; } @@ -77,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); } @@ -86,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); } @@ -94,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); } @@ -103,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); } @@ -115,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); } @@ -126,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); } @@ -136,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); } @@ -146,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); } @@ -157,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 */ @@ -168,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); } @@ -178,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); } @@ -187,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); } @@ -200,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; @@ -219,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; @@ -305,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) @@ -340,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; @@ -409,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; @@ -418,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."); @@ -443,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."); @@ -453,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, @@ -506,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); @@ -599,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 " @@ -606,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; @@ -660,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)); } @@ -680,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); @@ -689,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(). */ @@ -739,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) @@ -786,7 +835,7 @@ return 0; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ static int wpa_receive_error_report(struct wpa_authenticator *wpa_auth, @@ -828,29 +877,44 @@ struct wpa_ptk PTK; int ok = 0; const u8 *pmk = NULL; - unsigned int pmk_len; + 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; - pmk_len = PMK_LEN; +#ifdef CONFIG_IEEE80211R_AP + if (wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) { + os_memcpy(sm->xxkey, pmk, pmk_len); + sm->xxkey_len = pmk_len; + } +#endif /* CONFIG_IEEE80211R_AP */ } else { pmk = sm->PMK; pmk_len = sm->pmk_len; } - wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK); + if (wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK) < 0) + break; - if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, data, data_len) - == 0) { + if (wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK, + data, data_len) == 0) { + 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; } @@ -863,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; @@ -877,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)", @@ -950,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 { @@ -976,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, @@ -994,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, @@ -1004,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"); @@ -1092,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 && @@ -1119,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 || @@ -1204,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; } @@ -1239,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; @@ -1277,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) @@ -1292,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 && @@ -1335,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); @@ -1361,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; @@ -1372,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; } @@ -1412,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; @@ -1419,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; @@ -1453,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) @@ -1463,6 +1479,8 @@ } len += key_data_len; + if (!mic_len && encr) + len += AES_BLOCK_SIZE; hdr = os_zalloc(len); if (hdr == NULL) @@ -1471,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 ? @@ -1484,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, @@ -1510,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) { @@ -1530,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); @@ -1539,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); @@ -1555,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); @@ -1571,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"); @@ -1581,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 && @@ -1613,7 +1647,7 @@ { int timeout_ms; int pairwise = key_info & WPA_KEY_INFO_KEY_TYPE; - int ctr; + u32 ctr; if (sm == NULL) return; @@ -1627,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; @@ -1652,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; } @@ -1670,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); } @@ -1701,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: @@ -1734,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); @@ -1741,23 +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; @@ -1802,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); } @@ -1811,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); } @@ -1922,17 +1995,25 @@ size_t len = 2 * PMK_LEN; SM_ENTRY_MA(WPA_PTK, INITPMK, wpa_ptk); -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP sm->xxkey_len = 0; -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ if (sm->pmksa) { wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache"); os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len); sm->pmk_len = sm->pmksa->pmk_len; +#ifdef CONFIG_DPP + } else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP) { + wpa_printf(MSG_DEBUG, + "DPP: No PMKSA cache entry for STA - reject connection"); + sm->Disconnect = TRUE; + sm->disconnect_reason = WLAN_REASON_INVALID_PMKID; + return; +#endif /* CONFIG_DPP */ } else if (wpa_auth_get_msk(sm->wpa_auth, sm->addr, msk, &len) == 0) { unsigned int pmk_len; - if (sm->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) + if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) pmk_len = PMK_LEN_SUITE_B_192; else pmk_len = PMK_LEN; @@ -1948,15 +2029,20 @@ } os_memcpy(sm->PMK, msk, pmk_len); sm->pmk_len = pmk_len; -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (len >= 2 * PMK_LEN) { - os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN); - sm->xxkey_len = PMK_LEN; + if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) { + os_memcpy(sm->xxkey, msk, SHA384_MAC_LEN); + sm->xxkey_len = SHA384_MAC_LEN; + } else { + os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN); + sm->xxkey_len = PMK_LEN; + } } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ } else { wpa_printf(MSG_DEBUG, "WPA: Could not get PMK, get_msk: %p", - sm->wpa_auth->cb.get_msk); + sm->wpa_auth->cb->get_msk); sm->Disconnect = TRUE; return; } @@ -1978,16 +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); - sm->pmk_len = PMK_LEN; -#ifdef CONFIG_IEEE80211R + os_memcpy(sm->PMK, psk, psk_len); + sm->pmk_len = psk_len; +#ifdef CONFIG_IEEE80211R_AP os_memcpy(sm->xxkey, psk, PMK_LEN); sm->xxkey_len = PMK_LEN; -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ } +#ifdef CONFIG_SAE + if (wpa_auth_uses_sae(sm) && sm->pmksa) { + wpa_printf(MSG_DEBUG, "SAE: PMK from PMKSA cache"); + os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len); + sm->pmk_len = sm->pmksa->pmk_len; +#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; } @@ -2003,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; @@ -2012,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; @@ -2024,11 +2136,54 @@ 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 @@ -2036,7 +2191,10 @@ */ rsn_pmkid(sm->PMK, sm->pmk_len, sm->wpa_auth->addr, sm->addr, &pmkid[2 + RSN_SELECTOR_LEN], - wpa_key_mgmt_sha256(sm->wpa_key_mgmt)); + sm->wpa_key_mgmt); + wpa_hexdump(MSG_DEBUG, + "RSN: Message 1/4 PMKID derived from PMK", + &pmkid[2 + RSN_SELECTOR_LEN], PMKID_LEN); } } wpa_send_eapol(sm->wpa_auth, sm, @@ -2049,55 +2207,679 @@ 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; +#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; - unsigned int pmk_len; + size_t pmk_len; + int ft; + const u8 *eapol_key_ie, *key_data, *mic; + u16 key_data_length; + size_t mic_len, eapol_key_ie_len; + struct ieee802_1x_hdr *hdr; + struct wpa_eapol_key *key; + struct wpa_eapol_ie_parse kde; + 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; - pmk_len = PMK_LEN; +#ifdef CONFIG_IEEE80211R_AP + if (wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) { + os_memcpy(sm->xxkey, pmk, pmk_len); + sm->xxkey_len = pmk_len; + } +#endif /* CONFIG_IEEE80211R_AP */ } else { pmk = sm->PMK; pmk_len = sm->pmk_len; } - wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK); + if ((!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) { @@ -2108,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 @@ -2127,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); @@ -2186,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. @@ -2217,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]; @@ -2229,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; @@ -2259,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. @@ -2294,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; @@ -2314,7 +3238,7 @@ pos = kde; os_memcpy(pos, wpa_ie, wpa_ie_len); pos += wpa_ie_len; -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { int res; size_t elen; @@ -2330,7 +3254,7 @@ pos -= wpa_ie_len; pos += elen; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ if (gtk) { u8 hdr[2]; hdr[0] = keyidx & 0x03; @@ -2339,8 +3263,12 @@ 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; @@ -2352,7 +3280,10 @@ 2 + sm->assoc_resp_ftie[1]); res = 2 + sm->assoc_resp_ftie[1]; } else { - res = wpa_write_ftie(conf, conf->r0_key_holder, + int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt); + + res = wpa_write_ftie(conf, use_sha384, + conf->r0_key_holder, conf->r0_key_holder_len, NULL, NULL, pos, kde + kde_len - pos, @@ -2377,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]; @@ -2393,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); @@ -2410,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); } @@ -2449,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 */ } @@ -2495,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"); @@ -2512,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++; @@ -2526,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); @@ -2554,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); @@ -2594,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; @@ -2611,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. @@ -2622,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; @@ -2633,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; @@ -2640,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); } @@ -2651,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; @@ -2672,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); } @@ -2691,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); @@ -2794,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) { @@ -2873,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, @@ -3151,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, @@ -3255,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) @@ -3279,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) { @@ -3320,7 +4359,7 @@ sm->wpa_auth->conf.disable_pmksa_caching) return -1; - if (sm->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) { + if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) { if (pmk_len > PMK_LEN_SUITE_B_192) pmk_len = PMK_LEN_SUITE_B_192; } else if (pmk_len > PMK_LEN) { @@ -3372,6 +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) { @@ -3404,12 +4466,65 @@ } +#ifdef CONFIG_PMKSA_CACHE_EXTERNAL +#ifdef CONFIG_MESH + +int wpa_auth_pmksa_list_mesh(struct wpa_authenticator *wpa_auth, const u8 *addr, + char *buf, size_t len) +{ + if (!wpa_auth || !wpa_auth->pmksa) + return 0; + + return pmksa_cache_auth_list_mesh(wpa_auth->pmksa, addr, buf, len); +} + + struct rsn_pmksa_cache_entry * -wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr) +wpa_auth_pmksa_create_entry(const u8 *aa, const u8 *spa, const u8 *pmk, + const u8 *pmkid, int expiration) { + struct rsn_pmksa_cache_entry *entry; + struct os_reltime now; + + entry = pmksa_cache_auth_create_entry(pmk, PMK_LEN, pmkid, NULL, 0, aa, + spa, 0, NULL, WPA_KEY_MGMT_SAE); + if (!entry) + return NULL; + + os_get_reltime(&now); + entry->expiration = now.sec + expiration; + return entry; +} + + +int wpa_auth_pmksa_add_entry(struct wpa_authenticator *wpa_auth, + struct rsn_pmksa_cache_entry *entry) +{ + int ret; + if (!wpa_auth || !wpa_auth->pmksa) + return -1; + + ret = pmksa_cache_auth_add_entry(wpa_auth->pmksa, entry); + if (ret < 0) + wpa_printf(MSG_DEBUG, + "RSN: Failed to store external PMKSA cache for " + MACSTR, MAC2STR(entry->spa)); + + return ret; +} + +#endif /* CONFIG_MESH */ +#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ + + +struct rsn_pmksa_cache_entry * +wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr, + const u8 *pmkid) +{ + if (!wpa_auth || !wpa_auth->pmksa) return NULL; - return pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, NULL); + return pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, pmkid); } @@ -3662,6 +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 */ } @@ -3708,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,74 +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_NONCE_LEN 16 -#define FT_R0KH_R1KH_PULL_DATA_LEN (FT_R0KH_R1KH_PULL_NONCE_LEN + \ - WPA_PMK_NAME_LEN + FT_R1KH_ID_LEN + \ - ETH_ALEN) -#define FT_R0KH_R1KH_PULL_PAD_LEN ((8 - FT_R0KH_R1KH_PULL_DATA_LEN % 8) % 8) +/* 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[FT_R0KH_R1KH_PULL_PAD_LEN]; /* 8-octet boundary for AES block */ - u8 key_wrap_extra[8]; -} STRUCT_PACKED; +#define FT_RRB_NONCE_LEN 16 -#define FT_R0KH_R1KH_RESP_DATA_LEN (FT_R0KH_R1KH_PULL_NONCE_LEN + \ - FT_R1KH_ID_LEN + ETH_ALEN + PMK_LEN + \ - WPA_PMK_NAME_LEN + 2) -#define FT_R0KH_R1KH_RESP_PAD_LEN ((8 - FT_R0KH_R1KH_RESP_DATA_LEN % 8) % 8) -struct ft_r0kh_r1kh_resp_frame { - u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ - u8 packet_type; /* FT_PACKET_R0KH_R1KH_RESP */ - le16 data_length; /* little endian length of data (78) */ - u8 ap_address[ETH_ALEN]; +#define FT_RRB_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[FT_R0KH_R1KH_RESP_PAD_LEN]; /* 8-octet boundary for AES block */ - 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 */ -#define FT_R0KH_R1KH_PUSH_DATA_LEN (4 + FT_R1KH_ID_LEN + ETH_ALEN + \ - WPA_PMK_NAME_LEN + PMK_LEN + \ - WPA_PMK_NAME_LEN + 2) -#define FT_R0KH_R1KH_PUSH_PAD_LEN ((8 - FT_R0KH_R1KH_PUSH_DATA_LEN % 8) % 8) -struct ft_r0kh_r1kh_push_frame { - u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ - u8 packet_type; /* FT_PACKET_R0KH_R1KH_PUSH */ - le16 data_length; /* little endian length of data (82) */ - u8 ap_address[ETH_ALEN]; +#define FT_RRB_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[FT_R0KH_R1KH_PUSH_PAD_LEN]; /* 8-octet boundary for AES block */ - 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 */ @@ -116,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 { @@ -123,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; }; @@ -131,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; }; @@ -144,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; @@ -156,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]; @@ -164,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 @@ -184,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 { @@ -197,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); @@ -207,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); @@ -220,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 */ @@ -234,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, @@ -244,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); @@ -267,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_DRV_STA_REMOVED + WPA_REAUTH_EAPOL, WPA_ASSOC_FT, WPA_ASSOC_FILS, WPA_DRV_STA_REMOVED }; void wpa_remove_ptk(struct wpa_state_machine *sm); int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event); @@ -278,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 * @@ -297,13 +372,28 @@ struct eapol_state_machine *eapol); int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr, const u8 *pmk, const u8 *pmkid); +void wpa_auth_add_sae_pmkid(struct wpa_state_machine *sm, const u8 *pmkid); +int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr, + const u8 *pmk, size_t pmk_len, const u8 *pmkid, + int session_timeout, int akmp); void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth, const u8 *sta_addr); int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf, size_t len); void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth); +int wpa_auth_pmksa_list_mesh(struct wpa_authenticator *wpa_auth, const u8 *addr, + char *buf, size_t len); struct rsn_pmksa_cache_entry * -wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr); +wpa_auth_pmksa_create_entry(const u8 *aa, const u8 *spa, const u8 *pmk, + const u8 *pmkid, int expiration); +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, @@ -312,7 +402,7 @@ void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, int ack); -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, size_t max_len, int auth_alg, const u8 *req_ies, size_t req_ies_len); @@ -327,8 +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); @@ -347,5 +442,52 @@ 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,25 +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; - _ftie = (struct rsn_ftie *) (ftie + 2); + if (use_sha384) { + struct rsn_ftie_sha384 *_ftie = + (struct rsn_ftie_sha384 *) (ftie + 2); + + fte_mic = _ftie->mic; + elem_count = &_ftie->mic_control[1]; + } else { + struct rsn_ftie *_ftie = (struct rsn_ftie *) (ftie + 2); + + fte_mic = _ftie->mic; + elem_count = &_ftie->mic_control[1]; + } if (auth_alg == WLAN_AUTH_FT) - _ftie->mic_control[1] = 3; /* Information element count */ + *elem_count = 3; /* Information element count */ ric_start = pos; - if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse) == 0 && parse.ric) { + if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse, use_sha384) == 0 + && parse.ric) { pos = wpa_ft_process_ric(sm, pos, end, parse.ric, parse.ric_len); if (auth_alg == WLAN_AUTH_FT) - _ftie->mic_control[1] += + *elem_count += ieee802_11_ie_count(ric_start, pos - ric_start); } @@ -736,19 +2574,28 @@ if (ric_start == pos) ric_start = NULL; + if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) { + kck = sm->PTK.kck2; + kck_len = sm->PTK.kck2_len; + } else { + kck = sm->PTK.kck; + kck_len = sm->PTK.kck_len; + } if (auth_alg == WLAN_AUTH_FT && - wpa_ft_mic(sm->PTK.kck, sm->PTK.kck_len, sm->addr, - sm->wpa_auth->addr, 6, + wpa_ft_mic(kck, kck_len, sm->addr, sm->wpa_auth->addr, 6, mdie, mdie_len, ftie, ftie_len, rsnie, rsnie_len, ric_start, ric_start ? pos - ric_start : 0, - _ftie->mic) < 0) + fte_mic) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC"); + return NULL; + } os_free(sm->assoc_resp_ftie); sm->assoc_resp_ftie = os_malloc(ftie_len); - if (sm->assoc_resp_ftie) - os_memcpy(sm->assoc_resp_ftie, ftie, ftie_len); + if (!sm->assoc_resp_ftie) + return NULL; + os_memcpy(sm->assoc_resp_ftie, ftie, ftie_len); return pos; } @@ -759,10 +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,6 +9,8 @@ #include "utils/includes.h" #include "utils/common.h" +#include "utils/eloop.h" +#include "utils/list.h" #include "common/ieee802_11_defs.h" #include "common/sae.h" #include "common/wpa_ctrl.h" @@ -17,6 +19,7 @@ #include "eapol_auth/eapol_auth_sm_i.h" #include "eap_server/eap.h" #include "l2_packet/l2_packet.h" +#include "eth_p_oui.h" #include "hostapd.h" #include "ieee802_1x.h" #include "preauth_auth.h" @@ -24,6 +27,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" @@ -41,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; @@ -68,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) { @@ -107,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 */ } @@ -223,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) @@ -235,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. @@ -245,6 +299,9 @@ */ 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) { @@ -307,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); } @@ -401,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; @@ -414,33 +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 (!hapd->wpa_auth) - continue; - if (os_memcmp(hapd->own_addr, idata->dst, ETH_ALEN) == 0) { - wpa_printf(MSG_DEBUG, "FT: Send RRB data directly to " - "locally managed BSS " MACSTR "@%s -> " - MACSTR "@%s", - MAC2STR(idata->src_hapd->own_addr), - idata->src_hapd->conf->iface, - MAC2STR(hapd->own_addr), hapd->conf->iface); - wpa_ft_rrb_rx(hapd->wpa_auth, - idata->src_hapd->own_addr, - idata->data, idata->data_len); + + wpa_printf(MSG_DEBUG, + "FT: Send RRB data directly to locally managed BSS " + MACSTR "@%s -> " MACSTR "@%s", + MAC2STR(idata->src_hapd->own_addr), + idata->src_hapd->conf->iface, + MAC2STR(hapd->own_addr), hapd->conf->iface); + + /* Defer wpa_ft_rrb_rx() until next eloop step as this is + * when it would be triggered when reading from a socket. + * This avoids + * hapd0:send -> hapd1:recv -> hapd1:send -> hapd0:recv, + * that is calling hapd0:recv handler from within + * hapd0:send directly. + */ + data = os_zalloc(sizeof(*data) + idata->data_len); + if (!data) return 1; - } + + os_memcpy(data->addr, idata->src_hapd->own_addr, ETH_ALEN); + os_memcpy(data + 1, idata->data, idata->data_len); + data->data_len = idata->data_len; + + dl_list_add(&hapd->l2_queue, &data->list); + + if (!eloop_is_timeout_registered(hostapd_wpa_ft_rrb_rx_later, + hapd, NULL)) + eloop_register_timeout(0, 0, + hostapd_wpa_ft_rrb_rx_later, + hapd, NULL); + + return 1; } return 0; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto, @@ -465,7 +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; @@ -480,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, @@ -503,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) { @@ -541,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; @@ -547,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; @@ -563,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) { @@ -581,6 +1177,22 @@ } +static void hostapd_rrb_oui_receive(void *ctx, const u8 *src_addr, + const u8 *dst_addr, u8 oui_suffix, + const u8 *buf, size_t len) +{ + struct hostapd_data *hapd = ctx; + + wpa_printf(MSG_DEBUG, "FT: RRB received packet " MACSTR " -> " + MACSTR, MAC2STR(src_addr), MAC2STR(dst_addr)); + if (!is_multicast_ether_addr(dst_addr) && + os_memcmp(hapd->own_addr, dst_addr, ETH_ALEN) != 0) + return; + wpa_ft_rrb_oui_rx(hapd->wpa_auth, src_addr, dst_addr, oui_suffix, buf, + len); +} + + static int hostapd_wpa_auth_add_tspec(void *ctx, const u8 *sta_addr, u8 *tspec_ie, size_t tspec_ielen) { @@ -588,13 +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; @@ -603,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; @@ -649,12 +1326,14 @@ return -1; } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (!hostapd_drv_none(hapd) && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt)) { - hapd->l2 = l2_packet_init(hapd->conf->bridge[0] ? - hapd->conf->bridge : - hapd->conf->iface, NULL, ETH_P_RRB, + const char *ft_iface; + + ft_iface = hapd->conf->bridge[0] ? hapd->conf->bridge : + hapd->conf->iface; + hapd->l2 = l2_packet_init(ft_iface, NULL, ETH_P_RRB, hostapd_rrb_receive, hapd, 1); if (hapd->l2 == NULL && (hapd->driver == NULL || @@ -663,8 +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; @@ -702,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; @@ -62,6 +59,7 @@ u8 alt_replay_counter[WPA_REPLAY_COUNTER_LEN]; u8 PMK[PMK_LEN_MAX]; unsigned int pmk_len; + u8 pmkid[PMKID_LEN]; /* valid if pmkid_set == 1 */ struct wpa_ptk PTK; Boolean PTK_valid; Boolean pairwise_set; @@ -89,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; @@ -113,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 */ @@ -129,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; @@ -139,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 */ }; @@ -194,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; @@ -213,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, @@ -231,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,9 +293,13 @@ 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; @@ -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", @@ -723,6 +915,23 @@ os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmkid, PMKID_LEN); } +#ifdef CONFIG_SAE + if (sm->wpa_key_mgmt == WPA_KEY_MGMT_SAE && data.num_pmkid && + !sm->pmksa) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "No PMKSA cache entry found for SAE"); + return WPA_INVALID_PMKID; + } +#endif /* CONFIG_SAE */ + +#ifdef CONFIG_DPP + if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && !sm->pmksa) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "No PMKSA cache entry found for DPP"); + return WPA_INVALID_PMKID; + } +#endif /* CONFIG_DPP */ + if (sm->wpa_ie == NULL || sm->wpa_ie_len < wpa_ie_len) { os_free(sm->wpa_ie); sm->wpa_ie = os_malloc(wpa_ie_len); @@ -815,36 +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) { @@ -873,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; } @@ -908,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; @@ -915,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) @@ -938,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 @@ -354,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); @@ -401,6 +413,7 @@ char buf[1024]; int multi_bss; int wpa; + int pmf_changed = 0; if (hapd->wps == NULL) return 0; @@ -520,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="); @@ -528,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) { @@ -585,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=") || @@ -975,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); @@ -1064,7 +1103,9 @@ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) wps->auth_types |= WPS_AUTH_WPA2; - if (conf->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) { + if (conf->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | + WPA_CIPHER_CCMP_256 | + WPA_CIPHER_GCMP_256)) { wps->encr_types |= WPS_ENCR_AES; wps->encr_types_rsn |= WPS_ENCR_AES; } @@ -1131,6 +1172,31 @@ 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; @@ -1172,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; } --- 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. @@ -10,10 +10,12 @@ #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 { @@ -53,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 } }; @@ -59,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"); @@ -84,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; } @@ -193,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; @@ -201,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 @@ -113,11 +113,46 @@ } +static int ctrl_set_events(struct wpa_ctrl_dst *dst, const char *input) +{ + const char *value; + int val; + + if (!input) + return 0; + + value = os_strchr(input, '='); + if (!value) + return -1; + value++; + val = atoi(value); + if (val < 0 || val > 1) + return -1; + + if (str_starts(input, "probe_rx_events=")) { + if (val) + dst->events |= WPA_EVENT_RX_PROBE_REQUEST; + else + dst->events &= ~WPA_EVENT_RX_PROBE_REQUEST; + } + + return 0; +} + + int ctrl_iface_attach(struct dl_list *ctrl_dst, struct sockaddr_storage *from, - socklen_t fromlen) + socklen_t fromlen, const char *input) { struct wpa_ctrl_dst *dst; + /* Update event registration if already attached */ + dl_list_for_each(dst, ctrl_dst, struct wpa_ctrl_dst, list) { + if (!sockaddr_compare(from, fromlen, + &dst->addr, dst->addrlen)) + return ctrl_set_events(dst, input); + } + + /* New attachment */ dst = os_zalloc(sizeof(*dst)); if (dst == NULL) return -1; @@ -124,6 +159,7 @@ 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); --- contrib/wpa/src/common/ctrl_iface_common.h.orig +++ contrib/wpa/src/common/ctrl_iface_common.h @@ -11,6 +11,9 @@ #include "utils/list.h" +/* Events enable bits (wpa_ctrl_dst::events) */ +#define WPA_EVENT_RX_PROBE_REQUEST BIT(0) + /** * struct wpa_ctrl_dst - Data structure of control interface monitors * @@ -23,6 +26,7 @@ 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, @@ -29,7 +33,7 @@ socklen_t socklen); int ctrl_iface_attach(struct dl_list *ctrl_dst, struct sockaddr_storage *from, - socklen_t fromlen); + socklen_t fromlen, const char *input); int ctrl_iface_detach(struct dl_list *ctrl_dst, struct sockaddr_storage *from, socklen_t fromlen); int ctrl_iface_level(struct dl_list *ctrl_dst, struct sockaddr_storage *from, --- 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, @@ -341,4 +394,29 @@ 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