diff options
203 files changed, 7309 insertions, 5501 deletions
diff --git a/hostapd/Android.mk b/hostapd/Android.mk index bdc8706..57a894c 100644 --- a/hostapd/Android.mk +++ b/hostapd/Android.mk @@ -554,6 +554,9 @@ NEED_SHA512=y NEED_JSON=y NEED_GAS=y NEED_BASE64=y +ifdef CONFIG_DPP2 +L_CFLAGS += -DCONFIG_DPP2 +endif endif ifdef CONFIG_EAP_IKEV2 diff --git a/hostapd/ChangeLog b/hostapd/ChangeLog index f1366b4..327ee3b 100644 --- a/hostapd/ChangeLog +++ b/hostapd/ChangeLog @@ -1,5 +1,60 @@ 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 diff --git a/hostapd/Makefile b/hostapd/Makefile index dd3816e..6e263c5 100644 --- a/hostapd/Makefile +++ b/hostapd/Makefile @@ -588,6 +588,9 @@ NEED_SHA512=y NEED_JSON=y NEED_GAS=y NEED_BASE64=y +ifdef CONFIG_DPP2 +CFLAGS += -DCONFIG_DPP2 +endif endif ifdef CONFIG_EAP_IKEV2 diff --git a/hostapd/README-MULTI-AP b/hostapd/README-MULTI-AP new file mode 100644 index 0000000..ccee69e --- /dev/null +++ b/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) diff --git a/hostapd/config_file.c b/hostapd/config_file.c index aeec1d9..42f3b40 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -1380,10 +1380,26 @@ static int hostapd_config_vht_capab(struct hostapd_config *conf, #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 << ffs(mask))); + return (u8) (mask & (val << find_bit_offset(mask))); } + #endif /* CONFIG_IEEE80211AX */ @@ -2317,6 +2333,14 @@ static int parse_sae_password(struct hostapd_bss_config *bss, const char *val) 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) @@ -2501,6 +2525,16 @@ static int hostapd_config_fill(struct hostapd_config *conf, } 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) { @@ -3102,9 +3136,10 @@ static int hostapd_config_fill(struct hostapd_config *conf, * 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; } @@ -3588,6 +3623,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, } } 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 = @@ -3597,6 +3634,56 @@ static int hostapd_config_fill(struct hostapd_config *conf, 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); diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c index df2524a..e4b16e6 100644 --- a/hostapd/ctrl_iface.c +++ b/hostapd/ctrl_iface.c @@ -883,7 +883,7 @@ static int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd, /* 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"); @@ -2883,6 +2883,34 @@ static int hostapd_ctrl_iface_acl_add_mac(struct mac_acl_entry **acl, int *num, } +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, @@ -3242,7 +3270,7 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, reply_len = -1; } } else if (os_strncmp(buf, "DPP_BOOTSTRAP_GEN ", 18) == 0) { - res = hostapd_dpp_bootstrap_gen(hapd, buf + 18); + res = dpp_bootstrap_gen(hapd->iface->interfaces->dpp, buf + 18); if (res < 0) { reply_len = -1; } else { @@ -3251,12 +3279,14 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, reply_len = -1; } } else if (os_strncmp(buf, "DPP_BOOTSTRAP_REMOVE ", 21) == 0) { - if (hostapd_dpp_bootstrap_remove(hapd, buf + 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 = hostapd_dpp_bootstrap_get_uri(hapd, atoi(buf + 22)); + uri = dpp_bootstrap_get_uri(hapd->iface->interfaces->dpp, + atoi(buf + 22)); if (!uri) { reply_len = -1; } else { @@ -3265,8 +3295,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, reply_len = -1; } } else if (os_strncmp(buf, "DPP_BOOTSTRAP_INFO ", 19) == 0) { - reply_len = hostapd_dpp_bootstrap_info(hapd, atoi(buf + 19), - reply, reply_size); + 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; @@ -3277,7 +3308,8 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, hostapd_dpp_stop(hapd); hostapd_dpp_listen_stop(hapd); } else if (os_strncmp(buf, "DPP_CONFIGURATOR_ADD", 20) == 0) { - res = hostapd_dpp_configurator_add(hapd, buf + 20); + res = dpp_configurator_add(hapd->iface->interfaces->dpp, + buf + 20); if (res < 0) { reply_len = -1; } else { @@ -3286,15 +3318,17 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, reply_len = -1; } } else if (os_strncmp(buf, "DPP_CONFIGURATOR_REMOVE ", 24) == 0) { - if (hostapd_dpp_configurator_remove(hapd, buf + 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 = hostapd_dpp_configurator_get_key(hapd, - atoi(buf + 25), - reply, reply_size); + 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) { @@ -3313,6 +3347,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, 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; @@ -3793,7 +3830,7 @@ static void hostapd_ctrl_iface_flush(struct hapd_interfaces *interfaces) #endif /* CONFIG_TESTING_OPTIONS */ #ifdef CONFIG_DPP - hostapd_dpp_deinit_global(interfaces); + dpp_global_clear(interfaces->dpp); #endif /* CONFIG_DPP */ } diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index ab37f03..f8caa56 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -946,6 +946,32 @@ eap_server=0 # 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. @@ -1014,6 +1040,19 @@ eap_server=0 # 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 @@ -1174,6 +1213,8 @@ own_ip_addr=127.0.0.1 # 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. +# 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 @@ -1523,21 +1564,29 @@ own_ip_addr=127.0.0.1 # corresponds to the dot11RSNAConfigPasswordValueEntry. sae_password value # starts with the password (dot11RSNAConfigPasswordCredential). That value can # be followed by optional peer MAC address (dot11RSNAConfigPasswordPeerMac) and -# by optional password identifier (dot11RSNAConfigPasswordIdentifier). If the -# peer MAC address is not included or is set to the wildcard address +# 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. +# is allowed to use the entry. +# +# If the password identifier (with non-zero length) is included, the entry is +# limited to be used only with that specified identifier. + +# The last matching (based on peer MAC address and identifier) entry is used to +# select which password to use. Setting sae_password to an empty string has a +# special meaning of removing all previously added entries. +# # sae_password uses the following encoding: -#<password/credential>[|mac=<peer mac>][|id=<identifier>] +#<password/credential>[|mac=<peer mac>][|vlanid=<VLAN ID>][|id=<identifier>] # Examples: #sae_password=secret #sae_password=really secret|mac=ff:ff:ff:ff:ff:ff #sae_password=example secret|mac=02:03:04:05:06:07|id=pw identifier +#sae_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 @@ -1551,12 +1600,16 @@ own_ip_addr=127.0.0.1 # 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 -# used to specify a limited set of allowed groups. The group values are listed -# in the IANA registry: +# 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 -#sae_groups=19 20 21 25 26 +# 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 @@ -1915,6 +1968,14 @@ own_ip_addr=127.0.0.1 # 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 @@ -1923,6 +1984,15 @@ own_ip_addr=127.0.0.1 # 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 diff --git a/hostapd/hostapd.wpa_psk b/hostapd/hostapd.wpa_psk index 834d441..166e59e 100644 --- a/hostapd/hostapd.wpa_psk +++ b/hostapd/hostapd.wpa_psk @@ -5,8 +5,11 @@ # characters or as a 256-bit hex PSK (64 hex digits). # An optional key identifier can be added by prefixing the line with # keyid=<keyid_string> +# An optional VLAN ID can be specified by prefixing the line with +# vlanid=<VLAN ID>. 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 diff --git a/hostapd/main.c b/hostapd/main.c index bfc8acd..93d2dd3 100644 --- a/hostapd/main.c +++ b/hostapd/main.c @@ -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" @@ -671,7 +672,9 @@ int main(int argc, char *argv[]) dl_list_init(&interfaces.eth_p_oui); #endif /* CONFIG_ETH_P_OUI */ #ifdef CONFIG_DPP - hostapd_dpp_init_global(&interfaces); + interfaces.dpp = dpp_global_init(); + if (!interfaces.dpp) + return -1; #endif /* CONFIG_DPP */ for (;;) { @@ -901,7 +904,7 @@ int main(int argc, char *argv[]) os_free(interfaces.iface); #ifdef CONFIG_DPP - hostapd_dpp_deinit_global(&interfaces); + dpp_global_deinit(interfaces.dpp); #endif /* CONFIG_DPP */ if (interfaces.eloop_initialized) diff --git a/hs20/client/.gitignore b/hs20/client/.gitignore new file mode 100644 index 0000000..d2fd60f --- /dev/null +++ b/hs20/client/.gitignore @@ -0,0 +1 @@ +hs20-osu-client diff --git a/hs20/client/Makefile b/hs20/client/Makefile index fc9b619..67f6f55 100644 --- a/hs20/client/Makefile +++ b/hs20/client/Makefile @@ -8,12 +8,17 @@ ifndef LDO 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 diff --git a/hs20/client/osu_client.c b/hs20/client/osu_client.c index b48903d..1f594ce 100644 --- a/hs20/client/osu_client.c +++ b/hs20/client/osu_client.c @@ -612,8 +612,8 @@ int hs20_add_pps_mo(struct hs20_osu_client *ctx, const char *uri, } } - android_update_permission("SP", S_IRWXU | S_IRGRP | S_IXGRP); - android_update_permission(fname, S_IRWXU | S_IRGRP | S_IXGRP); + 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); diff --git a/hs20/server/www/terms.php b/hs20/server/www/terms.php index e269b3c..acba23e 100644 --- a/hs20/server/www/terms.php +++ b/hs20/server/www/terms.php @@ -2,6 +2,13 @@ require('config.php'); +function print_header() +{ + echo "<html>\n"; + echo "<head><title>HS 2.0 Terms and Conditions</title></head>\n"; + echo "<body>\n"; +} + $db = new PDO($osu_db); if (!$db) { die($sqliteerror); @@ -21,27 +28,21 @@ if (!$row) { die("No pending session for the specified MAC address"); } $identity = $row[0]; -?> -<html> -<head><title>HS 2.0 Terms and Conditions</title></head> -<body> - -<?php if (!$accept) { + print_header(); + echo "<p>Accept the following terms and conditions by clicking here: <a href=\"terms.php?addr=$addr&accept=yes\">Accept</a></p>\n<hr>\n"; readfile($t_c_file); } else { $res = $db->prepare("UPDATE users SET t_c_timestamp=? WHERE identity=?"); if (!$res->execute(array($t_c_timestamp, $identity))) { - echo "<p>Failed to update user account.</p>"; - } else { - $res = $db->prepare("DELETE FROM pending_tc WHERE mac_addr=?"); - $res->execute(array($addr)); - - echo "<p>Terms and conditions were accepted.</p>"; + die("Failed to update user account."); } + $res = $db->prepare("DELETE FROM pending_tc WHERE mac_addr=?"); + $res->execute(array($addr)); + $fp = fsockopen($hostapd_ctrl); if (!$fp) { die("Could not connect to hostapd(AS)"); @@ -69,8 +70,13 @@ if (!$accept) { sleep(1); } if ($ack) { + header('X-WFA-Hotspot20-Filtering: removed'); + print_header(); + echo "<p>Terms and conditions were accepted.</p>\n"; + echo "<P>Filtering disabled.</P>\n"; } else { + print_header(); echo "<P>Failed to disable filtering.</P>\n"; } } diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index 9611dc0..e640e99 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -200,8 +200,8 @@ struct hostapd_config * hostapd_config_defaults(void) 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->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; @@ -279,6 +279,8 @@ static int hostapd_config_read_wpa_psk(const char *fname, } while (fgets(buf, sizeof(buf), f)) { + int vlan_id = 0; + line++; if (buf[0] == '#') @@ -301,11 +303,15 @@ static int hostapd_config_read_wpa_psk(const char *fname, 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'", @@ -333,6 +339,7 @@ static int hostapd_config_read_wpa_psk(const char *fname, ret = -1; break; } + psk->vlan_id = vlan_id; if (is_zero_ether_addr(addr)) psk->group = 1; else @@ -588,6 +595,7 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) 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); @@ -637,6 +645,8 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) 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); @@ -858,11 +868,14 @@ const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id) 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", @@ -880,8 +893,11 @@ const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf, (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; diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index 6963df4..509677a 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -152,6 +152,7 @@ struct hostapd_wpa_psk { u8 psk[PMK_LEN]; u8 addr[ETH_ALEN]; u8 p2p_dev_addr[ETH_ALEN]; + int vlan_id; }; struct hostapd_eap_user { @@ -248,6 +249,7 @@ struct sae_password_entry { char *password; char *identifier; u8 peer_addr[ETH_ALEN]; + int vlan_id; }; /** @@ -390,6 +392,7 @@ struct hostapd_bss_config { 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; @@ -462,9 +465,11 @@ struct hostapd_bss_config { 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; @@ -872,7 +877,7 @@ int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries, 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); diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h index d45ab84..de40171 100644 --- a/src/ap/ap_drv_ops.h +++ b/src/ap/ap_drv_ops.h @@ -364,4 +364,14 @@ static inline int hostapd_drv_channel_info(struct hostapd_data *hapd, 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 */ diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c index 1bb3d9f..eced6c7 100644 --- a/src/ap/authsrv.c +++ b/src/ap/authsrv.c @@ -233,6 +233,7 @@ int authsrv_init(struct hostapd_data *hapd) 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"); diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c index 3128aed..c693715 100644 --- a/src/ap/ctrl_iface_ap.c +++ b/src/ap/ctrl_iface_ap.c @@ -1,6 +1,6 @@ /* * Control interface for shared AP commands - * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -451,11 +451,11 @@ static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype, 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; diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c index 149f389..75edbc9 100644 --- a/src/ap/dpp_hostapd.c +++ b/src/ap/dpp_hostapd.c @@ -28,34 +28,6 @@ static int hostapd_dpp_auth_init_next(struct hostapd_data *hapd); static const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; -static struct dpp_configurator * -hostapd_dpp_configurator_get_id(struct hostapd_data *hapd, unsigned int id) -{ - struct dpp_configurator *conf; - - dl_list_for_each(conf, &hapd->iface->interfaces->dpp_configurator, - struct dpp_configurator, list) { - if (conf->id == id) - return conf; - } - return NULL; -} - - -static unsigned int hapd_dpp_next_id(struct hostapd_data *hapd) -{ - struct dpp_bootstrap_info *bi; - unsigned int max_id = 0; - - dl_list_for_each(bi, &hapd->iface->interfaces->dpp_bootstrap, - struct dpp_bootstrap_info, list) { - if (bi->id > max_id) - max_id = bi->id; - } - return max_id + 1; -} - - /** * hostapd_dpp_qr_code - Parse and add DPP bootstrapping info from a QR Code * @hapd: Pointer to hostapd_data @@ -67,13 +39,10 @@ int hostapd_dpp_qr_code(struct hostapd_data *hapd, const char *cmd) struct dpp_bootstrap_info *bi; struct dpp_authentication *auth = hapd->dpp_auth; - bi = dpp_parse_qr_code(cmd); + bi = dpp_add_qr_code(hapd->iface->interfaces->dpp, cmd); if (!bi) return -1; - bi->id = hapd_dpp_next_id(hapd); - dl_list_add(&hapd->iface->interfaces->dpp_bootstrap, &bi->list); - if (auth && auth->response_pending && dpp_notify_new_qr_code(auth, bi) == 1) { wpa_printf(MSG_DEBUG, @@ -92,195 +61,6 @@ int hostapd_dpp_qr_code(struct hostapd_data *hapd, const char *cmd) } -static char * get_param(const char *cmd, const char *param) -{ - const char *pos, *end; - char *val; - size_t len; - - pos = os_strstr(cmd, param); - if (!pos) - return NULL; - - pos += os_strlen(param); - end = os_strchr(pos, ' '); - if (end) - len = end - pos; - else - len = os_strlen(pos); - val = os_malloc(len + 1); - if (!val) - return NULL; - os_memcpy(val, pos, len); - val[len] = '\0'; - return val; -} - - -int hostapd_dpp_bootstrap_gen(struct hostapd_data *hapd, const char *cmd) -{ - char *chan = NULL, *mac = NULL, *info = NULL, *pk = NULL, *curve = NULL; - char *key = NULL; - u8 *privkey = NULL; - size_t privkey_len = 0; - size_t len; - int ret = -1; - struct dpp_bootstrap_info *bi; - - bi = os_zalloc(sizeof(*bi)); - if (!bi) - goto fail; - - if (os_strstr(cmd, "type=qrcode")) - bi->type = DPP_BOOTSTRAP_QR_CODE; - else if (os_strstr(cmd, "type=pkex")) - bi->type = DPP_BOOTSTRAP_PKEX; - else - goto fail; - - chan = get_param(cmd, " chan="); - mac = get_param(cmd, " mac="); - info = get_param(cmd, " info="); - curve = get_param(cmd, " curve="); - key = get_param(cmd, " key="); - - if (key) { - privkey_len = os_strlen(key) / 2; - privkey = os_malloc(privkey_len); - if (!privkey || - hexstr2bin(key, privkey, privkey_len) < 0) - goto fail; - } - - pk = dpp_keygen(bi, curve, privkey, privkey_len); - if (!pk) - goto fail; - - len = 4; /* "DPP:" */ - if (chan) { - if (dpp_parse_uri_chan_list(bi, chan) < 0) - goto fail; - len += 3 + os_strlen(chan); /* C:...; */ - } - if (mac) { - if (dpp_parse_uri_mac(bi, mac) < 0) - goto fail; - len += 3 + os_strlen(mac); /* M:...; */ - } - if (info) { - if (dpp_parse_uri_info(bi, info) < 0) - goto fail; - len += 3 + os_strlen(info); /* I:...; */ - } - len += 4 + os_strlen(pk); - bi->uri = os_malloc(len + 1); - if (!bi->uri) - goto fail; - os_snprintf(bi->uri, len + 1, "DPP:%s%s%s%s%s%s%s%s%sK:%s;;", - chan ? "C:" : "", chan ? chan : "", chan ? ";" : "", - mac ? "M:" : "", mac ? mac : "", mac ? ";" : "", - info ? "I:" : "", info ? info : "", info ? ";" : "", - pk); - bi->id = hapd_dpp_next_id(hapd); - dl_list_add(&hapd->iface->interfaces->dpp_bootstrap, &bi->list); - ret = bi->id; - bi = NULL; -fail: - os_free(curve); - os_free(pk); - os_free(chan); - os_free(mac); - os_free(info); - str_clear_free(key); - bin_clear_free(privkey, privkey_len); - dpp_bootstrap_info_free(bi); - return ret; -} - - -static struct dpp_bootstrap_info * -dpp_bootstrap_get_id(struct hostapd_data *hapd, unsigned int id) -{ - struct dpp_bootstrap_info *bi; - - dl_list_for_each(bi, &hapd->iface->interfaces->dpp_bootstrap, - struct dpp_bootstrap_info, list) { - if (bi->id == id) - return bi; - } - return NULL; -} - - -static int dpp_bootstrap_del(struct hapd_interfaces *ifaces, unsigned int id) -{ - struct dpp_bootstrap_info *bi, *tmp; - int found = 0; - - dl_list_for_each_safe(bi, tmp, &ifaces->dpp_bootstrap, - struct dpp_bootstrap_info, list) { - if (id && bi->id != id) - continue; - found = 1; - dl_list_del(&bi->list); - dpp_bootstrap_info_free(bi); - } - - if (id == 0) - return 0; /* flush succeeds regardless of entries found */ - return found ? 0 : -1; -} - - -int hostapd_dpp_bootstrap_remove(struct hostapd_data *hapd, const char *id) -{ - unsigned int id_val; - - if (os_strcmp(id, "*") == 0) { - id_val = 0; - } else { - id_val = atoi(id); - if (id_val == 0) - return -1; - } - - return dpp_bootstrap_del(hapd->iface->interfaces, id_val); -} - - -const char * hostapd_dpp_bootstrap_get_uri(struct hostapd_data *hapd, - unsigned int id) -{ - struct dpp_bootstrap_info *bi; - - bi = dpp_bootstrap_get_id(hapd, id); - if (!bi) - return NULL; - return bi->uri; -} - - -int hostapd_dpp_bootstrap_info(struct hostapd_data *hapd, int id, - char *reply, int reply_size) -{ - struct dpp_bootstrap_info *bi; - - bi = dpp_bootstrap_get_id(hapd, id); - if (!bi) - return -1; - return os_snprintf(reply, reply_size, "type=%s\n" - "mac_addr=" MACSTR "\n" - "info=%s\n" - "num_freq=%u\n" - "curve=%s\n", - dpp_bootstrap_type_txt(bi->type), - MAC2STR(bi->mac_addr), - bi->info ? bi->info : "", - bi->num_freq, - bi->curve->name); -} - - static void hostapd_dpp_auth_resp_retry_timeout(void *eloop_ctx, void *timeout_ctx) { @@ -354,6 +134,16 @@ void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst, 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"); @@ -505,178 +295,6 @@ static void hostapd_dpp_set_testing_options(struct hostapd_data *hapd, } -static int hostapd_dpp_set_configurator(struct hostapd_data *hapd, - struct dpp_authentication *auth, - const char *cmd) -{ - const char *pos, *end; - struct dpp_configuration *conf_sta = NULL, *conf_ap = NULL; - struct dpp_configurator *conf = NULL; - u8 ssid[32] = { "test" }; - size_t ssid_len = 4; - char pass[64] = { }; - size_t pass_len = 0; - u8 psk[PMK_LEN]; - int psk_set = 0; - char *group_id = NULL; - - if (!cmd) - return 0; - - wpa_printf(MSG_DEBUG, "DPP: Set configurator parameters: %s", cmd); - pos = os_strstr(cmd, " ssid="); - if (pos) { - pos += 6; - end = os_strchr(pos, ' '); - ssid_len = end ? (size_t) (end - pos) : os_strlen(pos); - ssid_len /= 2; - if (ssid_len > sizeof(ssid) || - hexstr2bin(pos, ssid, ssid_len) < 0) - goto fail; - } - - pos = os_strstr(cmd, " pass="); - if (pos) { - pos += 6; - end = os_strchr(pos, ' '); - pass_len = end ? (size_t) (end - pos) : os_strlen(pos); - pass_len /= 2; - if (pass_len > sizeof(pass) - 1 || pass_len < 8 || - hexstr2bin(pos, (u8 *) pass, pass_len) < 0) - goto fail; - } - - pos = os_strstr(cmd, " psk="); - if (pos) { - pos += 5; - if (hexstr2bin(pos, psk, PMK_LEN) < 0) - goto fail; - psk_set = 1; - } - - pos = os_strstr(cmd, " group_id="); - if (pos) { - size_t group_id_len; - - pos += 10; - end = os_strchr(pos, ' '); - group_id_len = end ? (size_t) (end - pos) : os_strlen(pos); - group_id = os_malloc(group_id_len + 1); - if (!group_id) - goto fail; - os_memcpy(group_id, pos, group_id_len); - group_id[group_id_len] = '\0'; - } - - if (os_strstr(cmd, " conf=sta-")) { - conf_sta = os_zalloc(sizeof(struct dpp_configuration)); - if (!conf_sta) - goto fail; - os_memcpy(conf_sta->ssid, ssid, ssid_len); - conf_sta->ssid_len = ssid_len; - if (os_strstr(cmd, " conf=sta-psk") || - os_strstr(cmd, " conf=sta-sae") || - os_strstr(cmd, " conf=sta-psk-sae")) { - if (os_strstr(cmd, " conf=sta-psk-sae")) - conf_sta->akm = DPP_AKM_PSK_SAE; - else if (os_strstr(cmd, " conf=sta-sae")) - conf_sta->akm = DPP_AKM_SAE; - else - conf_sta->akm = DPP_AKM_PSK; - if (psk_set) { - os_memcpy(conf_sta->psk, psk, PMK_LEN); - } else { - conf_sta->passphrase = os_strdup(pass); - if (!conf_sta->passphrase) - goto fail; - } - } else if (os_strstr(cmd, " conf=sta-dpp")) { - conf_sta->akm = DPP_AKM_DPP; - } else { - goto fail; - } - if (os_strstr(cmd, " group_id=")) { - conf_sta->group_id = group_id; - group_id = NULL; - } - } - - if (os_strstr(cmd, " conf=ap-")) { - conf_ap = os_zalloc(sizeof(struct dpp_configuration)); - if (!conf_ap) - goto fail; - os_memcpy(conf_ap->ssid, ssid, ssid_len); - conf_ap->ssid_len = ssid_len; - if (os_strstr(cmd, " conf=ap-psk") || - os_strstr(cmd, " conf=ap-sae") || - os_strstr(cmd, " conf=ap-psk-sae")) { - if (os_strstr(cmd, " conf=ap-psk-sae")) - conf_ap->akm = DPP_AKM_PSK_SAE; - else if (os_strstr(cmd, " conf=ap-sae")) - conf_ap->akm = DPP_AKM_SAE; - else - conf_ap->akm = DPP_AKM_PSK; - if (psk_set) { - os_memcpy(conf_ap->psk, psk, PMK_LEN); - } else if (pass_len > 0) { - conf_ap->passphrase = os_strdup(pass); - if (!conf_ap->passphrase) - goto fail; - } else { - goto fail; - } - } else if (os_strstr(cmd, " conf=ap-dpp")) { - conf_ap->akm = DPP_AKM_DPP; - } else { - goto fail; - } - if (os_strstr(cmd, " group_id=")) { - conf_ap->group_id = group_id; - group_id = NULL; - } - } - - pos = os_strstr(cmd, " expiry="); - if (pos) { - long int val; - - pos += 8; - val = strtol(pos, NULL, 0); - if (val <= 0) - goto fail; - if (conf_sta) - conf_sta->netaccesskey_expiry = val; - if (conf_ap) - conf_ap->netaccesskey_expiry = val; - } - - pos = os_strstr(cmd, " configurator="); - if (pos) { - auth->configurator = 1; - pos += 14; - conf = hostapd_dpp_configurator_get_id(hapd, atoi(pos)); - if (!conf) { - wpa_printf(MSG_INFO, - "DPP: Could not find the specified configurator"); - goto fail; - } - } - auth->conf_sta = conf_sta; - auth->conf_ap = conf_ap; - auth->conf = conf; - os_free(group_id); - return 0; - -fail: - wpa_msg(hapd->msg_ctx, MSG_INFO, - "DPP: Failed to set configurator parameters"); - dpp_configuration_free(conf_sta); - dpp_configuration_free(conf_ap); - os_free(group_id); - return -1; -} - - static void hostapd_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx) { struct hostapd_data *hapd = eloop_ctx; @@ -786,7 +404,7 @@ int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd) if (!pos) return -1; pos += 6; - peer_bi = dpp_bootstrap_get_id(hapd, atoi(pos)); + 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"); @@ -796,7 +414,8 @@ int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd) pos = os_strstr(cmd, " own="); if (pos) { pos += 5; - own_bi = dpp_bootstrap_get_id(hapd, atoi(pos)); + 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"); @@ -846,7 +465,8 @@ int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd) if (!hapd->dpp_auth) goto fail; hostapd_dpp_set_testing_options(hapd, hapd->dpp_auth); - if (hostapd_dpp_set_configurator(hapd, hapd->dpp_auth, cmd) < 0) { + 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; @@ -905,7 +525,10 @@ static void hostapd_dpp_rx_auth_req(struct hostapd_data *hapd, const u8 *src, { const u8 *r_bootstrap, *i_bootstrap; u16 r_bootstrap_len, i_bootstrap_len; - struct dpp_bootstrap_info *bi, *own_bi = NULL, *peer_bi = NULL; + 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)); @@ -932,28 +555,8 @@ static void hostapd_dpp_rx_auth_req(struct hostapd_data *hapd, const u8 *src, /* Try to find own and peer bootstrapping key matches based on the * received hash values */ - dl_list_for_each(bi, &hapd->iface->interfaces->dpp_bootstrap, - struct dpp_bootstrap_info, list) { - if (!own_bi && bi->own && - os_memcmp(bi->pubkey_hash, r_bootstrap, - SHA256_MAC_LEN) == 0) { - wpa_printf(MSG_DEBUG, - "DPP: Found matching own bootstrapping information"); - own_bi = bi; - } - - if (!peer_bi && !bi->own && - os_memcmp(bi->pubkey_hash, i_bootstrap, - SHA256_MAC_LEN) == 0) { - wpa_printf(MSG_DEBUG, - "DPP: Found matching peer bootstrapping information"); - peer_bi = bi; - } - - if (own_bi && peer_bi) - break; - } - + 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"); @@ -975,8 +578,9 @@ static void hostapd_dpp_rx_auth_req(struct hostapd_data *hapd, const u8 *src, return; } hostapd_dpp_set_testing_options(hapd, hapd->dpp_auth); - if (hostapd_dpp_set_configurator(hapd, hapd->dpp_auth, - hapd->dpp_configurator_params) < 0) { + 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; @@ -1072,6 +676,7 @@ static void hostapd_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token, 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"); @@ -1107,12 +712,41 @@ static void hostapd_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token, } hostapd_dpp_handle_config_obj(hapd, auth); - dpp_auth_deinit(hapd->dpp_auth); - hapd->dpp_auth = NULL; - return; - + 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: - wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED); + 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; } @@ -1121,7 +755,7 @@ fail: static void hostapd_dpp_start_gas_client(struct hostapd_data *hapd) { struct dpp_authentication *auth = hapd->dpp_auth; - struct wpabuf *buf, *conf_req; + struct wpabuf *buf; char json[100]; int res; int netrole_ap = 1; @@ -1133,34 +767,13 @@ static void hostapd_dpp_start_gas_client(struct hostapd_data *hapd) netrole_ap ? "ap" : "sta"); wpa_printf(MSG_DEBUG, "DPP: GAS Config Attributes: %s", json); - conf_req = dpp_build_conf_req(auth, json); - if (!conf_req) { + buf = dpp_build_conf_req(auth, json); + if (!buf) { wpa_printf(MSG_DEBUG, "DPP: No configuration request data available"); return; } - buf = gas_build_initial_req(0, 10 + 2 + wpabuf_len(conf_req)); - if (!buf) { - wpabuf_free(conf_req); - return; - } - - /* Advertisement Protocol IE */ - wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO); - wpabuf_put_u8(buf, 8); /* Length */ - wpabuf_put_u8(buf, 0x7f); - wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); - wpabuf_put_u8(buf, 5); - wpabuf_put_be24(buf, OUI_WFA); - wpabuf_put_u8(buf, DPP_OUI_TYPE); - wpabuf_put_u8(buf, 0x01); - - /* GAS Query */ - wpabuf_put_le16(buf, wpabuf_len(conf_req)); - wpabuf_put_buf(buf, conf_req); - wpabuf_free(conf_req); - wpa_printf(MSG_DEBUG, "DPP: GAS request to " MACSTR " (freq %u MHz)", MAC2STR(auth->peer_mac_addr), auth->curr_freq); @@ -1281,6 +894,63 @@ static void hostapd_dpp_rx_auth_conf(struct hostapd_data *hapd, const u8 *src, } +#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, @@ -1596,24 +1266,10 @@ hostapd_dpp_rx_pkex_commit_reveal_req(struct hostapd_data *hapd, const u8 *src, wpabuf_head(msg), wpabuf_len(msg)); wpabuf_free(msg); - bi = os_zalloc(sizeof(*bi)); + bi = dpp_pkex_finish(hapd->iface->interfaces->dpp, pkex, src, freq); if (!bi) return; - bi->id = hapd_dpp_next_id(hapd); - bi->type = DPP_BOOTSTRAP_PKEX; - os_memcpy(bi->mac_addr, src, ETH_ALEN); - bi->num_freq = 1; - bi->freq[0] = freq; - bi->curve = pkex->own_bi->curve; - bi->pubkey = pkex->peer_bootstrap_key; - pkex->peer_bootstrap_key = NULL; - dpp_pkex_free(pkex); hapd->dpp_pkex = NULL; - if (dpp_bootstrap_key_hash(bi) < 0) { - dpp_bootstrap_info_free(bi); - return; - } - dl_list_add(&hapd->iface->interfaces->dpp_bootstrap, &bi->list); } @@ -1623,7 +1279,7 @@ hostapd_dpp_rx_pkex_commit_reveal_resp(struct hostapd_data *hapd, const u8 *src, unsigned int freq) { int res; - struct dpp_bootstrap_info *bi, *own_bi; + struct dpp_bootstrap_info *bi; struct dpp_pkex *pkex = hapd->dpp_pkex; char cmd[500]; @@ -1641,26 +1297,10 @@ hostapd_dpp_rx_pkex_commit_reveal_resp(struct hostapd_data *hapd, const u8 *src, return; } - own_bi = pkex->own_bi; - - bi = os_zalloc(sizeof(*bi)); + bi = dpp_pkex_finish(hapd->iface->interfaces->dpp, pkex, src, freq); if (!bi) return; - bi->id = hapd_dpp_next_id(hapd); - bi->type = DPP_BOOTSTRAP_PKEX; - os_memcpy(bi->mac_addr, src, ETH_ALEN); - bi->num_freq = 1; - bi->freq[0] = freq; - bi->curve = own_bi->curve; - bi->pubkey = pkex->peer_bootstrap_key; - pkex->peer_bootstrap_key = NULL; - dpp_pkex_free(pkex); hapd->dpp_pkex = NULL; - if (dpp_bootstrap_key_hash(bi) < 0) { - dpp_bootstrap_info_free(bi); - return; - } - dl_list_add(&hapd->iface->interfaces->dpp_bootstrap, &bi->list); os_snprintf(cmd, sizeof(cmd), " peer=%u %s", bi->id, @@ -1744,6 +1384,11 @@ void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src, 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); @@ -1790,11 +1435,28 @@ hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa, void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok) { - if (!hapd->dpp_auth) + 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) @@ -1806,93 +1468,6 @@ void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok) } -static unsigned int hostapd_dpp_next_configurator_id(struct hostapd_data *hapd) -{ - struct dpp_configurator *conf; - unsigned int max_id = 0; - - dl_list_for_each(conf, &hapd->iface->interfaces->dpp_configurator, - struct dpp_configurator, list) { - if (conf->id > max_id) - max_id = conf->id; - } - return max_id + 1; -} - - -int hostapd_dpp_configurator_add(struct hostapd_data *hapd, const char *cmd) -{ - char *curve = NULL; - char *key = NULL; - u8 *privkey = NULL; - size_t privkey_len = 0; - int ret = -1; - struct dpp_configurator *conf = NULL; - - curve = get_param(cmd, " curve="); - key = get_param(cmd, " key="); - - if (key) { - privkey_len = os_strlen(key) / 2; - privkey = os_malloc(privkey_len); - if (!privkey || - hexstr2bin(key, privkey, privkey_len) < 0) - goto fail; - } - - conf = dpp_keygen_configurator(curve, privkey, privkey_len); - if (!conf) - goto fail; - - conf->id = hostapd_dpp_next_configurator_id(hapd); - dl_list_add(&hapd->iface->interfaces->dpp_configurator, &conf->list); - ret = conf->id; - conf = NULL; -fail: - os_free(curve); - str_clear_free(key); - bin_clear_free(privkey, privkey_len); - dpp_configurator_free(conf); - return ret; -} - - -static int dpp_configurator_del(struct hapd_interfaces *ifaces, unsigned int id) -{ - struct dpp_configurator *conf, *tmp; - int found = 0; - - dl_list_for_each_safe(conf, tmp, &ifaces->dpp_configurator, - struct dpp_configurator, list) { - if (id && conf->id != id) - continue; - found = 1; - dl_list_del(&conf->list); - dpp_configurator_free(conf); - } - - if (id == 0) - return 0; /* flush succeeds regardless of entries found */ - return found ? 0 : -1; -} - - -int hostapd_dpp_configurator_remove(struct hostapd_data *hapd, const char *id) -{ - unsigned int id_val; - - if (os_strcmp(id, "*") == 0) { - id_val = 0; - } else { - id_val = atoi(id); - if (id_val == 0) - return -1; - } - - return dpp_configurator_del(hapd->iface->interfaces, id_val); -} - - int hostapd_dpp_configurator_sign(struct hostapd_data *hapd, const char *cmd) { struct dpp_authentication *auth; @@ -1905,7 +1480,8 @@ int hostapd_dpp_configurator_sign(struct hostapd_data *hapd, const char *cmd) curve = get_param(cmd, " curve="); hostapd_dpp_set_testing_options(hapd, auth); - if (hostapd_dpp_set_configurator(hapd, auth, cmd) == 0 && + 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; @@ -1918,19 +1494,6 @@ 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) -{ - struct dpp_configurator *conf; - - conf = hostapd_dpp_configurator_get_id(hapd, id); - if (!conf) - return -1; - - return dpp_configurator_get_key(conf, buf, buflen); -} - - int hostapd_dpp_pkex_add(struct hostapd_data *hapd, const char *cmd) { struct dpp_bootstrap_info *own_bi; @@ -1940,7 +1503,7 @@ int hostapd_dpp_pkex_add(struct hostapd_data *hapd, const char *cmd) if (!pos) return -1; pos += 5; - own_bi = dpp_bootstrap_get_id(hapd, atoi(pos)); + 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"); @@ -2070,6 +1633,10 @@ void hostapd_dpp_deinit(struct hostapd_data *hapd) 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, "*"); @@ -2077,20 +1644,3 @@ void hostapd_dpp_deinit(struct hostapd_data *hapd) os_free(hapd->dpp_configurator_params); hapd->dpp_configurator_params = NULL; } - - -void hostapd_dpp_init_global(struct hapd_interfaces *ifaces) -{ - dl_list_init(&ifaces->dpp_bootstrap); - dl_list_init(&ifaces->dpp_configurator); - ifaces->dpp_init_done = 1; -} - - -void hostapd_dpp_deinit_global(struct hapd_interfaces *ifaces) -{ - if (!ifaces->dpp_init_done) - return; - dpp_bootstrap_del(ifaces, 0); - dpp_configurator_del(ifaces, 0); -} diff --git a/src/ap/dpp_hostapd.h b/src/ap/dpp_hostapd.h index 3ef7c14..449ca16 100644 --- a/src/ap/dpp_hostapd.h +++ b/src/ap/dpp_hostapd.h @@ -10,12 +10,6 @@ #define DPP_HOSTAPD_H int hostapd_dpp_qr_code(struct hostapd_data *hapd, const char *cmd); -int hostapd_dpp_bootstrap_gen(struct hostapd_data *hapd, const char *cmd); -int hostapd_dpp_bootstrap_remove(struct hostapd_data *hapd, const char *id); -const char * hostapd_dpp_bootstrap_get_uri(struct hostapd_data *hapd, - unsigned int id); -int hostapd_dpp_bootstrap_info(struct hostapd_data *hapd, int id, - char *reply, int reply_size); int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd); int hostapd_dpp_listen(struct hostapd_data *hapd, const char *cmd); void hostapd_dpp_listen_stop(struct hostapd_data *hapd); diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c index 54be3b5..8ddf754 100644 --- a/src/ap/drv_callbacks.c +++ b/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" @@ -305,6 +306,7 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, return -1; } res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, + hapd->iface->freq, ie, ielen, elems.mdie, elems.mdie_len, elems.owe_dh, elems.owe_dh_len); @@ -564,6 +566,38 @@ skip_wpa_check: } #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); @@ -1072,6 +1106,7 @@ fail: } +#ifndef NEED_AP_MLME static void hostapd_action_rx(struct hostapd_data *hapd, struct rx_mgmt *drv_mgmt) { @@ -1084,7 +1119,7 @@ static void hostapd_action_rx(struct hostapd_data *hapd, if (drv_mgmt->frame_len < IEEE80211_HDRLEN + 2 + 1) return; - plen = drv_mgmt->frame_len - IEEE80211_HDRLEN - 1; + plen = drv_mgmt->frame_len - IEEE80211_HDRLEN; mgmt = (struct ieee80211_mgmt *) drv_mgmt->frame; fc = le_to_host16(mgmt->frame_control); @@ -1104,19 +1139,20 @@ static void hostapd_action_rx(struct hostapd_data *hapd, } #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_AP */ #ifdef CONFIG_IEEE80211W - if (mgmt->u.action.category == WLAN_ACTION_SA_QUERY && plen >= 4) { + 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_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_AP */ #ifdef CONFIG_FST @@ -1126,7 +1162,7 @@ static void hostapd_action_rx(struct hostapd_data *hapd, } #endif /* CONFIG_FST */ #ifdef CONFIG_DPP - if (plen >= 1 + 4 && + 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) == @@ -1143,6 +1179,7 @@ static void hostapd_action_rx(struct hostapd_data *hapd, } #endif /* CONFIG_DPP */ } +#endif /* NEED_AP_MLME */ #ifdef NEED_AP_MLME @@ -1604,10 +1641,10 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, if (!data->rx_mgmt.frame) break; #ifdef NEED_AP_MLME - if (hostapd_mgmt_rx(hapd, &data->rx_mgmt) > 0) - break; -#endif /* NEED_AP_MLME */ + hostapd_mgmt_rx(hapd, &data->rx_mgmt); +#else /* NEED_AP_MLME */ hostapd_action_rx(hapd, &data->rx_mgmt); +#endif /* NEED_AP_MLME */ break; case EVENT_RX_PROBE_REQ: if (data->rx_probe_req.sa == NULL || diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index 342585f..20c8e8f 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -1,6 +1,6 @@ /* * hostapd / Initialization and configuration - * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -348,10 +348,11 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd) 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); @@ -417,6 +418,20 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd) 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 */ } @@ -431,7 +446,7 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd) 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) { wpa_msg(hapd->msg_ctx, MSG_INFO, WPA_EVENT_TERMINATING); @@ -506,7 +521,7 @@ static void hostapd_cleanup_iface(struct hostapd_iface *iface) 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); } @@ -658,8 +673,10 @@ static int hostapd_validate_bssid_configuration(struct hostapd_iface *iface) 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)", @@ -1867,15 +1884,17 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface, } } - 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; @@ -1888,11 +1907,14 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface, 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)) @@ -2145,6 +2167,9 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface, 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; } @@ -2155,7 +2180,7 @@ static void hostapd_bss_deinit(struct hostapd_data *hapd) if (!hapd) return; wpa_printf(MSG_DEBUG, "%s: deinit bss %s", __func__, - hapd->conf->iface); + hapd->conf ? hapd->conf->iface : "N/A"); hostapd_bss_deinit_no_free(hapd); wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED); hostapd_cleanup(hapd); @@ -2182,7 +2207,7 @@ void hostapd_interface_deinit(struct hostapd_iface *iface) } #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]); diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index d304c11..790d377 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -66,9 +66,7 @@ struct hapd_interfaces { int eloop_initialized; #ifdef CONFIG_DPP - int dpp_init_done; - struct dl_list dpp_bootstrap; /* struct dpp_bootstrap_info */ - struct dl_list dpp_configurator; /* struct dpp_configurator */ + struct dpp_global *dpp; #endif /* CONFIG_DPP */ }; @@ -129,6 +127,13 @@ struct hostapd_neighbor_entry { 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 */ @@ -307,7 +312,10 @@ struct hostapd_data { /** 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 diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index c6138e1..fde19b5 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -21,6 +21,7 @@ #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" @@ -62,6 +63,9 @@ prepare_auth_resp_fils(struct hostapd_data *hapd, 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) @@ -420,6 +424,15 @@ static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd, return NULL; } + 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) @@ -509,22 +522,58 @@ static int use_sae_anti_clogging(struct hostapd_data *hapd) 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; } @@ -535,16 +584,25 @@ static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd, 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; 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); @@ -553,9 +611,25 @@ static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd, 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; } @@ -627,8 +701,52 @@ static void sae_set_retransmit_timer(struct hostapd_data *hapd, } +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); @@ -636,14 +754,18 @@ void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta) 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; @@ -653,7 +775,8 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, 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; sae_set_state(sta, SAE_COMMITTED, "Sent Commit"); @@ -742,7 +865,8 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, * 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: @@ -775,8 +899,9 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, wpa_printf(MSG_DEBUG, "SAE: remove the STA (" MACSTR ") doing reauthentication", MAC2STR(sta->addr)); - ap_free_sta(hapd, sta); wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr); + ap_free_sta(hapd, sta); + *sta_removed = 1; } else if (auth_transaction == 1) { wpa_printf(MSG_DEBUG, "SAE: Start reauthentication"); ret = auth_sae_send_commit(hapd, sta, bssid, 1); @@ -812,18 +937,21 @@ static void sae_pick_next_group(struct hostapd_data *hapd, struct sta_info *sta) { struct sae_data *sae = sta->sae; int i, *groups = hapd->conf->sae_groups; + int default_groups[] = { 19, 0 }; if (sae->state != SAE_COMMITTED) return; wpa_printf(MSG_DEBUG, "SAE: Previously selected group: %d", sae->group); - 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; @@ -852,11 +980,16 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, { 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) { - const u8 *pos, *end; - wpa_printf(MSG_DEBUG, "SAE: TESTING - reflection attack"); pos = mgmt->u.auth.variable; end = ((const u8 *) mgmt) + len; @@ -899,8 +1032,10 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, } 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)", @@ -917,8 +1052,7 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, 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, @@ -979,15 +1113,28 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, * to use a different group and that would not be * allowed if we remain in Committed state with the * previously set parameters. */ - sae_set_state(sta, SAE_NOTHING, - "Clear existing state to allow restart"); - sae_clear_data(sta->sae); + 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", @@ -1017,7 +1164,7 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, if (resp != WLAN_STATUS_SUCCESS) goto reply; - if (!token && use_sae_anti_clogging(hapd)) { + if (!token && use_sae_anti_clogging(hapd) && !allow_reuse) { wpa_printf(MSG_DEBUG, "SAE: Request anti-clogging token from " MACSTR, MAC2STR(sta->addr)); @@ -1030,7 +1177,8 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, 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, @@ -1071,7 +1219,8 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, } 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, @@ -1083,7 +1232,17 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, } 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 *) "", @@ -1091,8 +1250,9 @@ reply: } 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; } @@ -1131,6 +1291,105 @@ int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta) 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 */ @@ -1290,6 +1549,7 @@ void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta, } 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); @@ -1561,7 +1821,10 @@ prepare_auth_resp_fils(struct hostapd_data *hapd, } sta->fils_erp_pmkid_set = 0; - if (wpa_auth_pmksa_add2( + 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, @@ -1761,7 +2024,7 @@ ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta, static void handle_auth(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, - int rssi) + int rssi, int from_queue) { u16 auth_alg, auth_transaction, status_code; u16 resp = WLAN_STATUS_SUCCESS; @@ -1808,11 +2071,12 @@ static void handle_auth(struct hostapd_data *hapd, 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) { @@ -1940,6 +2204,22 @@ static void handle_auth(struct hostapd_data *hapd, 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; @@ -2259,28 +2539,30 @@ static u16 check_multi_ap(struct hostapd_data *hapd, struct sta_info *sta, } } - if (multi_ap_value == MULTI_AP_BACKHAUL_STA) - sta->flags |= WLAN_STA_MULTI_AP; + 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 ((hapd->conf->multi_ap & BACKHAUL_BSS) && - multi_ap_value == MULTI_AP_BACKHAUL_STA) - return WLAN_STATUS_SUCCESS; + if (!(multi_ap_value & MULTI_AP_BACKHAUL_STA)) { + if (hapd->conf->multi_ap & FRONTHAUL_BSS) + return WLAN_STATUS_SUCCESS; - if (hapd->conf->multi_ap & FRONTHAUL_BSS) { - if (multi_ap_value == MULTI_AP_BACKHAUL_STA) { - hostapd_logger(hapd, sta->addr, - HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_INFO, - "Backhaul STA tries to associate with fronthaul-only BSS"); - return WLAN_STATUS_ASSOC_DENIED_UNSPEC; - } - 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; } - 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; } @@ -2659,7 +2941,9 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, "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, elems.owe_dh, elems.owe_dh_len); @@ -2751,6 +3035,37 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, } #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) { @@ -3021,6 +3336,10 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, 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; @@ -3128,6 +3447,39 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, } #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 (sta && hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT)) p = hostapd_eid_vendor_vht(hapd, p); @@ -3224,30 +3576,6 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, } #endif /* CONFIG_FILS */ -#ifdef CONFIG_OWE - if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && - sta && sta->owe_ecdh && status_code == WLAN_STATUS_SUCCESS && - wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE) { - struct wpabuf *pub; - - pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0); - if (!pub) { - res = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto done; - } - /* OWE Diffie-Hellman Parameter element */ - *p++ = WLAN_EID_EXTENSION; /* Element ID */ - *p++ = 1 + 2 + wpabuf_len(pub); /* Length */ - *p++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension */ - WPA_PUT_LE16(p, sta->owe_group); - p += 2; - os_memcpy(p, wpabuf_head(pub), wpabuf_len(pub)); - p += wpabuf_len(pub); - send_len += 3 + 2 + wpabuf_len(pub); - wpabuf_free(pub); - } -#endif /* CONFIG_OWE */ - if (hostapd_drv_send_mlme(hapd, reply, send_len, 0) < 0) { wpa_printf(MSG_INFO, "Failed to send assoc resp: %s", strerror(errno)); @@ -3896,26 +4224,6 @@ static void handle_beacon(struct hostapd_data *hapd, #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, len); - return 1; -} - - static int robust_action_frame(u8 category) { return category != WLAN_ACTION_PUBLIC && @@ -4001,7 +4309,8 @@ static int handle_action(struct hostapd_data *hapd, 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_AP case WLAN_ACTION_WNM: @@ -4196,7 +4505,7 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, switch (stype) { case WLAN_FC_STYPE_AUTH: wpa_printf(MSG_DEBUG, "mgmt::auth"); - handle_auth(hapd, mgmt, len, ssi_signal); + handle_auth(hapd, mgmt, len, ssi_signal, 0); ret = 1; break; case WLAN_FC_STYPE_ASSOC_REQ: diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h index 5082226..db7badc 100644 --- a/src/ap/ieee802_11.h +++ b/src/ap/ieee802_11.h @@ -175,4 +175,6 @@ int ieee802_11_allowed_address(struct hostapd_data *hapd, const u8 *addr, 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 */ diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c index d70d6c1..707381f 100644 --- a/src/ap/ieee802_11_shared.c +++ b/src/ap/ieee802_11_shared.c @@ -213,6 +213,14 @@ void ieee802_11_sa_query_action(struct hostapd_data *hapd, 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 diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c index a56c82e..97f503f 100644 --- a/src/ap/ieee802_1x.c +++ b/src/ap/ieee802_1x.c @@ -1236,6 +1236,7 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) sta->eapol_sm->portValid = TRUE; if (sta->eapol_sm->eap) eap_sm_notify_cached(sta->eapol_sm->eap); + wpa_auth_set_ptk_rekey_timer(sta->wpa_sm); return; } #endif /* CONFIG_FILS */ @@ -2733,7 +2734,8 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, wpa_auth_sta_key_mgmt(sta->wpa_sm))) ? 1 : 2, (unsigned int) diff.sec, - sm->identity ? (char *) sm->identity : identity_buf); + sm->identity ? (char *) sm->identity : + (identity_buf ? identity_buf : "N/A")); os_free(identity_buf); if (os_snprintf_error(buflen - len, ret)) return len; diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c index 8858a34..4f9eae8 100644 --- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c @@ -13,6 +13,7 @@ #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" @@ -362,6 +363,11 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) 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 @@ -501,6 +507,13 @@ skip_poll: } 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", @@ -799,6 +812,8 @@ void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta, 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; diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h index ee3f628..ece0c60 100644 --- a/src/ap/sta_info.h +++ b/src/ap/sta_info.h @@ -265,6 +265,10 @@ struct sta_info { 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; diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 8e2b48e..f2e028c 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -1,6 +1,6 @@ /* * IEEE 802.11 RSN / WPA Authenticator - * Copyright (c) 2004-2018, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -114,12 +114,13 @@ static inline int wpa_auth_get_eapol(struct wpa_authenticator *wpa_auth, 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, size_t *psk_len) + const u8 *prev_psk, size_t *psk_len, + int *vlan_id) { 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, psk_len); + prev_psk, psk_len, vlan_id); } @@ -251,6 +252,15 @@ static int wpa_channel_info(struct wpa_authenticator *wpa_auth, #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; @@ -310,6 +320,19 @@ static void wpa_rekey_ptk(void *eloop_ctx, void *timeout_ctx) } +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) @@ -345,6 +368,10 @@ static int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth, 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; @@ -682,7 +709,10 @@ static void wpa_free_sta_sm(struct wpa_state_machine *sm) 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)); } @@ -848,13 +878,15 @@ static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data, int ok = 0; const u8 *pmk = NULL; size_t pmk_len; + int vlan_id = 0; os_memset(&PTK, 0, sizeof(PTK)); for (;;) { if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) && !wpa_key_mgmt_sae(sm->wpa_key_mgmt)) { pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, - sm->p2p_dev_addr, pmk, &pmk_len); + sm->p2p_dev_addr, pmk, &pmk_len, + &vlan_id); if (pmk == NULL) break; #ifdef CONFIG_IEEE80211R_AP @@ -873,8 +905,10 @@ static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data, if (wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK, data, data_len) == 0) { - os_memcpy(sm->PMK, pmk, pmk_len); - sm->pmk_len = pmk_len; + if (sm->PMK != pmk) { + os_memcpy(sm->PMK, pmk, pmk_len); + sm->pmk_len = pmk_len; + } ok = 1; break; } @@ -893,6 +927,11 @@ static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data, 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; @@ -1217,6 +1256,11 @@ continue_processing: 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 @@ -1225,9 +1269,17 @@ continue_processing: &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; @@ -1332,6 +1384,9 @@ static int wpa_gmk_to_gtk(const u8 *gmk, const char *label, const u8 *addr, 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, gtk_len) < 0) ret = -1; @@ -1611,6 +1666,9 @@ static void wpa_send_eapol(struct wpa_authenticator *wpa_auth, 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 %u)", timeout_ms, ctr); eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000, @@ -1685,6 +1743,14 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event) 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: @@ -1725,6 +1791,7 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event) /* Using FT protocol, not WPA auth state machine */ sm->ft_completed = 1; + wpa_auth_set_ptk_rekey_timer(sm); return 0; #else /* CONFIG_IEEE80211R_AP */ break; @@ -2001,7 +2068,7 @@ SM_STATE(WPA_PTK, INITPSK) SM_ENTRY_MA(WPA_PTK, INITPSK, wpa_ptk); psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL, - &psk_len); + &psk_len, NULL); if (psk) { os_memcpy(sm->PMK, psk, psk_len); sm->pmk_len = psk_len; @@ -2015,6 +2082,10 @@ SM_STATE(WPA_PTK, INITPSK) 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; @@ -2075,6 +2146,29 @@ SM_STATE(WPA_PTK, PTKSTART) 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) { @@ -2113,14 +2207,36 @@ static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, const u8 *pmk, unsigned int pmk_len, struct wpa_ptk *ptk) { + const u8 *z = NULL; + size_t z_len = 0; + #ifdef CONFIG_IEEE80211R_AP - if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) - return wpa_auth_derive_ptk_ft(sm, pmk, ptk); + 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); } @@ -2170,6 +2286,16 @@ int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk, 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 */ @@ -2689,6 +2815,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) 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; @@ -2704,7 +2831,8 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) 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, &pmk_len); + sm->p2p_dev_addr, pmk, &pmk_len, + &vlan_id); if (pmk == NULL) break; psk_found = 1; @@ -2719,6 +2847,12 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) pmk_len = sm->pmk_len; } + 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_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK) < 0) break; @@ -2726,8 +2860,10 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK, sm->last_rx_eapol_key, sm->last_rx_eapol_key_len) == 0) { - os_memcpy(sm->PMK, pmk, pmk_len); - sm->pmk_len = pmk_len; + if (sm->PMK != pmk) { + os_memcpy(sm->PMK, pmk, pmk_len); + sm->pmk_len = pmk_len; + } ok = 1; break; } @@ -2873,6 +3009,13 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) } #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); @@ -3207,12 +3350,7 @@ SM_STATE(WPA_PTK, PTKINITDONE) /* 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) || sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP || @@ -3312,7 +3450,7 @@ SM_STEP(WPA_PTK) break; case WPA_PTK_INITPSK: if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, - NULL, NULL)) { + NULL, NULL, NULL)) { SM_ENTER(WPA_PTK, PTKSTART); #ifdef CONFIG_SAE } else if (wpa_auth_uses_sae(sm) && sm->pmksa) { @@ -4760,9 +4898,37 @@ void wpa_auth_get_fils_aead_params(struct wpa_state_machine *sm, *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, diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h index e61648d..df1e17a 100644 --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -254,7 +254,8 @@ struct wpa_auth_callbacks { int value); int (*get_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var); const u8 * (*get_psk)(void *ctx, const u8 *addr, const u8 *p2p_dev_addr, - const u8 *prev_psk, size_t *psk_len); + 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); @@ -270,6 +271,7 @@ struct wpa_auth_callbacks { 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); @@ -316,7 +318,7 @@ enum { }; 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 *owe_dh, size_t owe_dh_len); @@ -468,9 +470,13 @@ int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth, int use_sha384, 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), @@ -482,5 +488,6 @@ int wpa_auth_resend_group_m1(struct wpa_state_machine *sm, void (*cb)(void *ctx1, void *ctx2), void *ctx1, void *ctx2); int wpa_auth_rekey_gtk(struct wpa_authenticator *wpa_auth); +void wpa_auth_set_ptk_rekey_timer(struct wpa_state_machine *sm); #endif /* WPA_AUTH_H */ diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c index ac736f0..ac16199 100644 --- a/src/ap/wpa_auth_ft.c +++ b/src/ap/wpa_auth_ft.c @@ -66,7 +66,7 @@ struct tlv_list { * Returns: 0 on success, -1 on error */ static int wpa_ft_rrb_decrypt(const u8 *key, const size_t key_len, - const u8 *enc, const size_t enc_len, + const u8 *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) @@ -74,7 +74,11 @@ static int wpa_ft_rrb_decrypt(const u8 *key, const size_t key_len, 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); @@ -97,8 +101,18 @@ static int wpa_ft_rrb_decrypt(const u8 *key, const size_t key_len, goto err; if (aes_siv_decrypt(key, key_len, enc, enc_len, 3, ad, ad_len, - *plain) < 0) - goto err; + *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", @@ -463,9 +477,12 @@ static int wpa_ft_rrb_encrypt(const u8 *key, const size_t key_len, 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 */ @@ -475,6 +492,8 @@ static int wpa_ft_rrb_encrypt(const u8 *key, const size_t key_len, 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; } @@ -503,9 +522,10 @@ static int wpa_ft_rrb_build(const u8 *key, const size_t key_len, const u8 *src_addr, u8 type, u8 **packet, size_t *packet_len) { - u8 *plain = NULL, *auth = NULL, *pos; + 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) @@ -517,6 +537,28 @@ static int wpa_ft_rrb_build(const u8 *key, const size_t key_len, *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; @@ -529,6 +571,7 @@ static int wpa_ft_rrb_build(const u8 *key, const size_t key_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; @@ -596,8 +639,8 @@ static int wpa_ft_rrb_oui_send(struct wpa_authenticator *wpa_auth, { if (!wpa_auth->cb->send_oui) return -1; - wpa_printf(MSG_DEBUG, "FT: RRB-OUI type %u send to " MACSTR, - oui_suffix, MAC2STR(dst)); + 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); } @@ -620,7 +663,7 @@ static const u8 * wpa_ft_get_psk(struct wpa_authenticator *wpa_auth, 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); + prev_psk, NULL, NULL); } @@ -907,6 +950,8 @@ wpa_ft_rrb_seq_req(struct wpa_authenticator *wpa_auth, 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; @@ -2029,8 +2074,7 @@ int wpa_ft_store_pmk_fils(struct wpa_state_machine *sm, } -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) { 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) ? @@ -2386,10 +2430,24 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, 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) @@ -2925,6 +2983,8 @@ 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 " @@ -4373,6 +4433,7 @@ void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, 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); if (is_multicast_ether_addr(src_addr)) { wpa_printf(MSG_DEBUG, @@ -4401,8 +4462,10 @@ void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, } 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: diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c index 9091f43..45172c6 100644 --- a/src/ap/wpa_auth_glue.c +++ b/src/ap/wpa_auth_glue.c @@ -246,12 +246,15 @@ static int hostapd_wpa_auth_get_eapol(void *ctx, const u8 *addr, static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr, const u8 *p2p_dev_addr, - const u8 *prev_psk, size_t *psk_len) + 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; @@ -287,7 +290,8 @@ static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr, } #endif /* CONFIG_OWE */ - psk = hostapd_get_psk(hapd->conf, addr, p2p_dev_addr, prev_psk); + 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. @@ -295,6 +299,9 @@ static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr, */ 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) { @@ -788,6 +795,45 @@ static int hostapd_channel_info(void *ctx, struct wpa_channel_info *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, @@ -1229,6 +1275,7 @@ int hostapd_setup_wpa(struct hostapd_data *hapd) .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 */ diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h index a349304..4babd0c 100644 --- a/src/ap/wpa_auth_i.h +++ b/src/ap/wpa_auth_i.h @@ -22,6 +22,7 @@ struct wpa_state_machine { u8 addr[ETH_ALEN]; u8 p2p_dev_addr[ETH_ALEN]; + u16 auth_alg; enum { WPA_PTK_INITIALIZE, WPA_PTK_DISCONNECT, WPA_PTK_DISCONNECTED, @@ -118,6 +119,8 @@ struct wpa_state_machine { 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 */ @@ -150,6 +153,10 @@ struct wpa_state_machine { 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; @@ -285,8 +292,7 @@ int wpa_write_ftie(struct wpa_auth_config *conf, int use_sha384, 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); diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c index 3bcbef7..8580a5a 100644 --- a/src/ap/wpa_auth_ie.c +++ b/src/ap/wpa_auth_ie.c @@ -530,7 +530,7 @@ static int wpa_auth_okc_iter(struct wpa_authenticator *a, void *ctx) int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, + 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 *owe_dh, size_t owe_dh_len) @@ -560,6 +560,10 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, 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)) { @@ -831,6 +835,12 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, "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"); @@ -848,6 +858,21 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, 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", diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c index 5ec0199..6161cdb 100644 --- a/src/ap/wps_hostapd.c +++ b/src/ap/wps_hostapd.c @@ -354,6 +354,18 @@ static int hapd_wps_reconfig_in_memory(struct hostapd_data *hapd, 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 @@ static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx) char buf[1024]; int multi_bss; int wpa; + int pmf_changed = 0; if (hapd->wps == NULL) return 0; @@ -520,6 +533,10 @@ static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx) 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 @@ static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx) 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 @@ static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx) 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 @@ int hostapd_init_wps(struct hostapd_data *hapd, { 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); @@ -1133,6 +1172,31 @@ int hostapd_init_wps(struct hostapd_data *hapd, 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; @@ -1174,10 +1238,12 @@ int hostapd_init_wps(struct hostapd_data *hapd, 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; } diff --git a/src/common/common_module_tests.c b/src/common/common_module_tests.c index 0b596bb..30c5247 100644 --- a/src/common/common_module_tests.c +++ b/src/common/common_module_tests.c @@ -1,6 +1,6 @@ /* * common module tests - * Copyright (c) 2014-2015, Jouni Malinen <j@w1.fi> + * Copyright (c) 2014-2019, Jouni Malinen <j@w1.fi> * * 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 { @@ -248,6 +250,179 @@ static int gas_tests(void) } +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; @@ -256,6 +431,7 @@ int common_module_tests(void) if (ieee802_11_parse_tests() < 0 || gas_tests() < 0 || + sae_tests() < 0 || rsn_ie_parse_tests() < 0) ret = -1; diff --git a/src/common/dpp.c b/src/common/dpp.c index bcb694b..49de476 100644 --- a/src/common/dpp.c +++ b/src/common/dpp.c @@ -1,6 +1,7 @@ /* * 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. @@ -18,6 +19,7 @@ #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" @@ -68,6 +70,11 @@ static void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, #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. */ @@ -1537,6 +1544,9 @@ static struct wpabuf * dpp_auth_build_req(struct dpp_authentication *auth, 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; @@ -1579,6 +1589,13 @@ static struct wpabuf * dpp_auth_build_req(struct dpp_authentication *auth, 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"); @@ -1705,6 +1722,9 @@ static struct wpabuf * dpp_auth_build_resp(struct dpp_authentication *auth, /* 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; @@ -1732,6 +1752,13 @@ static struct wpabuf * dpp_auth_build_resp(struct dpp_authentication *auth, 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 @@ -2202,8 +2229,8 @@ fail: } -struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth, - const char *json) +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; @@ -2307,6 +2334,55 @@ fail: } +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, @@ -2893,6 +2969,10 @@ dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual, 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) { @@ -2922,6 +3002,22 @@ dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual, 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) { @@ -3450,6 +3546,10 @@ dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr, 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) { @@ -3524,6 +3624,22 @@ dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr, 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) { @@ -3987,6 +4103,99 @@ fail: } +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) @@ -3997,6 +4206,162 @@ void dpp_configuration_free(struct dpp_configuration *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) @@ -4096,6 +4461,31 @@ fail: } +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) @@ -4116,6 +4506,8 @@ dpp_build_conf_obj_dpp(struct dpp_authentication *auth, int ap, 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, @@ -4134,6 +4526,13 @@ dpp_build_conf_obj_dpp(struct dpp_authentication *auth, int ap, 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); @@ -4251,14 +4650,22 @@ skip_groups: 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_put_str(buf, "\"cred\":{\"akm\":\"dpp\",\"signedConnector\":\""); + 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); @@ -4304,28 +4711,7 @@ dpp_build_conf_obj_legacy(struct dpp_authentication *auth, int ap, return NULL; wpabuf_printf(buf, "\"cred\":{\"akm\":\"%s\",", dpp_akm_str(conf->akm)); - if (conf->passphrase) { - char pass[63 * 6 + 1]; - - if (os_strlen(conf->passphrase) > 63) { - wpabuf_free(buf); - return NULL; - } - - json_escape_string(pass, sizeof(pass), conf->passphrase, - os_strlen(conf->passphrase)); - wpabuf_put_str(buf, "\"pass\":\""); - wpabuf_put_str(buf, pass); - wpabuf_put_str(buf, "\""); - } else { - char psk[2 * sizeof(conf->psk) + 1]; - - wpa_snprintf_hex(psk, sizeof(psk), - conf->psk, sizeof(conf->psk)); - wpabuf_put_str(buf, "\"psk_hex\":\""); - wpabuf_put_str(buf, psk); - wpabuf_put_str(buf, "\""); - } + dpp_build_legacy_cred_params(buf, conf); wpabuf_put_str(buf, "}}"); wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object (legacy)", @@ -4356,7 +4742,7 @@ dpp_build_conf_obj(struct dpp_authentication *auth, int ap) return NULL; } - if (conf->akm == DPP_AKM_DPP) + if (dpp_akm_dpp(conf->akm)) return dpp_build_conf_obj_dpp(auth, ap, conf); return dpp_build_conf_obj_legacy(auth, ap, conf); } @@ -4380,6 +4766,7 @@ dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce, 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; @@ -4552,6 +4939,7 @@ dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start, 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, @@ -4716,7 +5104,7 @@ static int dpp_parse_cred_legacy(struct dpp_authentication *auth, os_strlcpy(auth->passphrase, pass->string, sizeof(auth->passphrase)); } else if (psk_hex && psk_hex->type == JSON_STRING) { - if (auth->akm == DPP_AKM_SAE) { + if (dpp_akm_sae(auth->akm) && !dpp_akm_psk(auth->akm)) { wpa_printf(MSG_DEBUG, "DPP: Unexpected psk_hex with akm=sae"); return -1; @@ -4734,8 +5122,7 @@ static int dpp_parse_cred_legacy(struct dpp_authentication *auth, return -1; } - if ((auth->akm == DPP_AKM_SAE || auth->akm == DPP_AKM_PSK_SAE) && - !auth->passphrase[0]) { + if (dpp_akm_sae(auth->akm) && !auth->passphrase[0]) { wpa_printf(MSG_DEBUG, "DPP: No pass for sae found"); return -1; } @@ -5248,6 +5635,13 @@ static int dpp_parse_cred_dpp(struct dpp_authentication *auth, os_memset(&info, 0, sizeof(info)); + if (dpp_akm_psk(auth->akm) || dpp_akm_sae(auth->akm)) { + wpa_printf(MSG_DEBUG, + "DPP: Legacy credential included in Connector credential"); + if (dpp_parse_cred_legacy(auth, cred) < 0) + return -1; + } + wpa_printf(MSG_DEBUG, "DPP: Connector credential"); csign = json_get_member(cred, "csign"); @@ -5313,6 +5707,10 @@ const char * dpp_akm_str(enum dpp_akm akm) return "sae"; case DPP_AKM_PSK_SAE: return "psk+sae"; + case DPP_AKM_SAE_DPP: + return "dpp+sae"; + case DPP_AKM_PSK_SAE_DPP: + return "dpp+psk+sae"; default: return "??"; } @@ -5329,6 +5727,10 @@ static enum dpp_akm dpp_akm_from_str(const char *akm) return DPP_AKM_PSK_SAE; if (os_strcmp(akm, "dpp") == 0) return DPP_AKM_DPP; + if (os_strcmp(akm, "dpp+sae") == 0) + return DPP_AKM_SAE_DPP; + if (os_strcmp(akm, "dpp+psk+sae") == 0) + return DPP_AKM_PSK_SAE_DPP; return DPP_AKM_UNKNOWN; } @@ -5392,11 +5794,10 @@ static int dpp_parse_conf_obj(struct dpp_authentication *auth, } auth->akm = dpp_akm_from_str(token->string); - if (auth->akm == DPP_AKM_PSK || auth->akm == DPP_AKM_SAE || - auth->akm == DPP_AKM_PSK_SAE) { + if (dpp_akm_legacy(auth->akm)) { if (dpp_parse_cred_legacy(auth, cred) < 0) goto fail; - } else if (auth->akm == DPP_AKM_DPP) { + } else if (dpp_akm_dpp(auth->akm)) { if (dpp_parse_cred_dpp(auth, cred) < 0) goto fail; } else { @@ -5425,6 +5826,8 @@ int dpp_conf_resp_rx(struct dpp_authentication *auth, size_t unwrapped_len = 0; int ret = -1; + auth->conf_resp_status = 255; + if (dpp_check_attrs(wpabuf_head(resp), wpabuf_len(resp)) < 0) { dpp_auth_fail(auth, "Invalid attribute in config response"); return -1; @@ -5485,6 +5888,7 @@ int dpp_conf_resp_rx(struct dpp_authentication *auth, "Missing or invalid required DPP Status attribute"); goto fail; } + auth->conf_resp_status = status[0]; wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]); if (status[0] != DPP_STATUS_OK) { dpp_auth_fail(auth, "Configurator rejected configuration"); @@ -5511,6 +5915,146 @@ fail: } +#ifdef CONFIG_DPP2 +enum dpp_status_error dpp_conf_result_rx(struct dpp_authentication *auth, + const u8 *hdr, + const u8 *attr_start, size_t attr_len) +{ + const u8 *wrapped_data, *status, *e_nonce; + u16 wrapped_data_len, status_len, e_nonce_len; + const u8 *addr[2]; + size_t len[2]; + u8 *unwrapped = NULL; + size_t unwrapped_len = 0; + enum dpp_status_error ret = 256; + + 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"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data", + wrapped_data, wrapped_data_len); + + attr_len = wrapped_data - 4 - attr_start; + + 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->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; + } + + e_nonce = dpp_get_attr(unwrapped, unwrapped_len, + DPP_ATTR_ENROLLEE_NONCE, + &e_nonce_len); + if (!e_nonce || e_nonce_len != auth->curve->nonce_len) { + dpp_auth_fail(auth, + "Missing or invalid Enrollee Nonce attribute"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len); + if (os_memcmp(e_nonce, auth->e_nonce, e_nonce_len) != 0) { + dpp_auth_fail(auth, "Enrollee Nonce mismatch"); + wpa_hexdump(MSG_DEBUG, "DPP: Expected Enrollee Nonce", + auth->e_nonce, e_nonce_len); + goto fail; + } + + status = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_STATUS, + &status_len); + if (!status || status_len < 1) { + dpp_auth_fail(auth, + "Missing or invalid required DPP Status attribute"); + goto fail; + } + wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]); + ret = status[0]; + +fail: + bin_clear_free(unwrapped, unwrapped_len); + return ret; +} +#endif /* CONFIG_DPP2 */ + + +struct wpabuf * dpp_build_conf_result(struct dpp_authentication *auth, + enum dpp_status_error status) +{ + struct wpabuf *msg, *clear; + size_t nonce_len, clear_len, attr_len; + const u8 *addr[2]; + size_t len[2]; + u8 *wrapped; + + nonce_len = auth->curve->nonce_len; + clear_len = 5 + 4 + nonce_len; + attr_len = 4 + clear_len + AES_BLOCK_SIZE; + clear = wpabuf_alloc(clear_len); + msg = dpp_alloc_msg(DPP_PA_CONFIGURATION_RESULT, attr_len); + if (!clear || !msg) + return NULL; + + /* DPP Status */ + dpp_build_attr_status(clear, status); + + /* 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); + + /* 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 (none) */ + addr[1] = wpabuf_put(msg, 0); + len[1] = 0; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); + + /* Wrapped Data */ + 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), + 2, addr, len, wrapped) < 0) + goto fail; + + wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Result attributes", msg); + wpabuf_free(clear); + return msg; +fail: + wpabuf_free(clear); + wpabuf_free(msg); + return NULL; +} + + void dpp_configurator_free(struct dpp_configurator *conf) { if (!conf) @@ -7691,3 +8235,487 @@ fail: goto out; } #endif /* CONFIG_TESTING_OPTIONS */ + + +#ifdef CONFIG_DPP2 + +struct dpp_pfs * dpp_pfs_init(const u8 *net_access_key, + size_t net_access_key_len) +{ + struct wpabuf *pub = NULL; + EVP_PKEY *own_key; + struct dpp_pfs *pfs; + + pfs = os_zalloc(sizeof(*pfs)); + if (!pfs) + return NULL; + + own_key = dpp_set_keypair(&pfs->curve, net_access_key, + net_access_key_len); + if (!own_key) { + wpa_printf(MSG_ERROR, "DPP: Failed to parse own netAccessKey"); + goto fail; + } + EVP_PKEY_free(own_key); + + pfs->ecdh = crypto_ecdh_init(pfs->curve->ike_group); + if (!pfs->ecdh) + goto fail; + + pub = crypto_ecdh_get_pubkey(pfs->ecdh, 0); + pub = wpabuf_zeropad(pub, pfs->curve->prime_len); + if (!pub) + goto fail; + + pfs->ie = wpabuf_alloc(5 + wpabuf_len(pub)); + if (!pfs->ie) + goto fail; + wpabuf_put_u8(pfs->ie, WLAN_EID_EXTENSION); + wpabuf_put_u8(pfs->ie, 1 + 2 + wpabuf_len(pub)); + wpabuf_put_u8(pfs->ie, WLAN_EID_EXT_OWE_DH_PARAM); + wpabuf_put_le16(pfs->ie, pfs->curve->ike_group); + wpabuf_put_buf(pfs->ie, pub); + wpabuf_free(pub); + wpa_hexdump_buf(MSG_DEBUG, "DPP: Diffie-Hellman Parameter element", + pfs->ie); + + return pfs; +fail: + wpabuf_free(pub); + dpp_pfs_free(pfs); + return NULL; +} + + +int dpp_pfs_process(struct dpp_pfs *pfs, const u8 *peer_ie, size_t peer_ie_len) +{ + if (peer_ie_len < 2) + return -1; + if (WPA_GET_LE16(peer_ie) != pfs->curve->ike_group) { + wpa_printf(MSG_DEBUG, "DPP: Peer used different group for PFS"); + return -1; + } + + pfs->secret = crypto_ecdh_set_peerkey(pfs->ecdh, 0, peer_ie + 2, + peer_ie_len - 2); + pfs->secret = wpabuf_zeropad(pfs->secret, pfs->curve->prime_len); + if (!pfs->secret) { + wpa_printf(MSG_DEBUG, "DPP: Invalid peer DH public key"); + return -1; + } + wpa_hexdump_buf_key(MSG_DEBUG, "DPP: DH shared secret", pfs->secret); + return 0; +} + + +void dpp_pfs_free(struct dpp_pfs *pfs) +{ + if (!pfs) + return; + crypto_ecdh_deinit(pfs->ecdh); + wpabuf_free(pfs->ie); + wpabuf_clear_free(pfs->secret); + os_free(pfs); +} + +#endif /* CONFIG_DPP2 */ + + +static unsigned int dpp_next_id(struct dpp_global *dpp) +{ + struct dpp_bootstrap_info *bi; + unsigned int max_id = 0; + + dl_list_for_each(bi, &dpp->bootstrap, struct dpp_bootstrap_info, list) { + if (bi->id > max_id) + max_id = bi->id; + } + return max_id + 1; +} + + +static int dpp_bootstrap_del(struct dpp_global *dpp, unsigned int id) +{ + struct dpp_bootstrap_info *bi, *tmp; + int found = 0; + + if (!dpp) + return -1; + + dl_list_for_each_safe(bi, tmp, &dpp->bootstrap, + struct dpp_bootstrap_info, list) { + if (id && bi->id != id) + continue; + found = 1; + dl_list_del(&bi->list); + dpp_bootstrap_info_free(bi); + } + + if (id == 0) + return 0; /* flush succeeds regardless of entries found */ + return found ? 0 : -1; +} + + +struct dpp_bootstrap_info * dpp_add_qr_code(struct dpp_global *dpp, + const char *uri) +{ + struct dpp_bootstrap_info *bi; + + if (!dpp) + return NULL; + + bi = dpp_parse_qr_code(uri); + if (!bi) + return NULL; + + bi->id = dpp_next_id(dpp); + dl_list_add(&dpp->bootstrap, &bi->list); + return bi; +} + + +int dpp_bootstrap_gen(struct dpp_global *dpp, const char *cmd) +{ + char *chan = NULL, *mac = NULL, *info = NULL, *pk = NULL, *curve = NULL; + char *key = NULL; + u8 *privkey = NULL; + size_t privkey_len = 0; + size_t len; + int ret = -1; + struct dpp_bootstrap_info *bi; + + if (!dpp) + return -1; + + bi = os_zalloc(sizeof(*bi)); + if (!bi) + goto fail; + + if (os_strstr(cmd, "type=qrcode")) + bi->type = DPP_BOOTSTRAP_QR_CODE; + else if (os_strstr(cmd, "type=pkex")) + bi->type = DPP_BOOTSTRAP_PKEX; + else + goto fail; + + chan = get_param(cmd, " chan="); + mac = get_param(cmd, " mac="); + info = get_param(cmd, " info="); + curve = get_param(cmd, " curve="); + key = get_param(cmd, " key="); + + if (key) { + privkey_len = os_strlen(key) / 2; + privkey = os_malloc(privkey_len); + if (!privkey || + hexstr2bin(key, privkey, privkey_len) < 0) + goto fail; + } + + pk = dpp_keygen(bi, curve, privkey, privkey_len); + if (!pk) + goto fail; + + len = 4; /* "DPP:" */ + if (chan) { + if (dpp_parse_uri_chan_list(bi, chan) < 0) + goto fail; + len += 3 + os_strlen(chan); /* C:...; */ + } + if (mac) { + if (dpp_parse_uri_mac(bi, mac) < 0) + goto fail; + len += 3 + os_strlen(mac); /* M:...; */ + } + if (info) { + if (dpp_parse_uri_info(bi, info) < 0) + goto fail; + len += 3 + os_strlen(info); /* I:...; */ + } + len += 4 + os_strlen(pk); + bi->uri = os_malloc(len + 1); + if (!bi->uri) + goto fail; + os_snprintf(bi->uri, len + 1, "DPP:%s%s%s%s%s%s%s%s%sK:%s;;", + chan ? "C:" : "", chan ? chan : "", chan ? ";" : "", + mac ? "M:" : "", mac ? mac : "", mac ? ";" : "", + info ? "I:" : "", info ? info : "", info ? ";" : "", + pk); + bi->id = dpp_next_id(dpp); + dl_list_add(&dpp->bootstrap, &bi->list); + ret = bi->id; + bi = NULL; +fail: + os_free(curve); + os_free(pk); + os_free(chan); + os_free(mac); + os_free(info); + str_clear_free(key); + bin_clear_free(privkey, privkey_len); + dpp_bootstrap_info_free(bi); + return ret; +} + + +struct dpp_bootstrap_info * +dpp_bootstrap_get_id(struct dpp_global *dpp, unsigned int id) +{ + struct dpp_bootstrap_info *bi; + + if (!dpp) + return NULL; + + dl_list_for_each(bi, &dpp->bootstrap, struct dpp_bootstrap_info, list) { + if (bi->id == id) + return bi; + } + return NULL; +} + + +int dpp_bootstrap_remove(struct dpp_global *dpp, const char *id) +{ + unsigned int id_val; + + if (os_strcmp(id, "*") == 0) { + id_val = 0; + } else { + id_val = atoi(id); + if (id_val == 0) + return -1; + } + + return dpp_bootstrap_del(dpp, id_val); +} + + +struct dpp_bootstrap_info * +dpp_pkex_finish(struct dpp_global *dpp, struct dpp_pkex *pkex, const u8 *peer, + unsigned int freq) +{ + struct dpp_bootstrap_info *bi; + + bi = os_zalloc(sizeof(*bi)); + if (!bi) + return NULL; + bi->id = dpp_next_id(dpp); + bi->type = DPP_BOOTSTRAP_PKEX; + os_memcpy(bi->mac_addr, peer, ETH_ALEN); + bi->num_freq = 1; + bi->freq[0] = freq; + bi->curve = pkex->own_bi->curve; + bi->pubkey = pkex->peer_bootstrap_key; + pkex->peer_bootstrap_key = NULL; + if (dpp_bootstrap_key_hash(bi) < 0) { + dpp_bootstrap_info_free(bi); + return NULL; + } + dpp_pkex_free(pkex); + dl_list_add(&dpp->bootstrap, &bi->list); + return bi; +} + + +const char * dpp_bootstrap_get_uri(struct dpp_global *dpp, unsigned int id) +{ + struct dpp_bootstrap_info *bi; + + bi = dpp_bootstrap_get_id(dpp, id); + if (!bi) + return NULL; + return bi->uri; +} + + +int dpp_bootstrap_info(struct dpp_global *dpp, int id, + char *reply, int reply_size) +{ + struct dpp_bootstrap_info *bi; + + bi = dpp_bootstrap_get_id(dpp, id); + if (!bi) + return -1; + return os_snprintf(reply, reply_size, "type=%s\n" + "mac_addr=" MACSTR "\n" + "info=%s\n" + "num_freq=%u\n" + "curve=%s\n", + dpp_bootstrap_type_txt(bi->type), + MAC2STR(bi->mac_addr), + bi->info ? bi->info : "", + bi->num_freq, + bi->curve->name); +} + + +void dpp_bootstrap_find_pair(struct dpp_global *dpp, const u8 *i_bootstrap, + const u8 *r_bootstrap, + struct dpp_bootstrap_info **own_bi, + struct dpp_bootstrap_info **peer_bi) +{ + struct dpp_bootstrap_info *bi; + + *own_bi = NULL; + *peer_bi = NULL; + if (!dpp) + return; + + dl_list_for_each(bi, &dpp->bootstrap, struct dpp_bootstrap_info, list) { + if (!*own_bi && bi->own && + os_memcmp(bi->pubkey_hash, r_bootstrap, + SHA256_MAC_LEN) == 0) { + wpa_printf(MSG_DEBUG, + "DPP: Found matching own bootstrapping information"); + *own_bi = bi; + } + + if (!*peer_bi && !bi->own && + os_memcmp(bi->pubkey_hash, i_bootstrap, + SHA256_MAC_LEN) == 0) { + wpa_printf(MSG_DEBUG, + "DPP: Found matching peer bootstrapping information"); + *peer_bi = bi; + } + + if (*own_bi && *peer_bi) + break; + } + +} + + +static unsigned int dpp_next_configurator_id(struct dpp_global *dpp) +{ + struct dpp_configurator *conf; + unsigned int max_id = 0; + + dl_list_for_each(conf, &dpp->configurator, struct dpp_configurator, + list) { + if (conf->id > max_id) + max_id = conf->id; + } + return max_id + 1; +} + + +int dpp_configurator_add(struct dpp_global *dpp, const char *cmd) +{ + char *curve = NULL; + char *key = NULL; + u8 *privkey = NULL; + size_t privkey_len = 0; + int ret = -1; + struct dpp_configurator *conf = NULL; + + curve = get_param(cmd, " curve="); + key = get_param(cmd, " key="); + + if (key) { + privkey_len = os_strlen(key) / 2; + privkey = os_malloc(privkey_len); + if (!privkey || + hexstr2bin(key, privkey, privkey_len) < 0) + goto fail; + } + + conf = dpp_keygen_configurator(curve, privkey, privkey_len); + if (!conf) + goto fail; + + conf->id = dpp_next_configurator_id(dpp); + dl_list_add(&dpp->configurator, &conf->list); + ret = conf->id; + conf = NULL; +fail: + os_free(curve); + str_clear_free(key); + bin_clear_free(privkey, privkey_len); + dpp_configurator_free(conf); + return ret; +} + + +static int dpp_configurator_del(struct dpp_global *dpp, unsigned int id) +{ + struct dpp_configurator *conf, *tmp; + int found = 0; + + if (!dpp) + return -1; + + dl_list_for_each_safe(conf, tmp, &dpp->configurator, + struct dpp_configurator, list) { + if (id && conf->id != id) + continue; + found = 1; + dl_list_del(&conf->list); + dpp_configurator_free(conf); + } + + if (id == 0) + return 0; /* flush succeeds regardless of entries found */ + return found ? 0 : -1; +} + + +int dpp_configurator_remove(struct dpp_global *dpp, const char *id) +{ + unsigned int id_val; + + if (os_strcmp(id, "*") == 0) { + id_val = 0; + } else { + id_val = atoi(id); + if (id_val == 0) + return -1; + } + + return dpp_configurator_del(dpp, id_val); +} + + +int dpp_configurator_get_key_id(struct dpp_global *dpp, unsigned int id, + char *buf, size_t buflen) +{ + struct dpp_configurator *conf; + + conf = dpp_configurator_get_id(dpp, id); + if (!conf) + return -1; + + return dpp_configurator_get_key(conf, buf, buflen); +} + + +struct dpp_global * dpp_global_init(void) +{ + struct dpp_global *dpp; + + dpp = os_zalloc(sizeof(*dpp)); + if (!dpp) + return NULL; + + dl_list_init(&dpp->bootstrap); + dl_list_init(&dpp->configurator); + + return dpp; +} + + +void dpp_global_clear(struct dpp_global *dpp) +{ + if (!dpp) + return; + + dpp_bootstrap_del(dpp, 0); + dpp_configurator_del(dpp, 0); +} + + +void dpp_global_deinit(struct dpp_global *dpp) +{ + dpp_global_clear(dpp); + os_free(dpp); +} diff --git a/src/common/dpp.h b/src/common/dpp.h index 2575908..5a6d8cc 100644 --- a/src/common/dpp.h +++ b/src/common/dpp.h @@ -1,6 +1,7 @@ /* * 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. @@ -9,12 +10,16 @@ #ifndef DPP_H #define DPP_H +#ifdef CONFIG_DPP #include <openssl/x509.h> #include "utils/list.h" #include "common/wpa_common.h" #include "crypto/sha256.h" +struct crypto_ecdh; +struct dpp_global; + #define DPP_HDR_LEN (4 + 2) /* OUI, OUI Type, Crypto Suite, DPP frame type */ enum dpp_public_action_frame_type { @@ -27,6 +32,7 @@ enum dpp_public_action_frame_type { DPP_PA_PKEX_EXCHANGE_RESP = 8, DPP_PA_PKEX_COMMIT_REVEAL_REQ = 9, DPP_PA_PKEX_COMMIT_REVEAL_RESP = 10, + DPP_PA_CONFIGURATION_RESULT = 11, }; enum dpp_attribute_id { @@ -54,6 +60,8 @@ enum dpp_attribute_id { DPP_ATTR_TRANSACTION_ID = 0x1016, DPP_ATTR_BOOTSTRAP_INFO = 0x1017, DPP_ATTR_CHANNEL = 0x1018, + DPP_ATTR_PROTOCOL_VERSION = 0x1019, + DPP_ATTR_ENVELOPED_DATA = 0x101A, }; enum dpp_status_error { @@ -66,6 +74,7 @@ enum dpp_status_error { DPP_STATUS_RESPONSE_PENDING = 6, DPP_STATUS_INVALID_CONNECTOR = 7, DPP_STATUS_NO_MATCH = 8, + DPP_STATUS_CONFIG_REJECTED = 9, }; #define DPP_CAPAB_ENROLLEE BIT(0) @@ -141,7 +150,9 @@ enum dpp_akm { DPP_AKM_DPP, DPP_AKM_PSK, DPP_AKM_SAE, - DPP_AKM_PSK_SAE + DPP_AKM_PSK_SAE, + DPP_AKM_SAE_DPP, + DPP_AKM_PSK_SAE_DPP, }; struct dpp_configuration { @@ -158,10 +169,12 @@ struct dpp_configuration { /* For legacy configuration */ char *passphrase; u8 psk[32]; + int psk_set; }; struct dpp_authentication { void *msg_ctx; + u8 peer_version; const struct dpp_curve_params *curve; struct dpp_bootstrap_info *peer_bi; struct dpp_bootstrap_info *own_bi; @@ -169,6 +182,7 @@ struct dpp_authentication { u8 waiting_pubkey_hash[SHA256_MAC_LEN]; int response_pending; enum dpp_status_error auth_resp_status; + enum dpp_status_error conf_resp_status; u8 peer_mac_addr[ETH_ALEN]; u8 i_nonce[DPP_MAX_NONCE_LEN]; u8 r_nonce[DPP_MAX_NONCE_LEN]; @@ -204,6 +218,8 @@ struct dpp_authentication { u8 allowed_roles; int configurator; int remove_on_tx_status; + int connect_on_tx_status; + int waiting_conf_result; int auth_success; struct wpabuf *conf_req; const struct wpabuf *conf_resp; /* owned by GAS server */ @@ -336,6 +352,7 @@ enum dpp_test_behavior { DPP_TEST_STOP_AT_AUTH_RESP = 88, DPP_TEST_STOP_AT_AUTH_CONF = 89, DPP_TEST_STOP_AT_CONF_REQ = 90, + DPP_TEST_REJECT_CONFIG = 91, }; extern enum dpp_test_behavior dpp_test; @@ -382,13 +399,28 @@ int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr, const u8 *attr_start, size_t attr_len); int dpp_notify_new_qr_code(struct dpp_authentication *auth, struct dpp_bootstrap_info *peer_bi); +struct dpp_configuration * dpp_configuration_alloc(const char *type); +int dpp_akm_psk(enum dpp_akm akm); +int dpp_akm_sae(enum dpp_akm akm); +int dpp_akm_legacy(enum dpp_akm akm); +int dpp_akm_dpp(enum dpp_akm akm); +int dpp_akm_ver2(enum dpp_akm akm); +int dpp_configuration_valid(const struct dpp_configuration *conf); void dpp_configuration_free(struct dpp_configuration *conf); +int dpp_set_configurator(struct dpp_global *dpp, void *msg_ctx, + struct dpp_authentication *auth, + const char *cmd); void dpp_auth_deinit(struct dpp_authentication *auth); struct wpabuf * dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start, size_t attr_len); int dpp_conf_resp_rx(struct dpp_authentication *auth, const struct wpabuf *resp); +enum dpp_status_error dpp_conf_result_rx(struct dpp_authentication *auth, + const u8 *hdr, + const u8 *attr_start, size_t attr_len); +struct wpabuf * dpp_build_conf_result(struct dpp_authentication *auth, + enum dpp_status_error status); struct wpabuf * dpp_alloc_msg(enum dpp_public_action_frame_type type, size_t len); const u8 * dpp_get_attr(const u8 *buf, size_t len, u16 req_id, u16 *ret_len); @@ -432,4 +464,42 @@ void dpp_pkex_free(struct dpp_pkex *pkex); char * dpp_corrupt_connector_signature(const char *connector); + +struct dpp_pfs { + struct crypto_ecdh *ecdh; + const struct dpp_curve_params *curve; + struct wpabuf *ie; + struct wpabuf *secret; +}; + +struct dpp_pfs * dpp_pfs_init(const u8 *net_access_key, + size_t net_access_key_len); +int dpp_pfs_process(struct dpp_pfs *pfs, const u8 *peer_ie, size_t peer_ie_len); +void dpp_pfs_free(struct dpp_pfs *pfs); + +struct dpp_bootstrap_info * dpp_add_qr_code(struct dpp_global *dpp, + const char *uri); +int dpp_bootstrap_gen(struct dpp_global *dpp, const char *cmd); +struct dpp_bootstrap_info * +dpp_bootstrap_get_id(struct dpp_global *dpp, unsigned int id); +int dpp_bootstrap_remove(struct dpp_global *dpp, const char *id); +struct dpp_bootstrap_info * +dpp_pkex_finish(struct dpp_global *dpp, struct dpp_pkex *pkex, const u8 *peer, + unsigned int freq); +const char * dpp_bootstrap_get_uri(struct dpp_global *dpp, unsigned int id); +int dpp_bootstrap_info(struct dpp_global *dpp, int id, + char *reply, int reply_size); +void dpp_bootstrap_find_pair(struct dpp_global *dpp, const u8 *i_bootstrap, + const u8 *r_bootstrap, + struct dpp_bootstrap_info **own_bi, + struct dpp_bootstrap_info **peer_bi); +int dpp_configurator_add(struct dpp_global *dpp, const char *cmd); +int dpp_configurator_remove(struct dpp_global *dpp, const char *id); +int dpp_configurator_get_key_id(struct dpp_global *dpp, unsigned int id, + char *buf, size_t buflen); +struct dpp_global * dpp_global_init(void); +void dpp_global_clear(struct dpp_global *dpp); +void dpp_global_deinit(struct dpp_global *dpp); + +#endif /* CONFIG_DPP */ #endif /* DPP_H */ diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c index 871536f..e42a327 100644 --- a/src/common/ieee802_11_common.c +++ b/src/common/ieee802_11_common.c @@ -270,6 +270,10 @@ static int ieee802_11_parse_extension(const u8 *pos, size_t elen, elems->password_id = pos; elems->password_id_len = elen; break; + case WLAN_EID_EXT_HE_CAPABILITIES: + elems->he_capabilities = pos; + elems->he_capabilities_len = elen; + break; case WLAN_EID_EXT_OCV_OCI: elems->oci = pos; elems->oci_len = elen; @@ -299,29 +303,17 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, struct ieee802_11_elems *elems, int show_errors) { - size_t left = len; - const u8 *pos = start; + const struct element *elem; int unknown = 0; os_memset(elems, 0, sizeof(*elems)); - while (left >= 2) { - u8 id, elen; + if (!start) + return ParseOK; - id = *pos++; - elen = *pos++; - left -= 2; - - if (elen > left) { - if (show_errors) { - wpa_printf(MSG_DEBUG, "IEEE 802.11 element " - "parse failed (id=%d elen=%d " - "left=%lu)", - id, elen, (unsigned long) left); - wpa_hexdump(MSG_MSGDUMP, "IEs", start, len); - } - return ParseFailed; - } + for_each_element(elem, start, len) { + u8 id = elem->id, elen = elem->datalen; + const u8 *pos = elem->data; switch (id) { case WLAN_EID_SSID: @@ -469,8 +461,7 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, elems->mic = pos; elems->mic_len = elen; /* after mic everything is encrypted, so stop. */ - left = elen; - break; + goto done; case WLAN_EID_MULTI_BAND: if (elems->mb_ies.nof_ies >= MAX_NOF_MB_IES_SUPPORTED) { wpa_printf(MSG_MSGDUMP, @@ -529,35 +520,33 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, id, elen); break; } - - left -= elen; - pos += elen; } - if (left) + if (!for_each_element_completed(elem, start, len)) { + if (show_errors) { + wpa_printf(MSG_DEBUG, + "IEEE 802.11 element parse failed @%d", + (int) (start + len - (const u8 *) elem)); + wpa_hexdump(MSG_MSGDUMP, "IEs", start, len); + } return ParseFailed; + } +done: return unknown ? ParseUnknown : ParseOK; } int ieee802_11_ie_count(const u8 *ies, size_t ies_len) { + const struct element *elem; int count = 0; - const u8 *pos, *end; if (ies == NULL) return 0; - pos = ies; - end = ies + ies_len; - - while (end - pos >= 2) { - if (2 + pos[1] > end - pos) - break; + for_each_element(elem, ies, ies_len) count++; - pos += 2 + pos[1]; - } return count; } @@ -567,24 +556,17 @@ struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len, u32 oui_type) { struct wpabuf *buf; - const u8 *end, *pos, *ie; - - pos = ies; - end = ies + ies_len; - ie = NULL; + const struct element *elem, *found = NULL; - while (end - pos > 1) { - if (2 + pos[1] > end - pos) - return NULL; - if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && - WPA_GET_BE32(&pos[2]) == oui_type) { - ie = pos; + for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, ies_len) { + if (elem->datalen >= 4 && + WPA_GET_BE32(elem->data) == oui_type) { + found = elem; break; } - pos += 2 + pos[1]; } - if (ie == NULL) + if (!found) return NULL; /* No specified vendor IE found */ buf = wpabuf_alloc(ies_len); @@ -595,13 +577,9 @@ struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len, * There may be multiple vendor IEs in the message, so need to * concatenate their data fields. */ - while (end - pos > 1) { - if (2 + pos[1] > end - pos) - break; - if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && - WPA_GET_BE32(&pos[2]) == oui_type) - wpabuf_put_data(buf, pos + 6, pos[1] - 4); - pos += 2 + pos[1]; + for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, ies_len) { + if (elem->datalen >= 4 && WPA_GET_BE32(elem->data) == oui_type) + wpabuf_put_data(buf, elem->data + 4, elem->datalen - 4); } return buf; @@ -1340,27 +1318,27 @@ const char * fc2str(u16 fc) int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf, size_t ies_len) { + const struct element *elem; + os_memset(info, 0, sizeof(*info)); - while (ies_buf && ies_len >= 2 && - info->nof_ies < MAX_NOF_MB_IES_SUPPORTED) { - size_t len = 2 + ies_buf[1]; + if (!ies_buf) + return 0; - if (len > ies_len) { - wpa_hexdump(MSG_DEBUG, "Truncated IEs", - ies_buf, ies_len); - return -1; - } + for_each_element_id(elem, WLAN_EID_MULTI_BAND, ies_buf, ies_len) { + if (info->nof_ies >= MAX_NOF_MB_IES_SUPPORTED) + return 0; - if (ies_buf[0] == WLAN_EID_MULTI_BAND) { - wpa_printf(MSG_DEBUG, "MB IE of %zu bytes found", len); - info->ies[info->nof_ies].ie = ies_buf + 2; - info->ies[info->nof_ies].ie_len = ies_buf[1]; - info->nof_ies++; - } + wpa_printf(MSG_DEBUG, "MB IE of %u bytes found", + elem->datalen + 2); + info->ies[info->nof_ies].ie = elem->data; + info->ies[info->nof_ies].ie_len = elem->datalen; + info->nof_ies++; + } - ies_len -= len; - ies_buf += len; + if (!for_each_element_completed(elem, ies_buf, ies_len)) { + wpa_hexdump(MSG_DEBUG, "Truncated IEs", ies_buf, ies_len); + return -1; } return 0; @@ -1483,22 +1461,13 @@ size_t global_op_class_size = ARRAY_SIZE(global_op_class); */ const u8 * get_ie(const u8 *ies, size_t len, u8 eid) { - const u8 *end; + const struct element *elem; if (!ies) return NULL; - end = ies + len; - - while (end - ies > 1) { - if (2 + ies[1] > end - ies) - break; - - if (ies[0] == eid) - return ies; - - ies += 2 + ies[1]; - } + for_each_element_id(elem, eid, ies, len) + return &elem->id; return NULL; } @@ -1516,23 +1485,13 @@ const u8 * get_ie(const u8 *ies, size_t len, u8 eid) */ const u8 * get_ie_ext(const u8 *ies, size_t len, u8 ext) { - const u8 *end; + const struct element *elem; if (!ies) return NULL; - end = ies + len; - - while (end - ies > 1) { - if (2 + ies[1] > end - ies) - break; - - if (ies[0] == WLAN_EID_EXTENSION && ies[1] >= 1 && - ies[2] == ext) - return ies; - - ies += 2 + ies[1]; - } + for_each_element_extid(elem, ext, ies, len) + return &elem->id; return NULL; } @@ -1540,16 +1499,12 @@ const u8 * get_ie_ext(const u8 *ies, size_t len, u8 ext) const u8 * get_vendor_ie(const u8 *ies, size_t len, u32 vendor_type) { - const u8 *pos = ies, *end = ies + len; - - while (end - pos > 1) { - if (2 + pos[1] > end - pos) - break; + const struct element *elem; - if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && - vendor_type == WPA_GET_BE32(&pos[2])) - return pos; - pos += 2 + pos[1]; + for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, len) { + if (elem->datalen >= 4 && + vendor_type == WPA_GET_BE32(elem->data)) + return &elem->id; } return NULL; diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h index ce3bb8b..d41bd39 100644 --- a/src/common/ieee802_11_common.h +++ b/src/common/ieee802_11_common.h @@ -12,6 +12,12 @@ #include "defs.h" #include "ieee802_11_defs.h" +struct element { + u8 id; + u8 datalen; + u8 data[]; +} STRUCT_PACKED; + struct hostapd_hw_modes; #define MAX_NOF_MB_IES_SUPPORTED 5 @@ -87,6 +93,7 @@ struct ieee802_11_elems { const u8 *password_id; const u8 *oci; const u8 *multi_ap; + const u8 *he_capabilities; u8 ssid_len; u8 supp_rates_len; @@ -135,6 +142,7 @@ struct ieee802_11_elems { u8 password_id_len; u8 oci_len; u8 multi_ap_len; + u8 he_capabilities_len; struct mb_ies_info mb_ies; }; @@ -214,4 +222,51 @@ int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep, int ieee802_11_ext_capab(const u8 *ie, unsigned int capab); +/* element iteration helpers */ +#define for_each_element(_elem, _data, _datalen) \ + for (_elem = (const struct element *) (_data); \ + (const u8 *) (_data) + (_datalen) - (const u8 *) _elem >= \ + (int) sizeof(*_elem) && \ + (const u8 *) (_data) + (_datalen) - (const u8 *) _elem >= \ + (int) sizeof(*_elem) + _elem->datalen; \ + _elem = (const struct element *) (_elem->data + _elem->datalen)) + +#define for_each_element_id(element, _id, data, datalen) \ + for_each_element(element, data, datalen) \ + if (element->id == (_id)) + +#define for_each_element_extid(element, extid, _data, _datalen) \ + for_each_element(element, _data, _datalen) \ + if (element->id == WLAN_EID_EXTENSION && \ + element->datalen > 0 && \ + element->data[0] == (extid)) + +#define for_each_subelement(sub, element) \ + for_each_element(sub, (element)->data, (element)->datalen) + +#define for_each_subelement_id(sub, id, element) \ + for_each_element_id(sub, id, (element)->data, (element)->datalen) + +#define for_each_subelement_extid(sub, extid, element) \ + for_each_element_extid(sub, extid, (element)->data, (element)->datalen) + +/** + * for_each_element_completed - Determine if element parsing consumed all data + * @element: Element pointer after for_each_element() or friends + * @data: Same data pointer as passed to for_each_element() or friends + * @datalen: Same data length as passed to for_each_element() or friends + * + * This function returns 1 if all the data was parsed or considered + * while walking the elements. Only use this if your for_each_element() + * loop cannot be broken out of, otherwise it always returns 0. + * + * If some data was malformed, this returns %false since the last parsed + * element will not fill the whole remaining data. + */ +static inline int for_each_element_completed(const struct element *element, + const void *data, size_t datalen) +{ + return (const u8 *) element == (const u8 *) data + datalen; +} + #endif /* IEEE802_11_COMMON_H */ diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index cc512c6..adaa893 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -334,7 +334,7 @@ #define WLAN_EID_LOCATION_PARAMETERS 82 #define WLAN_EID_NONTRANSMITTED_BSSID_CAPA 83 #define WLAN_EID_SSID_LIST 84 -#define WLAN_EID_MLTIPLE_BSSID_INDEX 85 +#define WLAN_EID_MULTIPLE_BSSID_INDEX 85 #define WLAN_EID_FMS_DESCRIPTOR 86 #define WLAN_EID_FMS_REQUEST 87 #define WLAN_EID_FMS_RESPONSE 88 @@ -1444,7 +1444,9 @@ enum wmm_ac { /* WNM-Notification WFA vendors specific subtypes */ #define HS20_WNM_SUB_REM_NEEDED 0 #define HS20_WNM_DEAUTH_IMMINENT_NOTICE 1 -#define HS20_WNM_T_C_ACCEPTANCE 2 +#define WFA_WNM_NOTIF_SUBELEM_NON_PREF_CHAN_REPORT 2 +#define WFA_WNM_NOTIF_SUBELEM_CELL_DATA_CAPA 3 +#define HS20_WNM_T_C_ACCEPTANCE 4 #define HS20_DEAUTH_REASON_CODE_BSS 0 #define HS20_DEAUTH_REASON_CODE_ESS 1 @@ -1533,12 +1535,6 @@ enum mbo_transition_reject_reason { MBO_TRANSITION_REJECT_REASON_SERVICES = 6, }; -/* MBO v0.0_r19, 4.4: WNM-Notification vendor subelements */ -enum wfa_wnm_notif_subelem_id { - WFA_WNM_NOTIF_SUBELEM_NON_PREF_CHAN_REPORT = 2, - WFA_WNM_NOTIF_SUBELEM_CELL_DATA_CAPA = 3, -}; - /* MBO v0.0_r27, 4.3: MBO ANQP-elements */ #define MBO_ANQP_OUI_TYPE 0x12 #define MBO_ANQP_SUBTYPE_QUERY_LIST 1 diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h index 6f5b87e..c34a3bc 100644 --- a/src/common/qca-vendor.h +++ b/src/common/qca-vendor.h @@ -507,6 +507,23 @@ enum qca_radiotap_vendor_ids { * @QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_AKMS: This command is used to query * the supported AKM suite selectorss from the driver. It returns the list * of supported AKMs in the attribute NL80211_ATTR_AKM_SUITES. + * @QCA_NL80211_VENDOR_SUBCMD_GET_FW_STATE: This command is used to get firmware + * state from the driver. It returns the firmware state in the attribute + * QCA_WLAN_VENDOR_ATTR_FW_STATE. + * @QCA_NL80211_VENDOR_SUBCMD_PEER_STATS_CACHE_FLUSH: This vendor subcommand + * is used by the driver to flush per-peer cached statistics to user space + * application. This interface is used as an event from the driver to + * user space application. Attributes for this event are specified in + * enum qca_wlan_vendor_attr_peer_stats_cache_params. + * QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_DATA attribute is expected to be + * sent in the event. + * @QCA_NL80211_VENDOR_SUBCMD_MPTA_HELPER_CONFIG: This sub command is used to + * improve the success rate of Zigbee joining network. + * Due to PTA master limitation, Zigbee joining network success rate is + * low while WLAN is working. The WLAN driver needs to configure some + * parameters including Zigbee state and specific WLAN periods to enhance + * PTA master. All these parameters are delivered by the attributes + * defined in enum qca_mpta_helper_vendor_attr. */ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0, @@ -672,6 +689,9 @@ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_THROUGHPUT_CHANGE_EVENT = 174, QCA_NL80211_VENDOR_SUBCMD_COEX_CONFIG = 175, QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_AKMS = 176, + QCA_NL80211_VENDOR_SUBCMD_GET_FW_STATE = 177, + QCA_NL80211_VENDOR_SUBCMD_PEER_STATS_CACHE_FLUSH = 178, + QCA_NL80211_VENDOR_SUBCMD_MPTA_HELPER_CONFIG = 179, }; enum qca_wlan_vendor_attr { @@ -858,6 +878,12 @@ enum qca_wlan_vendor_attr { * for each antenna ID, and application extract them in user space. */ QCA_WLAN_VENDOR_ATTR_CHAIN_EVM = 41, + /* + * Used in QCA_NL80211_VENDOR_SUBCMD_GET_FW_STATE command to report + * wlan firmware current state. FW state is an unsigned 8 bit value, + * one of the values in enum qca_wlan_vendor_attr_fw_state. + */ + QCA_WLAN_VENDOR_ATTR_FW_STATE = 42, /* keep last */ QCA_WLAN_VENDOR_ATTR_AFTER_LAST, @@ -869,6 +895,49 @@ enum qca_roaming_policy { QCA_ROAMING_ALLOWED_WITHIN_ESS, }; +/** + * enum qca_roam_reason - Represents the reason codes for roaming. Used by + * QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REASON. + * + * @QCA_ROAM_REASON_UNKNOWN: Any reason that do not classify under the below + * reasons. + * + * @QCA_ROAM_REASON_PER: Roam triggered when packet error rates (PER) breached + * the configured threshold. + * + * @QCA_ROAM_REASON_BEACON_MISS: Roam triggered due to the continuous configured + * beacon misses from the then connected AP. + * + * @QCA_ROAM_REASON_POOR_RSSI: Roam triggered due to the poor RSSI reported + * by the connected AP. + * + * @QCA_ROAM_REASON_BETTER_RSSI: Roam triggered for finding a BSS with a better + * RSSI than the connected BSS. Here the RSSI of the current BSS is not poor. + * + * @QCA_ROAM_REASON_CONGESTION: Roam triggered considering the connected channel + * or environment being very noisy or congested. + * + * @QCA_ROAM_REASON_EXPLICIT_REQUEST: Roam triggered due to an explicit request + * from the user (user space). + * + * @QCA_ROAM_REASON_BTM: Roam triggered due to BTM Request frame received from + * the connected AP. + * + * @QCA_ROAM_REASON_BSS_LOAD: Roam triggered due to the channel utilization + * breaching out the configured threshold. + */ +enum qca_roam_reason { + QCA_ROAM_REASON_UNKNOWN, + QCA_ROAM_REASON_PER, + QCA_ROAM_REASON_BEACON_MISS, + QCA_ROAM_REASON_POOR_RSSI, + QCA_ROAM_REASON_BETTER_RSSI, + QCA_ROAM_REASON_CONGESTION, + QCA_ROAM_REASON_USER_TRIGGER, + QCA_ROAM_REASON_BTM, + QCA_ROAM_REASON_BSS_LOAD, +}; + enum qca_wlan_vendor_attr_roam_auth { QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_INVALID = 0, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID, @@ -911,6 +980,11 @@ enum qca_wlan_vendor_attr_roam_auth { * doing subsequent ERP based connections in the same realm. */ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_FILS_ERP_NEXT_SEQ_NUM = 13, + /* A 16-bit unsigned value representing the reasons for the roaming. + * Defined by enum qca_roam_reason. + */ + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REASON = 14, + /* keep last */ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX = @@ -1009,6 +1083,7 @@ enum qca_wlan_vendor_acs_hw_mode { * only OCE STA-CFON functionalities. * @QCA_WLAN_VENDOR_FEATURE_SELF_MANAGED_REGULATORY: Device supports self * managed regulatory. + * @QCA_WLAN_VENDOR_FEATURE_TWT: Device supports TWT (Target Wake Time). * @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits */ enum qca_wlan_vendor_features { @@ -1020,6 +1095,7 @@ enum qca_wlan_vendor_features { QCA_WLAN_VENDOR_FEATURE_OCE_AP = 5, QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON = 6, QCA_WLAN_VENDOR_FEATURE_SELF_MANAGED_REGULATORY = 7, + QCA_WLAN_VENDOR_FEATURE_TWT = 8, NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */ }; @@ -2453,6 +2529,18 @@ enum qca_wlan_vendor_attr_dmg_rf_sector_type { }; /** + * enum qca_wlan_vendor_attr_fw_state - State of firmware + * + * @QCA_WLAN_VENDOR_ATTR_FW_STATE_ERROR: FW is in bad state + * @QCA_WLAN_VENDOR_ATTR_FW_STATE_ACTIVE: FW is active + */ +enum qca_wlan_vendor_attr_fw_state { + QCA_WLAN_VENDOR_ATTR_FW_STATE_ERROR, + QCA_WLAN_VENDOR_ATTR_FW_STATE_ACTIVE, + QCA_WLAN_VENDOR_ATTR_FW_STATE_MAX +}; + +/** * BRP antenna limit mode * * @QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_DISABLE: Disable BRP force @@ -3196,6 +3284,8 @@ enum qca_wlan_vendor_attr_roaming_config_params { QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS = 18, QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID = 19, QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID = 20, + /* Flag attribute indicates this BSSID blacklist as a hint */ + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_HINT = 21, /* keep last */ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_AFTER_LAST, @@ -4886,6 +4976,10 @@ enum qca_wlan_vendor_attr_offloaded_packets { QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_DST_MAC_ADDR, /* Unsigned 32-bit value, in milli seconds */ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_PERIOD, + /* This optional unsigned 16-bit attribute is used for specifying + * ethernet protocol type. If not specified ethertype defaults to IPv4. + */ + QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_ETHER_PROTO_TYPE, /* keep last */ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_AFTER_LAST, @@ -5530,6 +5624,16 @@ enum qca_wlan_he_om_ctrl_ch_bw { * @QCA_WLAN_VENDOR_ATTR_HE_OMI_TX_NSTS: Mandatory 8-bit unsigned value * indicates the maximum number of space-time streams, NSTS, that * the STA supports in transmission and is set to NSTS - 1. + * + * @QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DATA_DISABLE: 8-bit unsigned value + * combined with the UL MU Disable subfield and the recipient's setting + * of the OM Control UL MU Data Disable RX Support subfield in the HE MAC + * capabilities to determine which HE TB PPDUs are possible by the + * STA to transmit. + * 0 - UL MU data operations are enabled by the STA. + * 1 - Determine which HE TB PPDU types are allowed by the STA if UL MU disable + * bit is not set, else UL MU Tx is suspended. + * */ enum qca_wlan_vendor_attr_he_omi_tx { QCA_WLAN_VENDOR_ATTR_HE_OMI_INVALID = 0, @@ -5537,6 +5641,7 @@ enum qca_wlan_vendor_attr_he_omi_tx { QCA_WLAN_VENDOR_ATTR_HE_OMI_CH_BW = 2, QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DISABLE = 3, QCA_WLAN_VENDOR_ATTR_HE_OMI_TX_NSTS = 4, + QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DATA_DISABLE = 5, /* keep last */ QCA_WLAN_VENDOR_ATTR_HE_OMI_AFTER_LAST, @@ -5808,6 +5913,25 @@ enum qca_wlan_vendor_attr_wifi_test_config { */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_HTC_HE_SUPP = 34, + /* 8-bit unsigned value to configure VHT support in 2.4G band. + * This attribute is used to configure the testbed device. + * 1-enable, 0-disable + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_2G_VHT = 35, + + /* 8-bit unsigned value to configure HE testbed defaults. + * This attribute is used to configure the testbed device. + * 1-set the device HE capabilities to testbed defaults. + * 0-reset the device HE capabilities to supported config. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_SET_HE_TESTBED_DEFAULTS = 36, + + /* 8-bit unsigned value to configure TWT request support. + * This attribute is used to configure the testbed device. + * 1-enable, 0-disable. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TWT_REQ_SUPPORT = 37, + /* keep last */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX = @@ -6290,13 +6414,35 @@ enum qca_coex_config_profiles { QCA_WIFI_SAP_CLASS_3_MGMT = 7, QCA_WIFI_SAP_DATA = 8, QCA_WIFI_SAP_ALL = 9, + QCA_WIFI_CASE_MAX = 31, /* 32 - 63 corresponds to BT */ QCA_BT_A2DP = 32, QCA_BT_BLE = 33, QCA_BT_SCO = 34, + QCA_BT_CASE_MAX = 63, /* 64 - 95 corresponds to Zigbee */ QCA_ZB_LOW = 64, - QCA_ZB_HIGH = 65 + QCA_ZB_HIGH = 65, + QCA_ZB_CASE_MAX = 95, + /* 0xff is default value if the u8 profile value is not set. */ + QCA_COEX_CONFIG_PROFILE_DEFAULT_VALUE = 255 +}; + +/** + * enum qca_vendor_attr_coex_config_types - Coex configurations types. + * This enum defines the valid set of values of coex configuration types. These + * values may used by attribute + * %QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_CONFIG_TYPE. + * + * @QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_COEX_RESET: Reset all the + * weights to default values. + * @QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_COEX_START: Start to config + * weights with configurability value. + */ +enum qca_vendor_attr_coex_config_types { + QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_COEX_RESET = 1, + QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_COEX_START = 2, }; /** @@ -6328,6 +6474,80 @@ enum qca_vendor_attr_coex_config { }; /** + * enum qca_vendor_attr_coex_config_three_way - Specifies vendor coex config + * attributes + * Attributes for data used by QCA_NL80211_VENDOR_SUBCMD_COEX_CONFIG + * + * QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_CONFIG_TYPE: u32 attribute. + * Indicate config type. + * The config types are 32-bit values from qca_vendor_attr_coex_config_types + * + * @QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_1: u32 attribute. + * Indicate the Priority 1 profiles. + * The profiles are 8-bit values from enum qca_coex_config_profiles. + * In same priority level, maximum to 4 profiles can be set here. + * @QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_2: u32 attribute. + * Indicate the Priority 2 profiles. + * The profiles are 8-bit values from enum qca_coex_config_profiles. + * In same priority level, maximum to 4 profiles can be set here. + * @QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_3: u32 attribute. + * Indicate the Priority 3 profiles. + * The profiles are 8-bit values from enum qca_coex_config_profiles. + * In same priority level, maximum to 4 profiles can be set here. + * @QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_4: u32 attribute. + * Indicate the Priority 4 profiles. + * The profiles are 8-bit values from enum qca_coex_config_profiles. + * In same priority level, maximum to 4 profiles can be set here. + * NOTE: + * Limitations for QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_x priority + * arrangement: + * 1: In the same u32 attribute (priority x), the profiles enum values own + * same priority level. + * 2: 0xff is default value if the u8 profile value is not set. + * 3: max to 4 rules/profiles in same priority level. + * 4: max to 4 priority level (priority 1 - priority 4) + * 5: one priority level only supports one scenario from WLAN/BT/ZB, + * hybrid rules not support. + * 6: if WMI_COEX_CONFIG_THREE_WAY_COEX_RESET called, priority x will + * remain blank to reset all parameters. + * For example: + * + * If the attributes as follow: + * priority 1: + * ------------------------------------ + * | 0xff | 0 | 1 | 2 | + * ------------------------------------ + * priority 2: + * ------------------------------------- + * | 0xff | 0xff | 0xff | 32 | + * ------------------------------------- + * priority 3: + * ------------------------------------- + * | 0xff | 0xff | 0xff | 65 | + * ------------------------------------- + * then it means: + * 1: WIFI_STA_DISCOVERY, WIFI_STA_CLASS_3_MGMT and WIFI_STA_CONNECTION + * owns same priority level. + * 2: WIFI_STA_DISCOVERY, WIFI_STA_CLASS_3_MGMT and WIFI_STA_CONNECTION + * has priority over BT_A2DP and ZB_HIGH. + * 3: BT_A2DP has priority over ZB_HIGH. + */ + +enum qca_vendor_attr_coex_config_three_way { + QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_INVALID = 0, + QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_CONFIG_TYPE = 1, + QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_1 = 2, + QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_2 = 3, + QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_3 = 4, + QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_4 = 5, + + /* Keep last */ + QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_AFTER_LAST, + QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_MAX = + QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_AFTER_LAST - 1, +}; + +/** * enum qca_wlan_vendor_attr_link_properties - Represent the link properties. * * @QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_MAC_ADDR: MAC address of the peer @@ -6349,4 +6569,144 @@ enum qca_wlan_vendor_attr_link_properties { QCA_VENDOR_ATTR_LINK_PROPERTIES_AFTER_LAST - 1, }; +/** + * enum qca_vendor_attr_peer_stats_cache_type - Represents peer stats cache type + * This enum defines the valid set of values of peer stats cache types. These + * values are used by attribute + * %QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_TYPE. + * + * @QCA_WLAN_VENDOR_ATTR_PEER_TX_RATE_STATS: Represents peer TX rate statistics + * @QCA_WLAN_VENDOR_ATTR_PEER_RX_RATE_STATS: Represents peer RX rate statistics + * @QCA_WLAN_VENDOR_ATTR_PEER_TX_SOJOURN_STATS: Represents peer TX sojourn + * statistics + */ +enum qca_vendor_attr_peer_stats_cache_type { + QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_TYPE_INVALID = 0, + + QCA_WLAN_VENDOR_ATTR_PEER_TX_RATE_STATS, + QCA_WLAN_VENDOR_ATTR_PEER_RX_RATE_STATS, + QCA_WLAN_VENDOR_ATTR_PEER_TX_SOJOURN_STATS, +}; + +/** + * enum qca_wlan_vendor_attr_peer_stats_cache_params - This enum defines + * attributes required for QCA_NL80211_VENDOR_SUBCMD_PEER_STATS_CACHE_FLUSH + * Information in these attributes is used to flush peer rate statistics from + * the driver to user application. + * + * @QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_TYPE: Unsigned 32-bit attribute + * Indicate peer statistics cache type. + * The statistics types are 32-bit values from + * enum qca_vendor_attr_peer_stats_cache_type. + * @QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_MAC: Unsigned 8-bit array + * of size 6 octets, representing the peer MAC address. + * @QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_DATA: Opaque data attribute + * containing buffer of statistics to send to application layer entity. + * @QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_COOKIE: Unsigned 64-bit attribute + * representing a cookie for peer unique session. + */ +enum qca_wlan_vendor_attr_peer_stats_cache_params { + QCA_WLAN_VENDOR_ATTR_PEER_STATS_INVALID = 0, + + QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_TYPE = 1, + QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_MAC = 2, + QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_DATA = 3, + QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_COOKIE = 4, + + /* Keep last */ + QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_LAST, + QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_MAX = + QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_LAST - 1 +}; + +/** + * enum qca_mpta_helper_attr_zigbee_state - Current Zigbee state + * This enum defines all the possible states of Zigbee, which can be + * delivered in the QCA_MPTA_HELPER_VENDOR_ATTR_ZIGBEE_STATE attribute. + * + * @ZIGBEE_IDLE: Zigbee in idle state + * @ZIGBEE_FORM_NETWORK: Zigbee forming network + * @ZIGBEE_WAIT_JOIN: Zigbee waiting for joining network + * @ZIGBEE_JOIN: Zigbee joining network + * @ZIGBEE_NETWORK_UP: Zigbee network is up + * @ZIGBEE_HMI: Zigbee in HMI mode + */ +enum qca_mpta_helper_attr_zigbee_state { + ZIGBEE_IDLE = 0, + ZIGBEE_FORM_NETWORK = 1, + ZIGBEE_WAIT_JOIN = 2, + ZIGBEE_JOIN = 3, + ZIGBEE_NETWORK_UP = 4, + ZIGBEE_HMI = 5, +}; + +/* + * enum qca_mpta_helper_vendor_attr - Attributes used in vendor sub-command + * QCA_NL80211_VENDOR_SUBCMD_MPTA_HELPER_CONFIG. + */ +enum qca_mpta_helper_vendor_attr { + QCA_MPTA_HELPER_VENDOR_ATTR_INVALID = 0, + /* Optional attribute used to update Zigbee state. + * enum qca_mpta_helper_attr_zigbee_state. + * NLA_U32 attribute. + */ + QCA_MPTA_HELPER_VENDOR_ATTR_ZIGBEE_STATE = 1, + /* Optional attribute used to configure WLAN duration for Shape-OCS + * during interrupt. + * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_INT_NON_WLAN_DURATION. + * Value range 0 ~ 300 (ms). + * NLA_U32 attribute. + */ + QCA_MPTA_HELPER_VENDOR_ATTR_INT_WLAN_DURATION = 2, + /* Optional attribute used to configure non-WLAN duration for Shape-OCS + * during interrupt. + * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_INT_WLAN_DURATION. + * Value range 0 ~ 300 (ms). + * NLA_U32 attribute. + */ + QCA_MPTA_HELPER_VENDOR_ATTR_INT_NON_WLAN_DURATION = 3, + /* Optional attribute used to configure WLAN duration for Shape-OCS + * monitor period. + * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_MON_NON_WLAN_DURATION. + * Value range 0 ~ 300 (ms) + * NLA_U32 attribute + */ + QCA_MPTA_HELPER_VENDOR_ATTR_MON_WLAN_DURATION = 4, + /* Optional attribute used to configure non-WLAN duration for Shape-OCS + * monitor period. + * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_MON_WLAN_DURATION. + * Value range 0 ~ 300 (ms) + * NLA_U32 attribute + */ + QCA_MPTA_HELPER_VENDOR_ATTR_MON_NON_WLAN_DURATION = 5, + /* Optional attribute used to configure OCS interrupt duration. + * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_MON_OCS_DURATION. + * Value range 1000 ~ 20000 (ms) + * NLA_U32 attribute + */ + QCA_MPTA_HELPER_VENDOR_ATTR_INT_OCS_DURATION = 6, + /* Optional attribute used to configure OCS monitor duration. + * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_INT_OCS_DURATION. + * Value range 1000 ~ 20000 (ms) + * NLA_U32 attribute + */ + QCA_MPTA_HELPER_VENDOR_ATTR_MON_OCS_DURATION = 7, + /* Optional attribute used to notify WLAN firmware the current Zigbee + * channel. + * Value range 11 ~ 26 + * NLA_U32 attribute + */ + QCA_MPTA_HELPER_VENDOR_ATTR_ZIGBEE_CHAN = 8, + /* Optional attribute used to configure WLAN mute duration. + * Value range 0 ~ 400 (ms) + * NLA_U32 attribute + */ + QCA_MPTA_HELPER_VENDOR_ATTR_WLAN_MUTE_DURATION = 9, + + /* keep last */ + QCA_MPTA_HELPER_VENDOR_ATTR_AFTER_LAST, + QCA_MPTA_HELPER_VENDOR_ATTR_MAX = + QCA_MPTA_HELPER_VENDOR_ATTR_AFTER_LAST - 1 +}; + #endif /* QCA_VENDOR_H */ diff --git a/src/common/sae.c b/src/common/sae.c index 981e788..5a50294 100644 --- a/src/common/sae.c +++ b/src/common/sae.c @@ -9,6 +9,7 @@ #include "includes.h" #include "common.h" +#include "utils/const_time.h" #include "crypto/crypto.h" #include "crypto/sha256.h" #include "crypto/random.h" @@ -17,10 +18,33 @@ #include "sae.h" +static int sae_suitable_group(int group) +{ +#ifdef CONFIG_TESTING_OPTIONS + /* Allow all groups for testing purposes in non-production builds. */ + return 1; +#else /* CONFIG_TESTING_OPTIONS */ + /* Enforce REVmd rules on which SAE groups are suitable for production + * purposes: FFC groups whose prime is >= 3072 bits and ECC groups + * defined over a prime field whose prime is >= 256 bits. Furthermore, + * ECC groups defined over a characteristic 2 finite field and ECC + * groups with a co-factor greater than 1 are not suitable. */ + return group == 19 || group == 20 || group == 21 || + group == 28 || group == 29 || group == 30 || + group == 15 || group == 16 || group == 17 || group == 18; +#endif /* CONFIG_TESTING_OPTIONS */ +} + + int sae_set_group(struct sae_data *sae, int group) { struct sae_temporary_data *tmp; + if (!sae_suitable_group(group)) { + wpa_printf(MSG_DEBUG, "SAE: Reject unsuitable group %d", group); + return -1; + } + sae_clear_data(sae); tmp = sae->tmp = os_zalloc(sizeof(*tmp)); if (tmp == NULL) @@ -208,12 +232,14 @@ get_rand_1_to_p_1(const u8 *prime, size_t prime_len, size_t prime_bits, static int is_quadratic_residue_blind(struct sae_data *sae, const u8 *prime, size_t bits, - const struct crypto_bignum *qr, - const struct crypto_bignum *qnr, + const u8 *qr, const u8 *qnr, const struct crypto_bignum *y_sqr) { - struct crypto_bignum *r, *num; + struct crypto_bignum *r, *num, *qr_or_qnr = NULL; int r_odd, check, res = -1; + u8 qr_or_qnr_bin[SAE_MAX_ECC_PRIME_LEN]; + size_t prime_len = sae->tmp->prime_len; + unsigned int mask; /* * Use the blinding technique to mask y_sqr while determining @@ -224,7 +250,7 @@ static int is_quadratic_residue_blind(struct sae_data *sae, * r = a random number between 1 and p-1, inclusive * num = (v * r * r) modulo p */ - r = get_rand_1_to_p_1(prime, sae->tmp->prime_len, bits, &r_odd); + r = get_rand_1_to_p_1(prime, prime_len, bits, &r_odd); if (!r) return -1; @@ -234,50 +260,51 @@ static int is_quadratic_residue_blind(struct sae_data *sae, crypto_bignum_mulmod(num, r, sae->tmp->prime, num) < 0) goto fail; - if (r_odd) { - /* - * num = (num * qr) module p - * LGR(num, p) = 1 ==> quadratic residue - */ - if (crypto_bignum_mulmod(num, qr, sae->tmp->prime, num) < 0) - goto fail; - check = 1; - } else { - /* - * num = (num * qnr) module p - * LGR(num, p) = -1 ==> quadratic residue - */ - if (crypto_bignum_mulmod(num, qnr, sae->tmp->prime, num) < 0) - goto fail; - check = -1; - } + /* + * Need to minimize differences in handling different cases, so try to + * avoid branches and timing differences. + * + * If r_odd: + * num = (num * qr) module p + * LGR(num, p) = 1 ==> quadratic residue + * else: + * num = (num * qnr) module p + * LGR(num, p) = -1 ==> quadratic residue + */ + mask = const_time_is_zero(r_odd); + const_time_select_bin(mask, qnr, qr, prime_len, qr_or_qnr_bin); + qr_or_qnr = crypto_bignum_init_set(qr_or_qnr_bin, prime_len); + if (!qr_or_qnr || + crypto_bignum_mulmod(num, qr_or_qnr, sae->tmp->prime, num) < 0) + goto fail; + /* r_odd is 0 or 1; branchless version of check = r_odd ? 1 : -1, */ + check = const_time_select_int(mask, -1, 1); res = crypto_bignum_legendre(num, sae->tmp->prime); if (res == -2) { res = -1; goto fail; } - res = res == check; + /* branchless version of res = res == check + * (res is -1, 0, or 1; check is -1 or 1) */ + mask = const_time_eq(res, check); + res = const_time_select_int(mask, 1, 0); fail: crypto_bignum_deinit(num, 1); crypto_bignum_deinit(r, 1); + crypto_bignum_deinit(qr_or_qnr, 1); return res; } static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed, - const u8 *prime, - const struct crypto_bignum *qr, - const struct crypto_bignum *qnr, - struct crypto_bignum **ret_x_cand) + const u8 *prime, const u8 *qr, const u8 *qnr, + u8 *pwd_value) { - u8 pwd_value[SAE_MAX_ECC_PRIME_LEN]; struct crypto_bignum *y_sqr, *x_cand; int res; size_t bits; - *ret_x_cand = NULL; - wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN); /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */ @@ -286,7 +313,7 @@ static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed, prime, sae->tmp->prime_len, pwd_value, bits) < 0) return -1; if (bits % 8) - buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8); + buf_shift_right(pwd_value, sae->tmp->prime_len, 8 - bits % 8); wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", pwd_value, sae->tmp->prime_len); @@ -297,31 +324,27 @@ static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed, if (!x_cand) return -1; y_sqr = crypto_ec_point_compute_y_sqr(sae->tmp->ec, x_cand); - if (!y_sqr) { - crypto_bignum_deinit(x_cand, 1); + crypto_bignum_deinit(x_cand, 1); + if (!y_sqr) return -1; - } res = is_quadratic_residue_blind(sae, prime, bits, qr, qnr, y_sqr); crypto_bignum_deinit(y_sqr, 1); - if (res <= 0) { - crypto_bignum_deinit(x_cand, 1); - return res; - } - - *ret_x_cand = x_cand; - return 1; + return res; } +/* Returns -1 on fatal failure, 0 if PWE cannot be derived from the provided + * pwd-seed, or 1 if a valid PWE was derived from pwd-seed. */ static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed, struct crypto_bignum *pwe) { u8 pwd_value[SAE_MAX_PRIME_LEN]; size_t bits = sae->tmp->prime_len * 8; u8 exp[1]; - struct crypto_bignum *a, *b; - int res; + struct crypto_bignum *a, *b = NULL; + int res, is_val; + u8 pwd_value_valid; wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN); @@ -333,16 +356,29 @@ static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed, wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", pwd_value, sae->tmp->prime_len); - if (os_memcmp(pwd_value, sae->tmp->dh->prime, sae->tmp->prime_len) >= 0) - { - wpa_printf(MSG_DEBUG, "SAE: pwd-value >= p"); - return 0; - } + /* Check whether pwd-value < p */ + res = const_time_memcmp(pwd_value, sae->tmp->dh->prime, + sae->tmp->prime_len); + /* pwd-value >= p is invalid, so res is < 0 for the valid cases and + * the negative sign can be used to fill the mask for constant time + * selection */ + pwd_value_valid = const_time_fill_msb(res); + + /* If pwd-value >= p, force pwd-value to be < p and perform the + * calculations anyway to hide timing difference. The derived PWE will + * be ignored in that case. */ + pwd_value[0] = const_time_select_u8(pwd_value_valid, pwd_value[0], 0); /* PWE = pwd-value^((p-1)/r) modulo p */ + res = -1; a = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len); + if (!a) + goto fail; + /* This is an optimization based on the used group that does not depend + * on the password in any way, so it is fine to use separate branches + * for this step without constant time operations. */ if (sae->tmp->dh->safe_prime) { /* * r = (p-1)/2 for the group used here, so this becomes: @@ -356,33 +392,34 @@ static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed, b = crypto_bignum_init_set(exp, sizeof(exp)); if (b == NULL || crypto_bignum_sub(sae->tmp->prime, b, b) < 0 || - crypto_bignum_div(b, sae->tmp->order, b) < 0) { - crypto_bignum_deinit(b, 0); - b = NULL; - } + crypto_bignum_div(b, sae->tmp->order, b) < 0) + goto fail; } - if (a == NULL || b == NULL) - res = -1; - else - res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe); - - crypto_bignum_deinit(a, 0); - crypto_bignum_deinit(b, 0); + if (!b) + goto fail; - if (res < 0) { - wpa_printf(MSG_DEBUG, "SAE: Failed to calculate PWE"); - return -1; - } + res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe); + if (res < 0) + goto fail; - /* if (PWE > 1) --> found */ - if (crypto_bignum_is_zero(pwe) || crypto_bignum_is_one(pwe)) { - wpa_printf(MSG_DEBUG, "SAE: PWE <= 1"); - return 0; - } + /* There were no fatal errors in calculations, so determine the return + * value using constant time operations. We get here for number of + * invalid cases which are cleared here after having performed all the + * computation. PWE is valid if pwd-value was less than prime and + * PWE > 1. Start with pwd-value check first and then use constant time + * operations to clear res to 0 if PWE is 0 or 1. + */ + res = const_time_select_u8(pwd_value_valid, 1, 0); + is_val = crypto_bignum_is_zero(pwe); + res = const_time_select_u8(const_time_is_zero(is_val), res, 0); + is_val = crypto_bignum_is_one(pwe); + res = const_time_select_u8(const_time_is_zero(is_val), res, 0); - wpa_printf(MSG_DEBUG, "SAE: PWE found"); - return 1; +fail: + crypto_bignum_deinit(a, 1); + crypto_bignum_deinit(b, 1); + return res; } @@ -431,25 +468,32 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, const u8 *addr[3]; size_t len[3]; size_t num_elem; - u8 dummy_password[32]; - size_t dummy_password_len; + u8 *dummy_password, *tmp_password; int pwd_seed_odd = 0; u8 prime[SAE_MAX_ECC_PRIME_LEN]; size_t prime_len; - struct crypto_bignum *x = NULL, *qr, *qnr; + struct crypto_bignum *x = NULL, *qr = NULL, *qnr = NULL; + u8 x_bin[SAE_MAX_ECC_PRIME_LEN]; + u8 x_cand_bin[SAE_MAX_ECC_PRIME_LEN]; + u8 qr_bin[SAE_MAX_ECC_PRIME_LEN]; + u8 qnr_bin[SAE_MAX_ECC_PRIME_LEN]; size_t bits; - int res; + int res = -1; + u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_* + * mask */ - dummy_password_len = password_len; - if (dummy_password_len > sizeof(dummy_password)) - dummy_password_len = sizeof(dummy_password); - if (random_get_bytes(dummy_password, dummy_password_len) < 0) - return -1; + os_memset(x_bin, 0, sizeof(x_bin)); + + dummy_password = os_malloc(password_len); + tmp_password = os_malloc(password_len); + if (!dummy_password || !tmp_password || + random_get_bytes(dummy_password, password_len) < 0) + goto fail; prime_len = sae->tmp->prime_len; if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime), prime_len) < 0) - return -1; + goto fail; bits = crypto_ec_prime_len_bits(sae->tmp->ec); /* @@ -457,8 +501,10 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, * (qnr) modulo p for blinding purposes during the loop. */ if (get_random_qr_qnr(prime, prime_len, sae->tmp->prime, bits, - &qr, &qnr) < 0) - return -1; + &qr, &qnr) < 0 || + crypto_bignum_to_bin(qr, qr_bin, sizeof(qr_bin), prime_len) < 0 || + crypto_bignum_to_bin(qnr, qnr_bin, sizeof(qnr_bin), prime_len) < 0) + goto fail; wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", password, password_len); @@ -474,7 +520,7 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, */ sae_pwd_seed_key(addr1, addr2, addrs); - addr[0] = password; + addr[0] = tmp_password; len[0] = password_len; num_elem = 1; if (identifier) { @@ -491,9 +537,8 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, * attacks that attempt to determine the number of iterations required * in the loop. */ - for (counter = 1; counter <= k || !x; counter++) { + for (counter = 1; counter <= k || !found; counter++) { u8 pwd_seed[SHA256_MAC_LEN]; - struct crypto_bignum *x_cand; if (counter > 200) { /* This should not happen in practice */ @@ -501,36 +546,45 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, break; } - wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter); + wpa_printf(MSG_DEBUG, "SAE: counter = %03u", counter); + const_time_select_bin(found, dummy_password, password, + password_len, tmp_password); if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem, addr, len, pwd_seed) < 0) break; res = sae_test_pwd_seed_ecc(sae, pwd_seed, - prime, qr, qnr, &x_cand); + prime, qr_bin, qnr_bin, x_cand_bin); + const_time_select_bin(found, x_bin, x_cand_bin, prime_len, + x_bin); + pwd_seed_odd = const_time_select_u8( + found, pwd_seed_odd, + pwd_seed[SHA256_MAC_LEN - 1] & 0x01); + os_memset(pwd_seed, 0, sizeof(pwd_seed)); if (res < 0) goto fail; - if (res > 0 && !x) { - wpa_printf(MSG_DEBUG, - "SAE: Selected pwd-seed with counter %u", - counter); - x = x_cand; - pwd_seed_odd = pwd_seed[SHA256_MAC_LEN - 1] & 0x01; - os_memset(pwd_seed, 0, sizeof(pwd_seed)); + /* Need to minimize differences in handling res == 0 and 1 here + * to avoid differences in timing and instruction cache access, + * so use const_time_select_*() to make local copies of the + * values based on whether this loop iteration was the one that + * found the pwd-seed/x. */ + + /* found is 0 or 0xff here and res is 0 or 1. Bitwise OR of them + * (with res converted to 0/0xff) handles this in constant time. + */ + found |= res * 0xff; + wpa_printf(MSG_DEBUG, "SAE: pwd-seed result %d found=0x%02x", + res, found); + } - /* - * Use a dummy password for the following rounds, if - * any. - */ - addr[0] = dummy_password; - len[0] = dummy_password_len; - } else if (res > 0) { - crypto_bignum_deinit(x_cand, 1); - } + if (!found) { + wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE"); + res = -1; + goto fail; } + x = crypto_bignum_init_set(x_bin, prime_len); if (!x) { - wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE"); res = -1; goto fail; } @@ -543,7 +597,6 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, res = crypto_ec_point_solve_y_coord(sae->tmp->ec, sae->tmp->pwe_ecc, x, pwd_seed_odd); - crypto_bignum_deinit(x, 1); if (res < 0) { /* * This should not happen since we already checked that there @@ -555,27 +608,48 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, fail: crypto_bignum_deinit(qr, 0); crypto_bignum_deinit(qnr, 0); + os_free(dummy_password); + bin_clear_free(tmp_password, password_len); + crypto_bignum_deinit(x, 1); + os_memset(x_bin, 0, sizeof(x_bin)); + os_memset(x_cand_bin, 0, sizeof(x_cand_bin)); return res; } +static int sae_modp_group_require_masking(int group) +{ + /* Groups for which pwd-value is likely to be >= p frequently */ + return group == 22 || group == 23 || group == 24; +} + + static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1, const u8 *addr2, const u8 *password, size_t password_len, const char *identifier) { - u8 counter; + u8 counter, k, sel_counter = 0; u8 addrs[2 * ETH_ALEN]; const u8 *addr[3]; size_t len[3]; size_t num_elem; - int found = 0; - - if (sae->tmp->pwe_ffc == NULL) { - sae->tmp->pwe_ffc = crypto_bignum_init(); - if (sae->tmp->pwe_ffc == NULL) - return -1; - } + u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_* + * mask */ + u8 mask; + struct crypto_bignum *pwe; + size_t prime_len = sae->tmp->prime_len * 8; + u8 *pwe_buf; + + crypto_bignum_deinit(sae->tmp->pwe_ffc, 1); + sae->tmp->pwe_ffc = NULL; + + /* Allocate a buffer to maintain selected and candidate PWE for constant + * time selection. */ + pwe_buf = os_zalloc(prime_len * 2); + pwe = crypto_bignum_init(); + if (!pwe_buf || !pwe) + goto fail; wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", password, password_len); @@ -599,7 +673,9 @@ static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1, len[num_elem] = sizeof(counter); num_elem++; - for (counter = 1; !found; counter++) { + k = sae_modp_group_require_masking(sae->group) ? 40 : 1; + + for (counter = 1; counter <= k || !found; counter++) { u8 pwd_seed[SHA256_MAC_LEN]; int res; @@ -609,20 +685,37 @@ static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1, break; } - wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter); + wpa_printf(MSG_DEBUG, "SAE: counter = %02u", counter); if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem, addr, len, pwd_seed) < 0) break; - res = sae_test_pwd_seed_ffc(sae, pwd_seed, sae->tmp->pwe_ffc); + res = sae_test_pwd_seed_ffc(sae, pwd_seed, pwe); + /* res is -1 for fatal failure, 0 if a valid PWE was not found, + * or 1 if a valid PWE was found. */ if (res < 0) break; - if (res > 0) { - wpa_printf(MSG_DEBUG, "SAE: Use this PWE"); - found = 1; - } + /* Store the candidate PWE into the second half of pwe_buf and + * the selected PWE in the beginning of pwe_buf using constant + * time selection. */ + if (crypto_bignum_to_bin(pwe, pwe_buf + prime_len, prime_len, + prime_len) < 0) + break; + const_time_select_bin(found, pwe_buf, pwe_buf + prime_len, + prime_len, pwe_buf); + sel_counter = const_time_select_u8(found, sel_counter, counter); + mask = const_time_eq_u8(res, 1); + found = const_time_select_u8(found, found, mask); } - return found ? 0 : -1; + if (!found) + goto fail; + + wpa_printf(MSG_DEBUG, "SAE: Use PWE from counter = %02u", sel_counter); + sae->tmp->pwe_ffc = crypto_bignum_init_set(pwe_buf, prime_len); +fail: + crypto_bignum_deinit(pwe, 1); + bin_clear_free(pwe_buf, prime_len * 2); + return sae->tmp->pwe_ffc ? 0 : -1; } @@ -1394,23 +1487,31 @@ int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len) wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", WPA_GET_LE16(data)); - if (sae->tmp == NULL) { + if (!sae->tmp || !sae->peer_commit_scalar || + !sae->tmp->own_commit_scalar) { wpa_printf(MSG_DEBUG, "SAE: Temporary data not yet available"); return -1; } - if (sae->tmp->ec) + if (sae->tmp->ec) { + if (!sae->tmp->peer_commit_element_ecc || + !sae->tmp->own_commit_element_ecc) + return -1; sae_cn_confirm_ecc(sae, data, sae->peer_commit_scalar, sae->tmp->peer_commit_element_ecc, sae->tmp->own_commit_scalar, sae->tmp->own_commit_element_ecc, verifier); - else + } else { + if (!sae->tmp->peer_commit_element_ffc || + !sae->tmp->own_commit_element_ffc) + return -1; sae_cn_confirm_ffc(sae, data, sae->peer_commit_scalar, sae->tmp->peer_commit_element_ffc, sae->tmp->own_commit_scalar, sae->tmp->own_commit_element_ffc, verifier); + } if (os_memcmp_const(verifier, data + 2, SHA256_MAC_LEN) != 0) { wpa_printf(MSG_DEBUG, "SAE: Confirm mismatch"); diff --git a/src/common/sae.h b/src/common/sae.h index 3fbcb58..3eb6e32 100644 --- a/src/common/sae.h +++ b/src/common/sae.h @@ -40,6 +40,8 @@ struct sae_temporary_data { struct crypto_bignum *order_buf; struct wpabuf *anti_clogging_token; char *pw_id; + int vlan_id; + u8 bssid[ETH_ALEN]; }; enum sae_state { diff --git a/src/common/version.h b/src/common/version.h index eb4f313..06fc5e4 100644 --- a/src/common/version.h +++ b/src/common/version.h @@ -9,6 +9,6 @@ #define GIT_VERSION_STR_POSTFIX "" #endif /* GIT_VERSION_STR_POSTFIX */ -#define VERSION_STR "2.8-devel" VERSION_STR_POSTFIX GIT_VERSION_STR_POSTFIX +#define VERSION_STR "2.8" VERSION_STR_POSTFIX GIT_VERSION_STR_POSTFIX #endif /* VERSION_H */ diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c index b47f632..ed2d1c2 100644 --- a/src/common/wpa_common.c +++ b/src/common/wpa_common.c @@ -340,14 +340,21 @@ int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver, * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy * PTK = PRF-X(PMK, "Pairwise key expansion", * Min(AA, SA) || Max(AA, SA) || - * Min(ANonce, SNonce) || Max(ANonce, SNonce)) + * Min(ANonce, SNonce) || Max(ANonce, SNonce) + * [ || Z.x ]) + * + * The optional Z.x component is used only with DPP and that part is not defined + * in IEEE 802.11. */ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, const u8 *addr1, const u8 *addr2, const u8 *nonce1, const u8 *nonce2, - struct wpa_ptk *ptk, int akmp, int cipher) + struct wpa_ptk *ptk, int akmp, int cipher, + const u8 *z, size_t z_len) { - u8 data[2 * ETH_ALEN + 2 * WPA_NONCE_LEN]; +#define MAX_Z_LEN 66 /* with NIST P-521 */ + u8 data[2 * ETH_ALEN + 2 * WPA_NONCE_LEN + MAX_Z_LEN]; + size_t data_len = 2 * ETH_ALEN + 2 * WPA_NONCE_LEN; u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN]; size_t ptk_len; @@ -356,6 +363,9 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, return -1; } + if (z_len > MAX_Z_LEN) + return -1; + if (os_memcmp(addr1, addr2, ETH_ALEN) < 0) { os_memcpy(data, addr1, ETH_ALEN); os_memcpy(data + ETH_ALEN, addr2, ETH_ALEN); @@ -374,6 +384,11 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, WPA_NONCE_LEN); } + if (z && z_len) { + os_memcpy(data + 2 * ETH_ALEN + 2 * WPA_NONCE_LEN, z, z_len); + data_len += z_len; + } + ptk->kck_len = wpa_kck_len(akmp, pmk_len); ptk->kek_len = wpa_kek_len(akmp, pmk_len); ptk->tk_len = wpa_cipher_key_len(cipher); @@ -388,7 +403,7 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, if (wpa_key_mgmt_sha384(akmp)) { #if defined(CONFIG_SUITEB192) || defined(CONFIG_FILS) wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA384)"); - if (sha384_prf(pmk, pmk_len, label, data, sizeof(data), + if (sha384_prf(pmk, pmk_len, label, data, data_len, tmp, ptk_len) < 0) return -1; #else /* CONFIG_SUITEB192 || CONFIG_FILS */ @@ -397,7 +412,7 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, } else if (wpa_key_mgmt_sha256(akmp) || akmp == WPA_KEY_MGMT_OWE) { #if defined(CONFIG_IEEE80211W) || defined(CONFIG_SAE) || defined(CONFIG_FILS) wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA256)"); - if (sha256_prf(pmk, pmk_len, label, data, sizeof(data), + if (sha256_prf(pmk, pmk_len, label, data, data_len, tmp, ptk_len) < 0) return -1; #else /* CONFIG_IEEE80211W or CONFIG_SAE or CONFIG_FILS */ @@ -406,17 +421,17 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, #ifdef CONFIG_DPP } else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 32) { wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA256)"); - if (sha256_prf(pmk, pmk_len, label, data, sizeof(data), + if (sha256_prf(pmk, pmk_len, label, data, data_len, tmp, ptk_len) < 0) return -1; } else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 48) { wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA384)"); - if (sha384_prf(pmk, pmk_len, label, data, sizeof(data), + if (sha384_prf(pmk, pmk_len, label, data, data_len, tmp, ptk_len) < 0) return -1; } else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 64) { wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA512)"); - if (sha512_prf(pmk, pmk_len, label, data, sizeof(data), + if (sha512_prf(pmk, pmk_len, label, data, data_len, tmp, ptk_len) < 0) return -1; } else if (akmp == WPA_KEY_MGMT_DPP) { @@ -426,7 +441,7 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, #endif /* CONFIG_DPP */ } else { wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA1)"); - if (sha1_prf(pmk, pmk_len, label, data, sizeof(data), tmp, + if (sha1_prf(pmk, pmk_len, label, data, data_len, tmp, ptk_len) < 0) return -1; } @@ -435,6 +450,8 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, MAC2STR(addr1), MAC2STR(addr2)); wpa_hexdump(MSG_DEBUG, "WPA: Nonce1", nonce1, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "WPA: Nonce2", nonce2, WPA_NONCE_LEN); + if (z && z_len) + wpa_hexdump_key(MSG_DEBUG, "WPA: Z.x", z, z_len); wpa_hexdump_key(MSG_DEBUG, "WPA: PMK", pmk, pmk_len); wpa_hexdump_key(MSG_DEBUG, "WPA: PTK", tmp, ptk_len); @@ -451,6 +468,7 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, ptk->kck2_len = 0; os_memset(tmp, 0, sizeof(tmp)); + os_memset(data, 0, data_len); return 0; } @@ -1209,6 +1227,7 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, left = rsn_ie_len - 6; data->group_cipher = WPA_CIPHER_GTK_NOT_USED; + data->has_group = 1; data->key_mgmt = WPA_KEY_MGMT_OSEN; data->proto = WPA_PROTO_OSEN; } else { @@ -1230,6 +1249,7 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, if (left >= RSN_SELECTOR_LEN) { data->group_cipher = rsn_selector_to_bitfield(pos); + data->has_group = 1; if (!wpa_cipher_valid_group(data->group_cipher)) { wpa_printf(MSG_DEBUG, "%s: invalid group cipher 0x%x (%08x)", @@ -1255,6 +1275,8 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, "count %u left %u", __func__, count, left); return -4; } + if (count) + data->has_pairwise = 1; for (i = 0; i < count; i++) { data->pairwise_cipher |= rsn_selector_to_bitfield(pos); pos += RSN_SELECTOR_LEN; @@ -1473,6 +1495,15 @@ int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, } +int wpa_default_rsn_cipher(int freq) +{ + if (freq > 56160) + return WPA_CIPHER_GCMP; /* DMG */ + + return WPA_CIPHER_CCMP; +} + + #ifdef CONFIG_IEEE80211R /** @@ -1760,7 +1791,7 @@ int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, size_t pmk_r1_len, os_memcpy(ptk->tk, tmp + offset, ptk->tk_len); offset += ptk->tk_len; os_memcpy(ptk->kck2, tmp + offset, ptk->kck2_len); - offset = ptk->kck2_len; + offset += ptk->kck2_len; os_memcpy(ptk->kek2, tmp + offset, ptk->kek2_len); wpa_hexdump_key(MSG_DEBUG, "FT: KCK", ptk->kck, ptk->kck_len); diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h index 37b5834..e83d688 100644 --- a/src/common/wpa_common.h +++ b/src/common/wpa_common.h @@ -347,7 +347,8 @@ int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver, int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, const u8 *addr1, const u8 *addr2, const u8 *nonce1, const u8 *nonce2, - struct wpa_ptk *ptk, int akmp, int cipher); + struct wpa_ptk *ptk, int akmp, int cipher, + const u8 *z, size_t z_len); int fils_rmsk_to_pmk(int akmp, const u8 *rmsk, size_t rmsk_len, const u8 *snonce, const u8 *anonce, const u8 *dh_ss, size_t dh_ss_len, u8 *pmk, size_t *pmk_len); @@ -392,7 +393,9 @@ int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, size_t pmk_r1_len, const u8 *snonce, struct wpa_ie_data { int proto; int pairwise_cipher; + int has_pairwise; int group_cipher; + int has_group; int key_mgmt; int capabilities; size_t num_pmkid; @@ -405,6 +408,7 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, struct wpa_ie_data *data); int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, struct wpa_ie_data *data); +int wpa_default_rsn_cipher(int freq); void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa, u8 *pmkid, int akmp); diff --git a/src/crypto/Makefile b/src/crypto/Makefile index ee93e41..ab108da 100644 --- a/src/crypto/Makefile +++ b/src/crypto/Makefile @@ -62,7 +62,9 @@ LIB_OBJS += crypto_internal-modexp.o LIB_OBJS += crypto_internal-rsa.o LIB_OBJS += tls_internal.o LIB_OBJS += fips_prf_internal.o +ifndef TEST_FUZZ LIB_OBJS += random.o +endif libcrypto.a: $(LIB_OBJS) diff --git a/src/crypto/aes-internal-enc.c b/src/crypto/aes-internal-enc.c index 9fdb4f3..baeffca 100644 --- a/src/crypto/aes-internal-enc.c +++ b/src/crypto/aes-internal-enc.c @@ -99,6 +99,10 @@ void * aes_encrypt_init(const u8 *key, size_t len) { u32 *rk; int res; + + if (TEST_FAIL()) + return NULL; + rk = os_malloc(AES_PRIV_SIZE); if (rk == NULL) return NULL; diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index 507b7ca..12109ce 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -420,6 +420,7 @@ int __must_check crypto_public_key_decrypt_pkcs1( int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, u8 *pubkey); int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *order, size_t order_len, const u8 *privkey, size_t privkey_len, const u8 *pubkey, size_t pubkey_len, u8 *secret, size_t *len); @@ -703,14 +704,6 @@ struct crypto_ec * crypto_ec_init(int group); void crypto_ec_deinit(struct crypto_ec *e); /** - * crypto_ec_cofactor - Set the cofactor into the big number - * @e: EC context from crypto_ec_init() - * @cofactor: Cofactor of curve. - * Returns: 0 on success, -1 on failure - */ -int crypto_ec_cofactor(struct crypto_ec *e, struct crypto_bignum *cofactor); - -/** * crypto_ec_prime_len - Get length of the prime in octets * @e: EC context from crypto_ec_init() * Returns: Length of the prime defining the group diff --git a/src/crypto/crypto_gnutls.c b/src/crypto/crypto_gnutls.c index 7a797b5..4ef1146 100644 --- a/src/crypto/crypto_gnutls.c +++ b/src/crypto/crypto_gnutls.c @@ -310,12 +310,51 @@ int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *order, size_t order_len, const u8 *privkey, size_t privkey_len, const u8 *pubkey, size_t pubkey_len, u8 *secret, size_t *len) { - return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, - prime, prime_len, secret, len); + gcry_mpi_t pub = NULL; + int res = -1; + + if (pubkey_len > prime_len || + (pubkey_len == prime_len && + os_memcmp(pubkey, prime, prime_len) >= 0)) + return -1; + + if (gcry_mpi_scan(&pub, GCRYMPI_FMT_USG, pubkey, pubkey_len, NULL) != + GPG_ERR_NO_ERROR || + gcry_mpi_cmp_ui(pub, 1) <= 0) + goto fail; + + if (order) { + gcry_mpi_t p = NULL, q = NULL, tmp; + int failed; + + /* verify: pubkey^q == 1 mod p */ + tmp = gcry_mpi_new(prime_len * 8); + failed = !tmp || + gcry_mpi_scan(&p, GCRYMPI_FMT_USG, prime, prime_len, + NULL) != GPG_ERR_NO_ERROR || + gcry_mpi_scan(&q, GCRYMPI_FMT_USG, order, order_len, + NULL) != GPG_ERR_NO_ERROR; + if (!failed) { + gcry_mpi_powm(tmp, pub, q, p); + failed = gcry_mpi_cmp_ui(tmp, 1) != 0; + } + gcry_mpi_release(p); + gcry_mpi_release(q); + gcry_mpi_release(tmp); + if (failed) + goto fail; + } + + res = crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, + prime, prime_len, secret, len); +fail: + gcry_mpi_release(pub); + return res; } diff --git a/src/crypto/crypto_internal-modexp.c b/src/crypto/crypto_internal-modexp.c index 92581ac..6819f1a 100644 --- a/src/crypto/crypto_internal-modexp.c +++ b/src/crypto/crypto_internal-modexp.c @@ -40,12 +40,49 @@ int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *order, size_t order_len, const u8 *privkey, size_t privkey_len, const u8 *pubkey, size_t pubkey_len, u8 *secret, size_t *len) { - return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, - prime, prime_len, secret, len); + struct bignum *pub; + int res = -1; + + if (pubkey_len > prime_len || + (pubkey_len == prime_len && + os_memcmp(pubkey, prime, prime_len) >= 0)) + return -1; + + pub = bignum_init(); + if (!pub || bignum_set_unsigned_bin(pub, pubkey, pubkey_len) < 0 || + bignum_cmp_d(pub, 1) <= 0) + goto fail; + + if (order) { + struct bignum *p, *q, *tmp; + int failed; + + /* verify: pubkey^q == 1 mod p */ + p = bignum_init(); + q = bignum_init(); + tmp = bignum_init(); + failed = !p || !q || !tmp || + bignum_set_unsigned_bin(p, prime, prime_len) < 0 || + bignum_set_unsigned_bin(q, order, order_len) < 0 || + bignum_exptmod(pub, q, p, tmp) < 0 || + bignum_cmp_d(tmp, 1) != 0; + bignum_deinit(p); + bignum_deinit(q); + bignum_deinit(tmp); + if (failed) + goto fail; + } + + res = crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, + prime, prime_len, secret, len); +fail: + bignum_deinit(pub); + return res; } diff --git a/src/crypto/crypto_internal.c b/src/crypto/crypto_internal.c index d391f48..aad40af 100644 --- a/src/crypto/crypto_internal.c +++ b/src/crypto/crypto_internal.c @@ -310,6 +310,9 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) os_free(ctx); + if (TEST_FAIL()) + return -1; + return 0; } diff --git a/src/crypto/crypto_libtomcrypt.c b/src/crypto/crypto_libtomcrypt.c index 259f995..ed30efa 100644 --- a/src/crypto/crypto_libtomcrypt.c +++ b/src/crypto/crypto_libtomcrypt.c @@ -278,6 +278,9 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) os_free(ctx); + if (TEST_FAIL()) + return -1; + return ret; } @@ -721,10 +724,12 @@ int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *order, size_t order_len, const u8 *privkey, size_t privkey_len, const u8 *pubkey, size_t pubkey_len, u8 *secret, size_t *len) { + /* TODO: check pubkey */ return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, prime, prime_len, secret, len); } diff --git a/src/crypto/crypto_linux.c b/src/crypto/crypto_linux.c index 8099193..1724456 100644 --- a/src/crypto/crypto_linux.c +++ b/src/crypto/crypto_linux.c @@ -386,6 +386,9 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) } crypto_hash_deinit(ctx); + + if (TEST_FAIL()) + return -1; return 0; } diff --git a/src/crypto/crypto_nettle.c b/src/crypto/crypto_nettle.c index 4e31bc8..f85d365 100644 --- a/src/crypto/crypto_nettle.c +++ b/src/crypto/crypto_nettle.c @@ -331,12 +331,44 @@ int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *order, size_t order_len, const u8 *privkey, size_t privkey_len, const u8 *pubkey, size_t pubkey_len, u8 *secret, size_t *len) { - return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, - prime, prime_len, secret, len); + mpz_t pub; + int res = -1; + + if (pubkey_len > prime_len || + (pubkey_len == prime_len && + os_memcmp(pubkey, prime, prime_len) >= 0)) + return -1; + + mpz_init(pub); + mpz_import(pub, pubkey_len, 1, 1, 1, 0, pubkey); + if (mpz_cmp_d(pub, 1) <= 0) + goto fail; + + if (order) { + mpz_t p, q, tmp; + int failed; + + /* verify: pubkey^q == 1 mod p */ + mpz_inits(p, q, tmp, NULL); + mpz_import(p, prime_len, 1, 1, 1, 0, prime); + mpz_import(q, order_len, 1, 1, 1, 0, order); + mpz_powm(tmp, pub, q, p); + failed = mpz_cmp_d(tmp, 1) != 0; + mpz_clears(p, q, tmp, NULL); + if (failed) + goto fail; + } + + res = crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, + prime, prime_len, secret, len); +fail: + mpz_clear(pub); + return res; } diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c index f89053a..1b0c1ec 100644 --- a/src/crypto/crypto_openssl.c +++ b/src/crypto/crypto_openssl.c @@ -24,6 +24,7 @@ #endif /* CONFIG_ECC */ #include "common.h" +#include "utils/const_time.h" #include "wpabuf.h" #include "dh_group5.h" #include "sha1.h" @@ -111,6 +112,31 @@ static BIGNUM * get_group5_prime(void) #endif } + +static BIGNUM * get_group5_order(void) +{ + static const unsigned char RFC3526_ORDER_1536[] = { + 0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xE4,0x87,0xED,0x51, + 0x10,0xB4,0x61,0x1A,0x62,0x63,0x31,0x45,0xC0,0x6E,0x0E,0x68, + 0x94,0x81,0x27,0x04,0x45,0x33,0xE6,0x3A,0x01,0x05,0xDF,0x53, + 0x1D,0x89,0xCD,0x91,0x28,0xA5,0x04,0x3C,0xC7,0x1A,0x02,0x6E, + 0xF7,0xCA,0x8C,0xD9,0xE6,0x9D,0x21,0x8D,0x98,0x15,0x85,0x36, + 0xF9,0x2F,0x8A,0x1B,0xA7,0xF0,0x9A,0xB6,0xB6,0xA8,0xE1,0x22, + 0xF2,0x42,0xDA,0xBB,0x31,0x2F,0x3F,0x63,0x7A,0x26,0x21,0x74, + 0xD3,0x1B,0xF6,0xB5,0x85,0xFF,0xAE,0x5B,0x7A,0x03,0x5B,0xF6, + 0xF7,0x1C,0x35,0xFD,0xAD,0x44,0xCF,0xD2,0xD7,0x4F,0x92,0x08, + 0xBE,0x25,0x8F,0xF3,0x24,0x94,0x33,0x28,0xF6,0x72,0x2D,0x9E, + 0xE1,0x00,0x3E,0x5C,0x50,0xB1,0xDF,0x82,0xCC,0x6D,0x24,0x1B, + 0x0E,0x2A,0xE9,0xCD,0x34,0x8B,0x1F,0xD4,0x7E,0x92,0x67,0xAF, + 0xC1,0xB2,0xAE,0x91,0xEE,0x51,0xD6,0xCB,0x0E,0x31,0x79,0xAB, + 0x10,0x42,0xA9,0x5D,0xCF,0x6A,0x94,0x83,0xB8,0x4B,0x4B,0x36, + 0xB3,0x86,0x1A,0xA7,0x25,0x5E,0x4C,0x02,0x78,0xBA,0x36,0x04, + 0x65,0x11,0xB9,0x93,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + }; + return BN_bin2bn(RFC3526_ORDER_1536, sizeof(RFC3526_ORDER_1536), NULL); +} + + #ifdef OPENSSL_NO_SHA256 #define NO_SHA256_WRAPPER #endif @@ -518,12 +544,45 @@ int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *order, size_t order_len, const u8 *privkey, size_t privkey_len, const u8 *pubkey, size_t pubkey_len, u8 *secret, size_t *len) { - return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, - prime, prime_len, secret, len); + BIGNUM *pub, *p; + int res = -1; + + pub = BN_bin2bn(pubkey, pubkey_len, NULL); + p = BN_bin2bn(prime, prime_len, NULL); + if (!pub || !p || BN_is_zero(pub) || BN_is_one(pub) || + BN_cmp(pub, p) >= 0) + goto fail; + + if (order) { + BN_CTX *ctx; + BIGNUM *q, *tmp; + int failed; + + /* verify: pubkey^q == 1 mod p */ + q = BN_bin2bn(order, order_len, NULL); + ctx = BN_CTX_new(); + tmp = BN_new(); + failed = !q || !ctx || !tmp || + !BN_mod_exp(tmp, pub, q, p, ctx) || + !BN_is_one(tmp); + BN_clear(q); + BN_clear(tmp); + BN_CTX_free(ctx); + if (failed) + goto fail; + } + + res = crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, + prime, prime_len, secret, len); +fail: + BN_clear(pub); + BN_clear(p); + return res; } @@ -549,7 +608,8 @@ int crypto_mod_exp(const u8 *base, size_t base_len, bn_result == NULL) goto error; - if (BN_mod_exp(bn_result, bn_base, bn_exp, bn_modulus, ctx) != 1) + if (BN_mod_exp_mont_consttime(bn_result, bn_base, bn_exp, bn_modulus, + ctx, NULL) != 1) goto error; *result_len = BN_bn2bin(bn_result, result); @@ -709,6 +769,10 @@ void * dh5_init(struct wpabuf **priv, struct wpabuf **publ) if (dh->p == NULL) goto err; + dh->q = get_group5_order(); + if (!dh->q) + goto err; + if (DH_generate_key(dh) != 1) goto err; @@ -737,7 +801,7 @@ err: DH *dh; struct wpabuf *pubkey = NULL, *privkey = NULL; size_t publen, privlen; - BIGNUM *p = NULL, *g; + BIGNUM *p, *g, *q; const BIGNUM *priv_key = NULL, *pub_key = NULL; *priv = NULL; @@ -750,10 +814,12 @@ err: g = BN_new(); p = get_group5_prime(); - if (!g || BN_set_word(g, 2) != 1 || !p || - DH_set0_pqg(dh, p, NULL, g) != 1) + q = get_group5_order(); + if (!g || BN_set_word(g, 2) != 1 || !p || !q || + DH_set0_pqg(dh, p, q, g) != 1) goto err; p = NULL; + q = NULL; g = NULL; if (DH_generate_key(dh) != 1) @@ -778,6 +844,7 @@ err: err: BN_free(p); + BN_free(q); BN_free(g); wpabuf_clear_free(pubkey); wpabuf_clear_free(privkey); @@ -987,6 +1054,9 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) HMAC_CTX_free(ctx->ctx); bin_clear_free(ctx, sizeof(*ctx)); + if (TEST_FAIL()) + return -1; + if (res == 1) { *len = mdlen; return 0; @@ -1250,6 +1320,8 @@ int crypto_bignum_to_bin(const struct crypto_bignum *a, int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m) { + if (TEST_FAIL()) + return -1; return BN_rand_range((BIGNUM *) r, (const BIGNUM *) m) == 1 ? 0 : -1; } @@ -1295,8 +1367,9 @@ int crypto_bignum_exptmod(const struct crypto_bignum *a, bnctx = BN_CTX_new(); if (bnctx == NULL) return -1; - res = BN_mod_exp((BIGNUM *) d, (const BIGNUM *) a, (const BIGNUM *) b, - (const BIGNUM *) c, bnctx); + res = BN_mod_exp_mont_consttime((BIGNUM *) d, (const BIGNUM *) a, + (const BIGNUM *) b, (const BIGNUM *) c, + bnctx, NULL); BN_CTX_free(bnctx); return res ? 0 : -1; @@ -1315,6 +1388,11 @@ int crypto_bignum_inverse(const struct crypto_bignum *a, bnctx = BN_CTX_new(); if (bnctx == NULL) return -1; +#ifdef OPENSSL_IS_BORINGSSL + /* TODO: use BN_mod_inverse_blinded() ? */ +#else /* OPENSSL_IS_BORINGSSL */ + BN_set_flags((BIGNUM *) a, BN_FLG_CONSTTIME); +#endif /* OPENSSL_IS_BORINGSSL */ res = BN_mod_inverse((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b, bnctx); BN_CTX_free(bnctx); @@ -1348,6 +1426,9 @@ int crypto_bignum_div(const struct crypto_bignum *a, bnctx = BN_CTX_new(); if (bnctx == NULL) return -1; +#ifndef OPENSSL_IS_BORINGSSL + BN_set_flags((BIGNUM *) a, BN_FLG_CONSTTIME); +#endif /* OPENSSL_IS_BORINGSSL */ res = BN_div((BIGNUM *) c, NULL, (const BIGNUM *) a, (const BIGNUM *) b, bnctx); BN_CTX_free(bnctx); @@ -1425,6 +1506,7 @@ int crypto_bignum_legendre(const struct crypto_bignum *a, BN_CTX *bnctx; BIGNUM *exp = NULL, *tmp = NULL; int res = -2; + unsigned int mask; if (TEST_FAIL()) return -2; @@ -1439,16 +1521,17 @@ int crypto_bignum_legendre(const struct crypto_bignum *a, /* exp = (p-1) / 2 */ !BN_sub(exp, (const BIGNUM *) p, BN_value_one()) || !BN_rshift1(exp, exp) || - !BN_mod_exp(tmp, (const BIGNUM *) a, exp, (const BIGNUM *) p, - bnctx)) + !BN_mod_exp_mont_consttime(tmp, (const BIGNUM *) a, exp, + (const BIGNUM *) p, bnctx, NULL)) goto fail; - if (BN_is_word(tmp, 1)) - res = 1; - else if (BN_is_zero(tmp)) - res = 0; - else - res = -1; + /* Return 1 if tmp == 1, 0 if tmp == 0, or -1 otherwise. Need to use + * constant time selection to avoid branches here. */ + res = -1; + mask = const_time_eq(BN_is_word(tmp, 1), 1); + res = const_time_select_int(mask, 1, res); + mask = const_time_eq(BN_is_zero(tmp), 1); + res = const_time_select_int(mask, 0, res); fail: BN_clear_free(tmp); @@ -1553,13 +1636,6 @@ void crypto_ec_deinit(struct crypto_ec *e) } -int crypto_ec_cofactor(struct crypto_ec *e, struct crypto_bignum *cofactor) -{ - return EC_GROUP_get_cofactor(e->group, (BIGNUM *) cofactor, - e->bnctx) == 0 ? -1 : 0; -} - - struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e) { if (TEST_FAIL()) diff --git a/src/crypto/crypto_wolfssl.c b/src/crypto/crypto_wolfssl.c index b5a1e3f..976a008 100644 --- a/src/crypto/crypto_wolfssl.c +++ b/src/crypto/crypto_wolfssl.c @@ -826,6 +826,7 @@ done: int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *order, size_t order_len, const u8 *privkey, size_t privkey_len, const u8 *pubkey, size_t pubkey_len, u8 *secret, size_t *len) @@ -952,6 +953,8 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) ret = 0; done: bin_clear_free(ctx, sizeof(*ctx)); + if (TEST_FAIL()) + return -1; return ret; } @@ -1082,6 +1085,8 @@ int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m) int ret = 0; WC_RNG rng; + if (TEST_FAIL()) + return -1; if (wc_InitRng(&rng) != 0) return -1; if (mp_rand_prime((mp_int *) r, @@ -1347,16 +1352,6 @@ void crypto_ec_deinit(struct crypto_ec* e) } -int crypto_ec_cofactor(struct crypto_ec *e, struct crypto_bignum *cofactor) -{ - if (!e || !cofactor) - return -1; - - mp_set((mp_int *) cofactor, e->key.dp->cofactor); - return 0; -} - - struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e) { if (TEST_FAIL()) diff --git a/src/crypto/dh_groups.c b/src/crypto/dh_groups.c index a9b770e..5e421b2 100644 --- a/src/crypto/dh_groups.c +++ b/src/crypto/dh_groups.c @@ -1249,6 +1249,7 @@ struct wpabuf * dh_derive_shared(const struct wpabuf *peer_public, if (shared == NULL) return NULL; if (crypto_dh_derive_secret(*dh->generator, dh->prime, dh->prime_len, + dh->order, dh->order_len, wpabuf_head(own_private), wpabuf_len(own_private), wpabuf_head(peer_public), diff --git a/src/crypto/sha1-tlsprf.c b/src/crypto/sha1-tlsprf.c index f9bc0eb..a11649a 100644 --- a/src/crypto/sha1-tlsprf.c +++ b/src/crypto/sha1-tlsprf.c @@ -40,9 +40,6 @@ int tls_prf_sha1_md5(const u8 *secret, size_t secret_len, const char *label, const unsigned char *SHA1_addr[3]; size_t SHA1_len[3]; - if (secret_len & 1) - return -1; - MD5_addr[0] = A_MD5; MD5_len[0] = MD5_MAC_LEN; MD5_addr[1] = (unsigned char *) label; diff --git a/src/crypto/tls.h b/src/crypto/tls.h index 413cccd..8bdb91f 100644 --- a/src/crypto/tls.h +++ b/src/crypto/tls.h @@ -42,6 +42,7 @@ enum tls_fail_reason { TLS_FAIL_DOMAIN_SUFFIX_MISMATCH = 9, TLS_FAIL_DOMAIN_MISMATCH = 10, TLS_FAIL_INSUFFICIENT_KEY_LEN = 11, + TLS_FAIL_DN_MISMATCH = 12, }; @@ -119,12 +120,19 @@ struct tls_config { * %NULL to allow all subjects * @altsubject_match: String to match in the alternative subject of the peer * certificate or %NULL to allow all alternative subjects - * @suffix_match: String to suffix match in the dNSName or CN of the peer - * certificate or %NULL to allow all domain names. This may allow subdomains an - * wildcard certificates. Each domain name label must have a full match. + * @suffix_match: Semicolon deliminated string of values to suffix match against + * the dNSName or CN of the peer certificate or %NULL to allow all domain names. + * This may allow subdomains and wildcard certificates. Each domain name label + * must have a full case-insensitive match. * @domain_match: String to match in the dNSName or CN of the peer * certificate or %NULL to allow all domain names. This requires a full, * case-insensitive match. + * + * More than one match string can be provided by using semicolons to + * separate the strings (e.g., example.org;example.com). When multiple + * strings are specified, a match with any one of the values is + * considered a sufficient match for the certificate, i.e., the + * conditions are ORed together. * @client_cert: File or reference name for client X.509 certificate in PEM or * DER format * @client_cert_blob: client_cert as inlined data or %NULL if not used @@ -156,6 +164,7 @@ struct tls_config { * @ocsp_stapling_response_multi: DER encoded file with cached OCSP stapling * response list (OCSPResponseList for ocsp_multi in RFC 6961) or %NULL if * ocsp_multi is not enabled + * @check_cert_subject: Client certificate subject name matching string * * TLS connection parameters to be configured with tls_connection_set_params() * and tls_global_set_params(). @@ -198,6 +207,7 @@ struct tls_connection_params { unsigned int flags; const char *ocsp_stapling_response; const char *ocsp_stapling_response_multi; + const char *check_cert_subject; }; @@ -367,15 +377,21 @@ int __must_check tls_connection_get_random(void *tls_ctx, * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @label: Label (e.g., description of the key) for PRF + * @context: Optional extra upper-layer context (max len 2^16) + * @context_len: The length of the context value * @out: Buffer for output data from TLS-PRF * @out_len: Length of the output buffer * Returns: 0 on success, -1 on failure * - * Exports keying material using the mechanism described in RFC 5705. + * Exports keying material using the mechanism described in RFC 5705. If + * context is %NULL, context is not provided; otherwise, context is provided + * (including the case of empty context with context_len == 0). */ int __must_check tls_connection_export_key(void *tls_ctx, struct tls_connection *conn, const char *label, + const u8 *context, + size_t context_len, u8 *out, size_t out_len); /** diff --git a/src/crypto/tls_gnutls.c b/src/crypto/tls_gnutls.c index 527d01e..daa01d9 100644 --- a/src/crypto/tls_gnutls.c +++ b/src/crypto/tls_gnutls.c @@ -739,6 +739,9 @@ int tls_global_set_params(void *tls_ctx, struct tls_global *global = tls_ctx; int ret; + if (params->check_cert_subject) + return -1; /* not yet supported */ + /* Currently, global parameters are only set when running in server * mode. */ global->server = 1; @@ -895,14 +898,23 @@ int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn, int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn, - const char *label, u8 *out, size_t out_len) + const char *label, const u8 *context, + size_t context_len, u8 *out, size_t out_len) { if (conn == NULL || conn->session == NULL) return -1; +#if GNUTLS_VERSION_NUMBER >= 0x030404 + return gnutls_prf_rfc5705(conn->session, os_strlen(label), label, + context_len, (const char *) context, + out_len, (char *) out); +#else /* 3.4.4 */ + if (context) + return -1; return gnutls_prf(conn->session, os_strlen(label), label, 0 /* client_random first */, 0, NULL, out_len, (char *) out); +#endif /* 3.4.4 */ } @@ -1074,6 +1086,52 @@ ocsp_error: } +static int tls_match_suffix_helper(gnutls_x509_crt_t cert, const char *match, + int full) +{ + int res = -1; + +#if GNUTLS_VERSION_NUMBER >= 0x030300 + if (full) + res = gnutls_x509_crt_check_hostname2( + cert, match, + GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS); +#endif /* >= 3.3.0 */ + if (res == -1) + res = gnutls_x509_crt_check_hostname(cert, match); + + wpa_printf(MSG_DEBUG, "TLS: Match domain against %s%s --> res=%d", + full ? "": "suffix ", match, res); + return res; +} + + +static int tls_match_suffix(gnutls_x509_crt_t cert, const char *match, + int full) +{ + char *values, *token, *context = NULL; + int ret = 0; + + if (!os_strchr(match, ';')) + return tls_match_suffix_helper(cert, match, full); + + values = os_strdup(match); + if (!values) + return 0; + + /* Process each match alternative separately until a match is found */ + while ((token = str_token(values, ";", &context))) { + if (tls_match_suffix_helper(cert, token, full)) { + ret = 1; + break; + } + } + + os_free(values); + return ret; +} + + static int tls_connection_verify_peer(gnutls_session_t session) { struct tls_connection *conn; @@ -1269,8 +1327,7 @@ static int tls_connection_verify_peer(gnutls_session_t session) if (i == 0) { if (conn->suffix_match && - !gnutls_x509_crt_check_hostname( - cert, conn->suffix_match)) { + !tls_match_suffix(cert, conn->suffix_match, 0)) { wpa_printf(MSG_WARNING, "TLS: Domain suffix match '%s' not found", conn->suffix_match); @@ -1286,9 +1343,7 @@ static int tls_connection_verify_peer(gnutls_session_t session) #if GNUTLS_VERSION_NUMBER >= 0x030300 if (conn->domain_match && - !gnutls_x509_crt_check_hostname2( - cert, conn->domain_match, - GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS)) { + !tls_match_suffix(cert, conn->domain_match, 1)) { wpa_printf(MSG_WARNING, "TLS: Domain match '%s' not found", conn->domain_match); diff --git a/src/crypto/tls_internal.c b/src/crypto/tls_internal.c index 9c57ab2..8095b43 100644 --- a/src/crypto/tls_internal.c +++ b/src/crypto/tls_internal.c @@ -1,6 +1,6 @@ /* * TLS interface functions and an internal TLS implementation - * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -309,6 +309,9 @@ int tls_global_set_params(void *tls_ctx, struct tls_global *global = tls_ctx; struct tlsv1_credentials *cred; + if (params->check_cert_subject) + return -1; /* not yet supported */ + /* Currently, global parameters are only set when running in server * mode. */ global->server = 1; @@ -409,7 +412,8 @@ static int tls_get_keyblock_size(struct tls_connection *conn) static int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, - const char *label, int server_random_first, + const char *label, const u8 *context, + size_t context_len, int server_random_first, int skip_keyblock, u8 *out, size_t out_len) { int ret = -1, skip = 0; @@ -428,15 +432,15 @@ static int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, #ifdef CONFIG_TLS_INTERNAL_CLIENT if (conn->client) { - ret = tlsv1_client_prf(conn->client, label, - server_random_first, + ret = tlsv1_client_prf(conn->client, label, context, + context_len, server_random_first, _out, skip + out_len); } #endif /* CONFIG_TLS_INTERNAL_CLIENT */ #ifdef CONFIG_TLS_INTERNAL_SERVER if (conn->server) { - ret = tlsv1_server_prf(conn->server, label, - server_random_first, + ret = tlsv1_server_prf(conn->server, label, context, + context_len, server_random_first, _out, skip + out_len); } #endif /* CONFIG_TLS_INTERNAL_SERVER */ @@ -449,17 +453,19 @@ static int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn, - const char *label, u8 *out, size_t out_len) + const char *label, const u8 *context, + size_t context_len, u8 *out, size_t out_len) { - return tls_connection_prf(tls_ctx, conn, label, 0, 0, out, out_len); + return tls_connection_prf(tls_ctx, conn, label, context, context_len, + 0, 0, out, out_len); } int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn, u8 *out, size_t out_len) { - return tls_connection_prf(tls_ctx, conn, "key expansion", 1, 1, out, - out_len); + return tls_connection_prf(tls_ctx, conn, "key expansion", NULL, 0, + 1, 1, out, out_len); } @@ -726,12 +732,20 @@ int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn, int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn) { +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_get_failed(conn->server); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ return 0; } int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn) { +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_get_read_alerts(conn->server); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ return 0; } @@ -739,6 +753,10 @@ int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn) int tls_connection_get_write_alerts(void *tls_ctx, struct tls_connection *conn) { +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_get_write_alerts(conn->server); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ return 0; } diff --git a/src/crypto/tls_none.c b/src/crypto/tls_none.c index 108e9aa..6d6fb0c 100644 --- a/src/crypto/tls_none.c +++ b/src/crypto/tls_none.c @@ -94,7 +94,8 @@ int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn, int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn, - const char *label, u8 *out, size_t out_len) + const char *label, const u8 *context, + size_t context_len, u8 *out, size_t out_len) { return -1; } diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c index 99caa68..b0c23ae 100644 --- a/src/crypto/tls_openssl.c +++ b/src/crypto/tls_openssl.c @@ -219,6 +219,7 @@ struct tls_data { char *ca_cert; unsigned int crl_reload_interval; struct os_reltime crl_last_reload; + char *check_cert_subject; }; struct tls_connection { @@ -232,6 +233,7 @@ struct tls_connection { EVP_PKEY *private_key; /* the private key if using engine */ #endif /* OPENSSL_NO_ENGINE */ char *subject_match, *altsubject_match, *suffix_match, *domain_match; + char *check_cert_subject; int read_alerts, write_alerts, failed; tls_session_ticket_cb session_ticket_cb; @@ -329,8 +331,7 @@ static X509_STORE * tls_crl_cert_reload(const char *ca_cert, int check_crl) return NULL; } - if (check_crl) - flags = X509_V_FLAG_CRL_CHECK; + flags = check_crl ? X509_V_FLAG_CRL_CHECK : 0; if (check_crl == 2) flags |= X509_V_FLAG_CRL_CHECK_ALL; @@ -1135,6 +1136,7 @@ void tls_deinit(void *ssl_ctx) tls_global = NULL; } + os_free(data->check_cert_subject); os_free(data); } @@ -1347,8 +1349,16 @@ static const char * openssl_handshake_type(int content_type, const u8 *buf, return "client hello"; case 2: return "server hello"; + case 3: + return "hello verify request"; case 4: return "new session ticket"; + case 5: + return "end of early data"; + case 6: + return "hello retry request"; + case 8: + return "encrypted extensions"; case 11: return "certificate"; case 12: @@ -1367,6 +1377,12 @@ static const char * openssl_handshake_type(int content_type, const u8 *buf, return "certificate url"; case 22: return "certificate status"; + case 23: + return "supplemental data"; + case 24: + return "key update"; + case 254: + return "message hash"; default: return "?"; } @@ -1598,6 +1614,7 @@ void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) os_free(conn->altsubject_match); os_free(conn->suffix_match); os_free(conn->domain_match); + os_free(conn->check_cert_subject); os_free(conn->session_ticket); os_free(conn); } @@ -1718,9 +1735,9 @@ static int tls_match_altsubject(X509 *cert, const char *match) #ifndef CONFIG_NATIVE_WINDOWS static int domain_suffix_match(const u8 *val, size_t len, const char *match, - int full) + size_t match_len, int full) { - size_t i, match_len; + size_t i; /* Check for embedded nuls that could mess up suffix matching */ for (i = 0; i < len; i++) { @@ -1730,7 +1747,6 @@ static int domain_suffix_match(const u8 *val, size_t len, const char *match, } } - match_len = os_strlen(match); if (match_len > len || (full && match_len != len)) return 0; @@ -1750,12 +1766,223 @@ static int domain_suffix_match(const u8 *val, size_t len, const char *match, #endif /* CONFIG_NATIVE_WINDOWS */ -static int tls_match_suffix(X509 *cert, const char *match, int full) +struct tls_dn_field_order_cnt { + u8 cn; + u8 c; + u8 l; + u8 st; + u8 o; + u8 ou; + u8 email; +}; + + +static int get_dn_field_index(const struct tls_dn_field_order_cnt *dn_cnt, + int nid) +{ + switch (nid) { + case NID_commonName: + return dn_cnt->cn; + case NID_countryName: + return dn_cnt->c; + case NID_localityName: + return dn_cnt->l; + case NID_stateOrProvinceName: + return dn_cnt->st; + case NID_organizationName: + return dn_cnt->o; + case NID_organizationalUnitName: + return dn_cnt->ou; + case NID_pkcs9_emailAddress: + return dn_cnt->email; + default: + wpa_printf(MSG_ERROR, + "TLS: Unknown NID '%d' in check_cert_subject", + nid); + return -1; + } +} + + +/** + * match_dn_field - Match configuration DN field against Certificate DN field + * @cert: Certificate + * @nid: NID of DN field + * @field: Field name + * @value DN field value which is passed from configuration + * e.g., if configuration have C=US and this argument will point to US. + * @dn_cnt: DN matching context + * Returns: 1 on success and 0 on failure + */ +static int match_dn_field(const X509 *cert, int nid, const char *field, + const char *value, + const struct tls_dn_field_order_cnt *dn_cnt) +{ + int i, ret = 0, len, config_dn_field_index, match_index = 0; + X509_NAME *name; + + len = os_strlen(value); + name = X509_get_subject_name((X509 *) cert); + + /* Assign incremented cnt for every field of DN to check DN field in + * right order */ + config_dn_field_index = get_dn_field_index(dn_cnt, nid); + if (config_dn_field_index < 0) + return 0; + + /* Fetch value based on NID */ + for (i = -1; (i = X509_NAME_get_index_by_NID(name, nid, i)) > -1;) { + X509_NAME_ENTRY *e; + ASN1_STRING *cn; + + e = X509_NAME_get_entry(name, i); + if (!e) + continue; + + cn = X509_NAME_ENTRY_get_data(e); + if (!cn) + continue; + + match_index++; + + /* check for more than one DN field with same name */ + if (match_index != config_dn_field_index) + continue; + + /* Check wildcard at the right end side */ + /* E.g., if OU=develop* mentioned in configuration, allow 'OU' + * of the subject in the client certificate to start with + * 'develop' */ + if (len > 0 && value[len - 1] == '*') { + /* Compare actual certificate DN field value with + * configuration DN field value up to the specified + * length. */ + ret = ASN1_STRING_length(cn) >= len - 1 && + os_memcmp(ASN1_STRING_get0_data(cn), value, + len - 1) == 0; + } else { + /* Compare actual certificate DN field value with + * configuration DN field value */ + ret = ASN1_STRING_length(cn) == len && + os_memcmp(ASN1_STRING_get0_data(cn), value, + len) == 0; + } + if (!ret) { + wpa_printf(MSG_ERROR, + "OpenSSL: Failed to match %s '%s' with certificate DN field value '%s'", + field, value, ASN1_STRING_get0_data(cn)); + } + break; + } + + return ret; +} + + +/** + * get_value_from_field - Get value from DN field + * @cert: Certificate + * @field_str: DN field string which is passed from configuration file (e.g., + * C=US) + * @dn_cnt: DN matching context + * Returns: 1 on success and 0 on failure + */ +static int get_value_from_field(const X509 *cert, char *field_str, + struct tls_dn_field_order_cnt *dn_cnt) +{ + int nid; + char *context = NULL, *name, *value; + + if (os_strcmp(field_str, "*") == 0) + return 1; /* wildcard matches everything */ + + name = str_token(field_str, "=", &context); + if (!name) + return 0; + + /* Compare all configured DN fields and assign nid based on that to + * fetch correct value from certificate subject */ + if (os_strcmp(name, "CN") == 0) { + nid = NID_commonName; + dn_cnt->cn++; + } else if(os_strcmp(name, "C") == 0) { + nid = NID_countryName; + dn_cnt->c++; + } else if (os_strcmp(name, "L") == 0) { + nid = NID_localityName; + dn_cnt->l++; + } else if (os_strcmp(name, "ST") == 0) { + nid = NID_stateOrProvinceName; + dn_cnt->st++; + } else if (os_strcmp(name, "O") == 0) { + nid = NID_organizationName; + dn_cnt->o++; + } else if (os_strcmp(name, "OU") == 0) { + nid = NID_organizationalUnitName; + dn_cnt->ou++; + } else if (os_strcmp(name, "emailAddress") == 0) { + nid = NID_pkcs9_emailAddress; + dn_cnt->email++; + } else { + wpa_printf(MSG_ERROR, + "TLS: Unknown field '%s' in check_cert_subject", name); + return 0; + } + + value = str_token(field_str, "=", &context); + if (!value) { + wpa_printf(MSG_ERROR, + "TLS: Distinguished Name field '%s' value is not defined in check_cert_subject", + name); + return 0; + } + + return match_dn_field(cert, nid, name, value, dn_cnt); +} + + +/** + * tls_match_dn_field - Match subject DN field with check_cert_subject + * @cert: Certificate + * @match: check_cert_subject string + * Returns: Return 1 on success and 0 on failure +*/ +static int tls_match_dn_field(X509 *cert, const char *match) +{ + const char *token, *last = NULL; + char field[256]; + struct tls_dn_field_order_cnt dn_cnt; + + os_memset(&dn_cnt, 0, sizeof(dn_cnt)); + + /* Maximum length of each DN field is 255 characters */ + + /* Process each '/' delimited field */ + while ((token = cstr_token(match, "/", &last))) { + if (last - token >= (int) sizeof(field)) { + wpa_printf(MSG_ERROR, + "OpenSSL: Too long DN matching field value in '%s'", + match); + return 0; + } + os_memcpy(field, token, last - token); + field[last - token] = '\0'; + + if (!get_value_from_field(cert, field, &dn_cnt)) { + wpa_printf(MSG_DEBUG, "OpenSSL: No match for DN '%s'", + field); + return 0; + } + } + + return 1; +} + + +#ifndef CONFIG_NATIVE_WINDOWS +static int tls_match_suffix_helper(X509 *cert, const char *match, + size_t match_len, int full) { -#ifdef CONFIG_NATIVE_WINDOWS - /* wincrypt.h has conflicting X509_NAME definition */ - return -1; -#else /* CONFIG_NATIVE_WINDOWS */ GENERAL_NAME *gen; void *ext; int i; @@ -1777,8 +2004,8 @@ static int tls_match_suffix(X509 *cert, const char *match, int full) gen->d.dNSName->data, gen->d.dNSName->length); if (domain_suffix_match(gen->d.dNSName->data, - gen->d.dNSName->length, match, full) == - 1) { + gen->d.dNSName->length, + match, match_len, full) == 1) { wpa_printf(MSG_DEBUG, "TLS: %s in dNSName found", full ? "Match" : "Suffix match"); sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free); @@ -1809,8 +2036,8 @@ static int tls_match_suffix(X509 *cert, const char *match, int full) continue; wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate commonName", cn->data, cn->length); - if (domain_suffix_match(cn->data, cn->length, match, full) == 1) - { + if (domain_suffix_match(cn->data, cn->length, + match, match_len, full) == 1) { wpa_printf(MSG_DEBUG, "TLS: %s in commonName found", full ? "Match" : "Suffix match"); return 1; @@ -1820,6 +2047,25 @@ static int tls_match_suffix(X509 *cert, const char *match, int full) wpa_printf(MSG_DEBUG, "TLS: No CommonName %smatch found", full ? "": "suffix "); return 0; +} +#endif /* CONFIG_NATIVE_WINDOWS */ + + +static int tls_match_suffix(X509 *cert, const char *match, int full) +{ +#ifdef CONFIG_NATIVE_WINDOWS + /* wincrypt.h has conflicting X509_NAME definition */ + return -1; +#else /* CONFIG_NATIVE_WINDOWS */ + const char *token, *last = NULL; + + /* Process each match alternative separately until a match is found */ + while ((token = cstr_token(match, ";", &last))) { + if (tls_match_suffix_helper(cert, token, last - token, full)) + return 1; + } + + return 0; #endif /* CONFIG_NATIVE_WINDOWS */ } @@ -2014,6 +2260,7 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) struct tls_connection *conn; struct tls_context *context; char *match, *altmatch, *suffix_match, *domain_match; + const char *check_cert_subject; const char *err_str; err_cert = X509_STORE_CTX_get_current_cert(x509_ctx); @@ -2114,6 +2361,18 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) "err=%d (%s) ca_cert_verify=%d depth=%d buf='%s'", preverify_ok, err, err_str, conn->ca_cert_verify, depth, buf); + check_cert_subject = conn->check_cert_subject; + if (!check_cert_subject) + check_cert_subject = conn->data->check_cert_subject; + if (check_cert_subject) { + if (depth == 0 && + !tls_match_dn_field(err_cert, check_cert_subject)) { + preverify_ok = 0; + openssl_tls_fail_event(conn, err_cert, err, depth, buf, + "Distinguished Name", + TLS_FAIL_DN_MISMATCH); + } + } if (depth == 0 && match && os_strstr(buf, match) == NULL) { wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not " "match with '%s'", buf, match); @@ -2490,7 +2749,8 @@ static int tls_connection_set_subject_match(struct tls_connection *conn, const char *subject_match, const char *altsubject_match, const char *suffix_match, - const char *domain_match) + const char *domain_match, + const char *check_cert_subject) { os_free(conn->subject_match); conn->subject_match = NULL; @@ -2524,6 +2784,14 @@ static int tls_connection_set_subject_match(struct tls_connection *conn, return -1; } + os_free(conn->check_cert_subject); + conn->check_cert_subject = NULL; + if (check_cert_subject) { + conn->check_cert_subject = os_strdup(check_cert_subject); + if (!conn->check_cert_subject) + return -1; + } + return 0; } @@ -2867,7 +3135,8 @@ static int tls_connection_client_cert(struct tls_connection *conn, return 0; } -#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ + !defined(LIBRESSL_VERSION_NUMBER) && !defined(OPENSSL_IS_BORINGSSL) if (SSL_use_certificate_chain_file(conn->ssl, client_cert) == 1) { ERR_clear_error(); wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_chain_file" @@ -3656,11 +3925,13 @@ static int openssl_get_keyblock_size(SSL *ssl) int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn, - const char *label, u8 *out, size_t out_len) + const char *label, const u8 *context, + size_t context_len, u8 *out, size_t out_len) { if (!conn || SSL_export_keying_material(conn->ssl, out, out_len, label, - os_strlen(label), NULL, 0, 0) != 1) + os_strlen(label), context, context_len, + context != NULL) != 1) return -1; return 0; } @@ -4578,7 +4849,8 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, params->subject_match, params->altsubject_match, params->suffix_match, - params->domain_match)) + params->domain_match, + params->check_cert_subject)) return -1; if (engine_id && ca_cert_id) { @@ -4719,6 +4991,15 @@ int tls_global_set_params(void *tls_ctx, __func__, ERR_error_string(err, NULL)); } + os_free(data->check_cert_subject); + data->check_cert_subject = NULL; + if (params->check_cert_subject) { + data->check_cert_subject = + os_strdup(params->check_cert_subject); + if (!data->check_cert_subject) + return -1; + } + if (tls_global_ca_cert(data, params->ca_cert) || tls_global_client_cert(data, params->client_cert) || tls_global_private_key(data, params->private_key, @@ -4756,6 +5037,9 @@ int tls_global_set_params(void *tls_ctx, return -1; #else /* OPENSSL_IS_BORINGSSL || < 1.0.2 */ #ifndef OPENSSL_NO_EC +#if OPENSSL_VERSION_NUMBER < 0x10100000L + SSL_CTX_set_ecdh_auto(ssl_ctx, 1); +#endif if (SSL_CTX_set1_curves_list(ssl_ctx, params->openssl_ecdh_curves) != 1) { diff --git a/src/crypto/tls_wolfssl.c b/src/crypto/tls_wolfssl.c index b59622e..e9cb425 100644 --- a/src/crypto/tls_wolfssl.c +++ b/src/crypto/tls_wolfssl.c @@ -643,9 +643,9 @@ static int tls_match_alt_subject(WOLFSSL_X509 *cert, const char *match) static int domain_suffix_match(const char *val, size_t len, const char *match, - int full) + size_t match_len, int full) { - size_t i, match_len; + size_t i; /* Check for embedded nuls that could mess up suffix matching */ for (i = 0; i < len; i++) { @@ -656,7 +656,6 @@ static int domain_suffix_match(const char *val, size_t len, const char *match, } } - match_len = os_strlen(match); if (match_len > len || (full && match_len != len)) return 0; @@ -674,7 +673,8 @@ static int domain_suffix_match(const char *val, size_t len, const char *match, } -static int tls_match_suffix(WOLFSSL_X509 *cert, const char *match, int full) +static int tls_match_suffix_helper(WOLFSSL_X509 *cert, const char *match, + size_t match_len, int full) { WOLFSSL_ASN1_OBJECT *gen; void *ext; @@ -690,14 +690,14 @@ static int tls_match_suffix(WOLFSSL_X509 *cert, const char *match, int full) for (j = 0; ext && j < wolfSSL_sk_num(ext); j++) { gen = wolfSSL_sk_value(ext, j); - if (gen->type != ALT_NAMES_OID) + if (gen->type != ASN_DNS_TYPE) continue; dns_name++; wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate dNSName", gen->obj, os_strlen((char *)gen->obj)); if (domain_suffix_match((const char *) gen->obj, os_strlen((char *) gen->obj), match, - full) == 1) { + match_len, full) == 1) { wpa_printf(MSG_DEBUG, "TLS: %s in dNSName found", full ? "Match" : "Suffix match"); wolfSSL_sk_ASN1_OBJECT_free(ext); @@ -729,8 +729,8 @@ static int tls_match_suffix(WOLFSSL_X509 *cert, const char *match, int full) continue; wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate commonName", cn->data, cn->length); - if (domain_suffix_match(cn->data, cn->length, match, full) == 1) - { + if (domain_suffix_match(cn->data, cn->length, + match, match_len, full) == 1) { wpa_printf(MSG_DEBUG, "TLS: %s in commonName found", full ? "Match" : "Suffix match"); return 1; @@ -743,6 +743,20 @@ static int tls_match_suffix(WOLFSSL_X509 *cert, const char *match, int full) } +static int tls_match_suffix(WOLFSSL_X509 *cert, const char *match, int full) +{ + const char *token, *last = NULL; + + /* Process each match alternative separately until a match is found */ + while ((token = cstr_token(match, ";", &last))) { + if (tls_match_suffix_helper(cert, token, last - token, full)) + return 1; + } + + return 0; +} + + static enum tls_fail_reason wolfssl_tls_fail_reason(int err) { switch (err) { @@ -1487,6 +1501,9 @@ int tls_global_set_params(void *tls_ctx, { wpa_printf(MSG_DEBUG, "SSL: global set params"); + if (params->check_cert_subject) + return -1; /* not yet supported */ + if (tls_global_ca_cert(tls_ctx, params->ca_cert) < 0) { wpa_printf(MSG_INFO, "SSL: Failed to load ca cert file '%s'", params->ca_cert); @@ -1970,8 +1987,11 @@ int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn, int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn, - const char *label, u8 *out, size_t out_len) + const char *label, const u8 *context, + size_t context_len, u8 *out, size_t out_len) { + if (context) + return -1; if (!conn || wolfSSL_make_eap_keys(conn->ssl, out, out_len, label) != 0) return -1; return 0; diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 23423d9..e7c8f31 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -2156,17 +2156,19 @@ enum wpa_drv_update_connect_params_mask { * use %WLAN_STATUS_UNSPECIFIED_FAILURE if wpa_supplicant cannot give * the real status code for failures. Used only for the request interface * from user space to the driver. + * @pmkid: Generated PMKID as part of external auth exchange (e.g., SAE). */ struct external_auth { enum { EXT_AUTH_START, EXT_AUTH_ABORT, } action; - u8 bssid[ETH_ALEN]; - u8 ssid[SSID_MAX_LEN]; + const u8 *bssid; + const u8 *ssid; size_t ssid_len; unsigned int key_mgmt_suite; u16 status; + const u8 *pmkid; }; /** diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c index 62f5baa..807cd94 100644 --- a/src/drivers/driver_atheros.c +++ b/src/drivers/driver_atheros.c @@ -1218,8 +1218,7 @@ atheros_new_sta(struct atheros_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) #ifdef ATH_WPS_IE /* if WPS IE is present, preference is given to WPS */ - if (ie.wps_ie && - (ie.wps_ie[1] > 0 && (ie.wps_ie[0] == WLAN_EID_VENDOR_SPECIFIC))) { + if (ie.wps_ie[0] == WLAN_EID_VENDOR_SPECIFIC && ie.wps_ie[1] > 0) { iebuf = ie.wps_ie; ielen = ie.wps_ie[1]; } diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c index 8621aa0..4675496 100644 --- a/src/drivers/driver_bsd.c +++ b/src/drivers/driver_bsd.c @@ -143,7 +143,7 @@ bsd_get80211(void *priv, struct ieee80211req *ireq, int op, void *arg, ireq->i_data = arg; if (ioctl(drv->global->sock, SIOCG80211, ireq) < 0) { - wpa_printf(MSG_ERROR, "ioctl[SIOCS80211, op=%u, " + wpa_printf(MSG_ERROR, "ioctl[SIOCG80211, op=%u, " "arg_len=%u]: %s", op, arg_len, strerror(errno)); return -1; } diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index a24497f..54fe390 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -40,6 +40,9 @@ #include "driver_nl80211.h" +#ifndef NETLINK_CAP_ACK +#define NETLINK_CAP_ACK 10 +#endif /* NETLINK_CAP_ACK */ /* support for extack if compilation headers are too old */ #ifndef NETLINK_EXT_ACK #define NETLINK_EXT_ACK 11 @@ -304,6 +307,7 @@ void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv) os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN); drv->associated = 0; os_memset(drv->bssid, 0, ETH_ALEN); + drv->first_bss->freq = 0; } @@ -406,6 +410,11 @@ static int send_and_recv(struct nl80211_global *global, setsockopt(nl_socket_get_fd(nl_handle), SOL_NETLINK, NETLINK_EXT_ACK, &opt, sizeof(opt)); + /* try to set NETLINK_CAP_ACK to 1, ignoring errors */ + opt = 1; + setsockopt(nl_socket_get_fd(nl_handle), SOL_NETLINK, + NETLINK_CAP_ACK, &opt, sizeof(opt)); + err = nl_send_auto_complete(nl_handle, msg); if (err < 0) goto out; @@ -2461,6 +2470,16 @@ static int nl80211_mgmt_subscribe_ap_dev_sme(struct i802_bss *bss) if (nl80211_action_subscribe_ap(bss)) goto out_err; + if (bss->drv->device_ap_sme) { + u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_AUTH << 4); + + /* Register for all Authentication frames */ + if (nl80211_register_frame(bss, bss->nl_mgmt, type, NULL, 0) + < 0) + wpa_printf(MSG_DEBUG, + "nl80211: Failed to subscribe to handle Authentication frames - SAE offload may not work"); + } + nl80211_mgmt_handle_register_eloop(bss); return 0; @@ -3469,8 +3488,8 @@ retry: goto fail; } if (params->ssid) { - wpa_hexdump_ascii(MSG_DEBUG, " * SSID", - params->ssid, params->ssid_len); + wpa_printf(MSG_DEBUG, " * SSID=%s", + wpa_ssid_txt(params->ssid, params->ssid_len)); if (nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid)) goto fail; @@ -4066,8 +4085,8 @@ static int wpa_driver_nl80211_set_ap(void *priv, wpa_printf(MSG_DEBUG, "nl80211: beacon_rate=%u", params->beacon_rate); wpa_printf(MSG_DEBUG, "nl80211: rate_type=%d", params->rate_type); wpa_printf(MSG_DEBUG, "nl80211: dtim_period=%d", params->dtim_period); - wpa_hexdump_ascii(MSG_DEBUG, "nl80211: ssid", - params->ssid, params->ssid_len); + wpa_printf(MSG_DEBUG, "nl80211: ssid=%s", + wpa_ssid_txt(params->ssid, params->ssid_len)); if (!(msg = nl80211_bss_msg(bss, 0, cmd)) || nla_put(msg, NL80211_ATTR_BEACON_HEAD, params->head_len, params->head) || @@ -4152,6 +4171,11 @@ static int wpa_driver_nl80211_set_ap(void *priv, nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT))) goto fail; + if (drv->device_ap_sme && + (params->key_mgmt_suites & WPA_KEY_MGMT_SAE) && + nla_put_flag(msg, NL80211_ATTR_EXTERNAL_AUTH_SUPPORT)) + goto fail; + wpa_printf(MSG_DEBUG, "nl80211: pairwise_ciphers=0x%x", params->pairwise_ciphers); num_suites = wpa_cipher_to_cipher_suites(params->pairwise_ciphers, @@ -4671,7 +4695,8 @@ static int wpa_driver_nl80211_sta_add(void *priv, goto fail; #endif /* CONFIG_MESH */ - if (params->flags & WPA_STA_WMM) { + if ((!params->set || FULL_AP_CLIENT_STATE_SUPP(drv->capa.flags)) && + (params->flags & WPA_STA_WMM)) { struct nlattr *wme = nla_nest_start(msg, NL80211_ATTR_STA_WME); wpa_printf(MSG_DEBUG, " * qosinfo=0x%x", params->qosinfo); @@ -5281,8 +5306,8 @@ retry: params->ssid == NULL || params->ssid_len > sizeof(drv->ssid)) goto fail; - wpa_hexdump_ascii(MSG_DEBUG, " * SSID", - params->ssid, params->ssid_len); + wpa_printf(MSG_DEBUG, " * SSID=%s", + wpa_ssid_txt(params->ssid, params->ssid_len)); if (nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid)) goto fail; os_memcpy(drv->ssid, params->ssid, params->ssid_len); @@ -5443,8 +5468,8 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, } if (params->ssid) { - wpa_hexdump_ascii(MSG_DEBUG, " * SSID", - params->ssid, params->ssid_len); + wpa_printf(MSG_DEBUG, " * SSID=%s", + wpa_ssid_txt(params->ssid, params->ssid_len)); if (nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid)) return -1; @@ -5502,8 +5527,11 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, params->key_mgmt_suite == WPA_KEY_MGMT_OSEN || params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 || params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 || + params->key_mgmt_suite == WPA_KEY_MGMT_SAE || + params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE || params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B || params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 || + params->key_mgmt_suite == WPA_KEY_MGMT_FT_IEEE8021X_SHA384 || params->key_mgmt_suite == WPA_KEY_MGMT_FILS_SHA256 || params->key_mgmt_suite == WPA_KEY_MGMT_FILS_SHA384 || params->key_mgmt_suite == WPA_KEY_MGMT_FT_FILS_SHA256 || @@ -5534,12 +5562,21 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, case WPA_KEY_MGMT_OSEN: mgmt = RSN_AUTH_KEY_MGMT_OSEN; break; + case WPA_KEY_MGMT_SAE: + mgmt = RSN_AUTH_KEY_MGMT_SAE; + break; + case WPA_KEY_MGMT_FT_SAE: + mgmt = RSN_AUTH_KEY_MGMT_FT_SAE; + break; case WPA_KEY_MGMT_IEEE8021X_SUITE_B: mgmt = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B; break; case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192: mgmt = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192; break; + case WPA_KEY_MGMT_FT_IEEE8021X_SHA384: + mgmt = RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384; + break; case WPA_KEY_MGMT_FILS_SHA256: mgmt = RSN_AUTH_KEY_MGMT_FILS_SHA256; break; @@ -5708,9 +5745,10 @@ skip_auth_type: goto fail; if (nl_connect) - ret = send_and_recv(drv->global, nl_connect, msg, NULL, NULL); + ret = send_and_recv(drv->global, nl_connect, msg, + NULL, (void *) -1); else - ret = send_and_recv_msgs(drv, msg, NULL, NULL); + ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1); msg = NULL; if (ret) { @@ -5722,6 +5760,7 @@ skip_auth_type: } fail: + nl80211_nlmsg_clear(msg); nlmsg_free(msg); return ret; @@ -6122,6 +6161,7 @@ static int get_key_handler(struct nl_msg *msg, void *arg) if (tb[NL80211_ATTR_KEY_SEQ]) memcpy(arg, nla_data(tb[NL80211_ATTR_KEY_SEQ]), min_int(nla_len(tb[NL80211_ATTR_KEY_SEQ]), 6)); + nl80211_nlmsg_clear(msg); return NL_SKIP; } @@ -6156,7 +6196,7 @@ static int i802_set_rts(void *priv, int rts) int ret; u32 val; - if (rts >= 2347) + if (rts >= 2347 || rts == -1) val = (u32) -1; else val = rts; @@ -6184,7 +6224,7 @@ static int i802_set_frag(void *priv, int frag) int ret; u32 val; - if (frag >= 2346) + if (frag >= 2346 || frag == -1) val = (u32) -1; else val = frag; @@ -7917,13 +7957,15 @@ static int nl80211_pmkid(struct i802_bss *bss, int cmd, (params->fils_cache_id && nla_put(msg, NL80211_ATTR_FILS_CACHE_ID, 2, params->fils_cache_id)) || - (params->pmk_len && params->pmk_len <= PMK_MAX_LEN && + (cmd != NL80211_CMD_DEL_PMKSA && + params->pmk_len && params->pmk_len <= PMK_MAX_LEN && nla_put(msg, NL80211_ATTR_PMK, params->pmk_len, params->pmk))) { + nl80211_nlmsg_clear(msg); nlmsg_free(msg); return -ENOBUFS; } - return send_and_recv_msgs(bss->drv, msg, NULL, NULL); + return send_and_recv_msgs(bss->drv, msg, NULL, (void *) -1); } @@ -8246,6 +8288,7 @@ static void nl80211_poll_client(void *priv, const u8 *own_addr, const u8 *addr, struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; + u64 cookie; int ret; if (!drv->poll_command_supported) { @@ -8259,11 +8302,16 @@ static void nl80211_poll_client(void *priv, const u8 *own_addr, const u8 *addr, return; } - ret = send_and_recv_msgs(drv, msg, NULL, NULL); + ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie); if (ret < 0) { wpa_printf(MSG_DEBUG, "nl80211: Client probe request for " MACSTR " failed: ret=%d (%s)", MAC2STR(addr), ret, strerror(-ret)); + } else { + wpa_printf(MSG_DEBUG, + "nl80211: Client probe request addr=" MACSTR + " cookie=%llu", MAC2STR(addr), + (long long unsigned int) cookie); } } @@ -9424,8 +9472,8 @@ static int nl80211_put_mesh_id(struct nl_msg *msg, const u8 *mesh_id, size_t mesh_id_len) { if (mesh_id) { - wpa_hexdump_ascii(MSG_DEBUG, " * Mesh ID (SSID)", - mesh_id, mesh_id_len); + wpa_printf(MSG_DEBUG, " * Mesh ID (SSID)=%s", + wpa_ssid_txt(mesh_id, mesh_id_len)); return nla_put(msg, NL80211_ATTR_MESH_ID, mesh_id_len, mesh_id); } @@ -10766,15 +10814,27 @@ static int nl80211_send_external_auth_status(void *priv, struct nl_msg *msg = NULL; int ret = -1; + /* External auth command/status is intended for drivers that implement + * intenral SME but want to offload authentication processing (e.g., + * SAE) to hostapd/wpa_supplicant. Do nott send the status to drivers + * which do not support AP SME or use wpa_supplicant/hostapd SME. + */ + if (!bss->drv->device_ap_sme || + (drv->capa.flags & WPA_DRIVER_FLAGS_SME)) + return -1; + wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: External auth status: %u", params->status); msg = nl80211_drv_msg(drv, 0, NL80211_CMD_EXTERNAL_AUTH); if (!msg || nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, params->status) || - nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, - params->ssid) || - nla_put(msg, NL80211_ATTR_BSSID, ETH_ALEN, params->bssid)) + (params->ssid && params->ssid_len && + nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid)) || + (params->pmkid && + nla_put(msg, NL80211_ATTR_PMKID, PMKID_LEN, params->pmkid)) || + (params->bssid && + nla_put(msg, NL80211_ATTR_BSSID, ETH_ALEN, params->bssid))) goto fail; ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c index 06e619b..ee7b4da 100644 --- a/src/drivers/driver_nl80211_event.c +++ b/src/drivers/driver_nl80211_event.c @@ -206,7 +206,8 @@ static void nl80211_parse_wmm_params(struct nlattr *wmm_attr, static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv, - const u8 *frame, size_t len, struct nlattr *wmm) + const u8 *frame, size_t len, struct nlattr *wmm, + struct nlattr *req_ie) { const struct ieee80211_mgmt *mgmt; union wpa_event_data event; @@ -261,7 +262,10 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv, len - 24 - sizeof(mgmt->u.assoc_resp); } - event.assoc_info.freq = drv->assoc_freq; + if (req_ie) { + event.assoc_info.req_ies = nla_data(req_ie); + event.assoc_info.req_ies_len = nla_len(req_ie); + } /* When this association was initiated outside of wpa_supplicant, * drv->ssid needs to be set here to satisfy later checking. */ @@ -273,6 +277,9 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv, wpa_ssid_txt(drv->ssid, drv->ssid_len)); } + event.assoc_info.freq = drv->assoc_freq; + drv->first_bss->freq = drv->assoc_freq; + nl80211_parse_wmm_params(wmm, &event.assoc_info.wmm_params); wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event); @@ -402,6 +409,7 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, } event.assoc_info.freq = nl80211_get_assoc_freq(drv); + drv->first_bss->freq = drv->assoc_freq; if ((!ssid || ssid[1] == 0 || ssid[1] > 32) && (ssid_len = nl80211_get_assoc_ssid(drv, drv->ssid)) > 0) { @@ -868,7 +876,7 @@ static void mlme_event(struct i802_bss *bss, struct nlattr *addr, struct nlattr *timed_out, struct nlattr *freq, struct nlattr *ack, struct nlattr *cookie, struct nlattr *sig, - struct nlattr *wmm) + struct nlattr *wmm, struct nlattr *req_ie) { struct wpa_driver_nl80211_data *drv = bss->drv; const u8 *data; @@ -917,7 +925,8 @@ static void mlme_event(struct i802_bss *bss, mlme_event_auth(drv, nla_data(frame), nla_len(frame)); break; case NL80211_CMD_ASSOCIATE: - mlme_event_assoc(drv, nla_data(frame), nla_len(frame), wmm); + mlme_event_assoc(drv, nla_data(frame), nla_len(frame), wmm, + req_ie); break; case NL80211_CMD_DEAUTHENTICATE: mlme_event_deauth_disassoc(drv, EVENT_DEAUTH, @@ -1429,16 +1438,23 @@ static void nl80211_client_probe_event(struct wpa_driver_nl80211_data *drv, struct nlattr **tb) { union wpa_event_data data; + const u8 *addr; + u64 cookie = 0; - wpa_printf(MSG_DEBUG, "nl80211: Probe client event"); - - if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_ACK]) + addr = nla_data(tb[NL80211_ATTR_MAC]); + if (!addr) + return; + if (tb[NL80211_ATTR_COOKIE]) + cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]); + wpa_printf(MSG_DEBUG, "nl80211: Probe client event (addr=" MACSTR + " ack=%d cookie=%llu)", MAC2STR(addr), + tb[NL80211_ATTR_ACK] != NULL, + (long long unsigned int) cookie); + if (!tb[NL80211_ATTR_ACK]) return; os_memset(&data, 0, sizeof(data)); - os_memcpy(data.client_poll.addr, - nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); - + os_memcpy(data.client_poll.addr, addr, ETH_ALEN); wpa_supplicant_event(drv->ctx, EVENT_DRIVER_CLIENT_POLL_OK, &data); } @@ -2262,11 +2278,9 @@ static void nl80211_external_auth(struct wpa_driver_nl80211_data *drv, event.external_auth.ssid_len = nla_len(tb[NL80211_ATTR_SSID]); if (event.external_auth.ssid_len > SSID_MAX_LEN) return; - os_memcpy(event.external_auth.ssid, nla_data(tb[NL80211_ATTR_SSID]), - event.external_auth.ssid_len); + event.external_auth.ssid = nla_data(tb[NL80211_ATTR_SSID]); - os_memcpy(event.external_auth.bssid, nla_data(tb[NL80211_ATTR_BSSID]), - ETH_ALEN); + event.external_auth.bssid = nla_data(tb[NL80211_ATTR_BSSID]); wpa_printf(MSG_DEBUG, "nl80211: External auth action: %u, AKM: 0x%x", @@ -2475,7 +2489,8 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK], tb[NL80211_ATTR_COOKIE], tb[NL80211_ATTR_RX_SIGNAL_DBM], - tb[NL80211_ATTR_STA_WME]); + tb[NL80211_ATTR_STA_WME], + tb[NL80211_ATTR_REQ_IE]); break; case NL80211_CMD_CONNECT: case NL80211_CMD_ROAM: @@ -2648,7 +2663,7 @@ int process_bss_event(struct nl_msg *msg, void *arg) tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK], tb[NL80211_ATTR_COOKIE], tb[NL80211_ATTR_RX_SIGNAL_DBM], - tb[NL80211_ATTR_STA_WME]); + tb[NL80211_ATTR_STA_WME], NULL); break; case NL80211_CMD_UNEXPECTED_FRAME: nl80211_spurious_frame(bss, tb, 0); diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c index 33a8d35..9afa5b3 100644 --- a/src/drivers/driver_nl80211_scan.c +++ b/src/drivers/driver_nl80211_scan.c @@ -197,9 +197,9 @@ nl80211_scan_common(struct i802_bss *bss, u8 cmd, if (ssids == NULL) goto fail; for (i = 0; i < params->num_ssids; i++) { - wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan SSID", - params->ssids[i].ssid, - params->ssids[i].ssid_len); + wpa_printf(MSG_MSGDUMP, "nl80211: Scan SSID %s", + wpa_ssid_txt(params->ssids[i].ssid, + params->ssids[i].ssid_len)); if (nla_put(msg, i + 1, params->ssids[i].ssid_len, params->ssids[i].ssid)) goto fail; @@ -537,10 +537,10 @@ int wpa_driver_nl80211_sched_scan(void *priv, for (i = 0; i < drv->num_filter_ssids; i++) { struct nlattr *match_set_ssid; - wpa_hexdump_ascii(MSG_MSGDUMP, - "nl80211: Sched scan filter SSID", - drv->filter_ssids[i].ssid, - drv->filter_ssids[i].ssid_len); + wpa_printf(MSG_MSGDUMP, + "nl80211: Sched scan filter SSID %s", + wpa_ssid_txt(drv->filter_ssids[i].ssid, + drv->filter_ssids[i].ssid_len)); match_set_ssid = nla_nest_start(msg, i + 1); if (match_set_ssid == NULL || @@ -1098,9 +1098,9 @@ int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss, if (ssids == NULL) goto fail; for (i = 0; i < params->num_ssids; i++) { - wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan SSID", - params->ssids[i].ssid, - params->ssids[i].ssid_len); + wpa_printf(MSG_MSGDUMP, "nl80211: Scan SSID %s", + wpa_ssid_txt(params->ssids[i].ssid, + params->ssids[i].ssid_len)); if (nla_put(msg, i + 1, params->ssids[i].ssid_len, params->ssids[i].ssid)) goto fail; diff --git a/src/drivers/driver_roboswitch.c b/src/drivers/driver_roboswitch.c index e8a5135..9beb6c4 100644 --- a/src/drivers/driver_roboswitch.c +++ b/src/drivers/driver_roboswitch.c @@ -290,21 +290,26 @@ static int wpa_driver_roboswitch_leave(struct wpa_driver_roboswitch_data *drv, wpa_driver_roboswitch_addr_be16(addr, addr_be16); - wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_CONF, - &_read, 1); + if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_CONF, &_read, 1) < 0) + return -1; /* If ARL control is disabled, there is nothing to leave. */ if (!(_read & (1 << 4))) return -1; - wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, - ROBO_ARLCTRL_ADDR_1, addr_read, 3); - wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_VEC_1, - &ports_read, 1); + if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_1, addr_read, 3) < 0 || + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_1, &ports_read, 1) < 0) + return -1; /* check if we occupy multiport address 1 */ if (os_memcmp(addr_read, addr_be16, 6) == 0 && ports_read == ports) { - wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, - ROBO_ARLCTRL_ADDR_2, addr_read, 3); - wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, - ROBO_ARLCTRL_VEC_2, &ports_read, 1); + if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_2, addr_read, + 3) < 0 || + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_2, &ports_read, + 1) < 0) + return -1; /* and multiport address 2 */ if (os_memcmp(addr_read, addr_be16, 6) == 0 && ports_read == ports) { @@ -327,10 +332,13 @@ static int wpa_driver_roboswitch_leave(struct wpa_driver_roboswitch_data *drv, &ports_read, 1); } } else { - wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, - ROBO_ARLCTRL_ADDR_2, addr_read, 3); - wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, - ROBO_ARLCTRL_VEC_2, &ports_read, 1); + if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_2, addr_read, + 3) < 0 || + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_2, &ports_read, + 1) < 0) + return -1; /* or multiport address 2 */ if (os_memcmp(addr_read, addr_be16, 6) == 0 && ports_read == ports) { diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h index 31ae5c7..dd4f86e 100644 --- a/src/drivers/nl80211_copy.h +++ b/src/drivers/nl80211_copy.h @@ -1565,6 +1565,12 @@ enum nl80211_commands { * (a u32 with flags from &enum nl80211_wpa_versions). * @NL80211_ATTR_AKM_SUITES: Used with CONNECT, ASSOCIATE, and NEW_BEACON to * indicate which key management algorithm(s) to use (an array of u32). + * This attribute is also sent in response to @NL80211_CMD_GET_WIPHY, + * indicating the supported AKM suites, intended for specific drivers which + * implement SME and have constraints on which AKMs are supported and also + * the cases where an AKM support is offloaded to the driver/firmware. + * If there is no such notification from the driver, user space should + * assume the driver supports all the AKM suites. * * @NL80211_ATTR_REQ_IE: (Re)association request information elements as * sent out by the card, for ROAM and successful CONNECT events. @@ -2260,10 +2266,10 @@ enum nl80211_commands { * &enum nl80211_external_auth_action value). This is used with the * %NL80211_CMD_EXTERNAL_AUTH request event. * @NL80211_ATTR_EXTERNAL_AUTH_SUPPORT: Flag attribute indicating that the user - * space supports external authentication. This attribute shall be used - * only with %NL80211_CMD_CONNECT request. The driver may offload - * authentication processing to user space if this capability is indicated - * in NL80211_CMD_CONNECT requests from the user space. + * space supports external authentication. This attribute shall be used + * with %NL80211_CMD_CONNECT and %NL80211_CMD_START_AP request. The driver + * may offload authentication processing to user space if this capability + * is indicated in the respective requests from the user space. * * @NL80211_ATTR_NSS: Station's New/updated RX_NSS value notified using this * u8 attribute. This is used with %NL80211_CMD_STA_OPMODE_CHANGED. @@ -2299,6 +2305,9 @@ enum nl80211_commands { * This is also used for capability advertisement in the wiphy information, * with the appropriate sub-attributes. * + * @NL80211_ATTR_AIRTIME_WEIGHT: Station's weight when scheduled by the airtime + * scheduler. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -2748,6 +2757,8 @@ enum nl80211_attrs { NL80211_ATTR_PEER_MEASUREMENTS, + NL80211_ATTR_AIRTIME_WEIGHT, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -3125,6 +3136,9 @@ enum nl80211_sta_bss_param { * might not be fully accurate. * @NL80211_STA_INFO_CONNECTED_TO_GATE: set to true if STA has a path to a * mesh gate (u8, 0 or 1) + * @NL80211_STA_INFO_TX_DURATION: aggregate PPDU duration for all frames + * sent to the station (u64, usec) + * @NL80211_STA_INFO_AIRTIME_WEIGHT: current airtime weight for station (u16) * @__NL80211_STA_INFO_AFTER_LAST: internal * @NL80211_STA_INFO_MAX: highest possible station info attribute */ @@ -3168,6 +3182,8 @@ enum nl80211_sta_info { NL80211_STA_INFO_RX_MPDUS, NL80211_STA_INFO_FCS_ERROR_COUNT, NL80211_STA_INFO_CONNECTED_TO_GATE, + NL80211_STA_INFO_TX_DURATION, + NL80211_STA_INFO_AIRTIME_WEIGHT, /* keep last */ __NL80211_STA_INFO_AFTER_LAST, @@ -3277,8 +3293,10 @@ enum nl80211_mpath_flags { * &enum nl80211_mpath_flags; * @NL80211_MPATH_INFO_DISCOVERY_TIMEOUT: total path discovery timeout, in msec * @NL80211_MPATH_INFO_DISCOVERY_RETRIES: mesh path discovery retries + * @NL80211_MPATH_INFO_HOP_COUNT: hop count to destination + * @NL80211_MPATH_INFO_PATH_CHANGE: total number of path changes to destination * @NL80211_MPATH_INFO_MAX: highest mesh path information attribute number - * currently defind + * currently defined * @__NL80211_MPATH_INFO_AFTER_LAST: internal use */ enum nl80211_mpath_info { @@ -3290,6 +3308,8 @@ enum nl80211_mpath_info { NL80211_MPATH_INFO_FLAGS, NL80211_MPATH_INFO_DISCOVERY_TIMEOUT, NL80211_MPATH_INFO_DISCOVERY_RETRIES, + NL80211_MPATH_INFO_HOP_COUNT, + NL80211_MPATH_INFO_PATH_CHANGE, /* keep last */ __NL80211_MPATH_INFO_AFTER_LAST, @@ -5316,6 +5336,13 @@ enum nl80211_feature_flags { * if this flag is not set. Ignoring this can leak clear text packets and/or * freeze the connection. * + * @NL80211_EXT_FEATURE_AIRTIME_FAIRNESS: Driver supports getting airtime + * fairness for transmitted packets and has enabled airtime fairness + * scheduling. + * + * @NL80211_EXT_FEATURE_AP_PMKSA_CACHING: Driver/device supports PMKSA caching + * (set/del PMKSA operations) in AP mode. + * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. */ @@ -5355,6 +5382,8 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT, NL80211_EXT_FEATURE_CAN_REPLACE_PTK0, NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER, + NL80211_EXT_FEATURE_AIRTIME_FAIRNESS, + NL80211_EXT_FEATURE_AP_PMKSA_CACHING, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, @@ -5606,9 +5635,14 @@ enum nl80211_crit_proto_id { * Used by cfg80211_rx_mgmt() * * @NL80211_RXMGMT_FLAG_ANSWERED: frame was answered by device/driver. + * @NL80211_RXMGMT_FLAG_EXTERNAL_AUTH: Host driver intends to offload + * the authentication. Exclusively defined for host drivers that + * advertises the SME functionality but would like the userspace + * to handle certain authentication algorithms (e.g. SAE). */ enum nl80211_rxmgmt_flags { NL80211_RXMGMT_FLAG_ANSWERED = 1 << 0, + NL80211_RXMGMT_FLAG_EXTERNAL_AUTH = 1 << 1, }; /* diff --git a/src/eap_common/eap_eke_common.c b/src/eap_common/eap_eke_common.c index bfe8811..438baf1 100644 --- a/src/eap_common/eap_eke_common.c +++ b/src/eap_common/eap_eke_common.c @@ -399,7 +399,7 @@ int eap_eke_shared_secret(struct eap_eke_session *sess, const u8 *key, /* SharedSecret = prf(0+, g ^ (x_s * x_p) (mod p)) */ len = dh->prime_len; if (crypto_dh_derive_secret(*dh->generator, dh->prime, dh->prime_len, - dhpriv, dh->prime_len, peer_pub, + NULL, 0, dhpriv, dh->prime_len, peer_pub, dh->prime_len, modexp, &len) < 0) return -1; if (len < dh->prime_len) { diff --git a/src/eap_common/eap_pwd_common.c b/src/eap_common/eap_pwd_common.c index 88c6595..884150e 100644 --- a/src/eap_common/eap_pwd_common.c +++ b/src/eap_common/eap_pwd_common.c @@ -8,11 +8,15 @@ #include "includes.h" #include "common.h" +#include "utils/const_time.h" #include "crypto/sha256.h" #include "crypto/crypto.h" #include "eap_defs.h" #include "eap_pwd_common.h" +#define MAX_ECC_PRIME_LEN 66 + + /* The random function H(x) = HMAC-SHA256(0^32, x) */ struct crypto_hash * eap_pwd_h_init(void) { @@ -81,10 +85,23 @@ static int eap_pwd_kdf(const u8 *key, size_t keylen, const u8 *label, } +static int eap_pwd_suitable_group(u16 num) +{ + /* Do not allow ECC groups with prime under 256 bits based on guidance + * for the similar design in SAE. */ + return num == 19 || num == 20 || num == 21 || + num == 28 || num == 29 || num == 30; +} + + EAP_PWD_group * get_eap_pwd_group(u16 num) { EAP_PWD_group *grp; + if (!eap_pwd_suitable_group(num)) { + wpa_printf(MSG_INFO, "EAP-pwd: unsuitable group %u", num); + return NULL; + } grp = os_zalloc(sizeof(EAP_PWD_group)); if (!grp) return NULL; @@ -102,6 +119,15 @@ EAP_PWD_group * get_eap_pwd_group(u16 num) } +static void buf_shift_right(u8 *buf, size_t len, size_t bits) +{ + size_t i; + for (i = len - 1; i > 0; i--) + buf[i] = (buf[i - 1] << (8 - bits)) | (buf[i] >> bits); + buf[0] >>= bits; +} + + /* * compute a "random" secret point on an elliptic curve based * on the password and identities. @@ -113,33 +139,37 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, const u8 *token) { struct crypto_bignum *qr = NULL, *qnr = NULL, *one = NULL; + struct crypto_bignum *qr_or_qnr = NULL; + u8 qr_bin[MAX_ECC_PRIME_LEN]; + u8 qnr_bin[MAX_ECC_PRIME_LEN]; + u8 qr_or_qnr_bin[MAX_ECC_PRIME_LEN]; + u8 x_bin[MAX_ECC_PRIME_LEN]; struct crypto_bignum *tmp1 = NULL, *tmp2 = NULL, *pm1 = NULL; struct crypto_hash *hash; unsigned char pwe_digest[SHA256_MAC_LEN], *prfbuf = NULL, ctr; - int is_odd, ret = 0, check, found = 0; - size_t primebytelen, primebitlen; - struct crypto_bignum *x_candidate = NULL, *rnd = NULL, *cofactor = NULL; + int ret = 0, check, res; + u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_* + * mask */ + size_t primebytelen = 0, primebitlen; + struct crypto_bignum *x_candidate = NULL; const struct crypto_bignum *prime; + u8 mask, found_ctr = 0, is_odd = 0; if (grp->pwe) return -1; + os_memset(x_bin, 0, sizeof(x_bin)); + prime = crypto_ec_get_prime(grp->group); - cofactor = crypto_bignum_init(); grp->pwe = crypto_ec_point_init(grp->group); tmp1 = crypto_bignum_init(); pm1 = crypto_bignum_init(); one = crypto_bignum_init_set((const u8 *) "\x01", 1); - if (!cofactor || !grp->pwe || !tmp1 || !pm1 || !one) { + if (!grp->pwe || !tmp1 || !pm1 || !one) { wpa_printf(MSG_INFO, "EAP-pwd: unable to create bignums"); goto fail; } - if (crypto_ec_cofactor(grp->group, cofactor) < 0) { - wpa_printf(MSG_INFO, "EAP-pwd: unable to get cofactor for " - "curve"); - goto fail; - } primebitlen = crypto_ec_prime_len_bits(grp->group); primebytelen = crypto_ec_prime_len(grp->group); if ((prfbuf = os_malloc(primebytelen)) == NULL) { @@ -152,8 +182,6 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, /* get a random quadratic residue and nonresidue */ while (!qr || !qnr) { - int res; - if (crypto_bignum_rand(tmp1, prime) < 0) goto fail; res = crypto_bignum_legendre(tmp1, prime); @@ -167,6 +195,11 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, if (!tmp1) goto fail; } + if (crypto_bignum_to_bin(qr, qr_bin, sizeof(qr_bin), + primebytelen) < 0 || + crypto_bignum_to_bin(qnr, qnr_bin, sizeof(qnr_bin), + primebytelen) < 0) + goto fail; os_memset(prfbuf, 0, primebytelen); ctr = 0; @@ -194,17 +227,16 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, eap_pwd_h_update(hash, &ctr, sizeof(ctr)); eap_pwd_h_final(hash, pwe_digest); - crypto_bignum_deinit(rnd, 1); - rnd = crypto_bignum_init_set(pwe_digest, SHA256_MAC_LEN); - if (!rnd) { - wpa_printf(MSG_INFO, "EAP-pwd: unable to create rnd"); - goto fail; - } + is_odd = const_time_select_u8( + found, is_odd, pwe_digest[SHA256_MAC_LEN - 1] & 0x01); if (eap_pwd_kdf(pwe_digest, SHA256_MAC_LEN, (u8 *) "EAP-pwd Hunting And Pecking", os_strlen("EAP-pwd Hunting And Pecking"), prfbuf, primebitlen) < 0) goto fail; + if (primebitlen % 8) + buf_shift_right(prfbuf, primebytelen, + 8 - primebitlen % 8); crypto_bignum_deinit(x_candidate, 1); x_candidate = crypto_bignum_init_set(prfbuf, primebytelen); @@ -214,30 +246,20 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, goto fail; } - /* - * eap_pwd_kdf() returns a string of bits 0..primebitlen but - * BN_bin2bn will treat that string of bits as a big endian - * number. If the primebitlen is not an even multiple of 8 - * then excessive bits-- those _after_ primebitlen-- so now - * we have to shift right the amount we masked off. - */ - if ((primebitlen % 8) && - crypto_bignum_rshift(x_candidate, - (8 - (primebitlen % 8)), - x_candidate) < 0) - goto fail; - if (crypto_bignum_cmp(x_candidate, prime) >= 0) continue; - wpa_hexdump(MSG_DEBUG, "EAP-pwd: x_candidate", - prfbuf, primebytelen); + wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: x_candidate", + prfbuf, primebytelen); + const_time_select_bin(found, x_bin, prfbuf, primebytelen, + x_bin); /* * compute y^2 using the equation of the curve * * y^2 = x^3 + ax + b */ + crypto_bignum_deinit(tmp2, 1); tmp2 = crypto_ec_point_compute_y_sqr(grp->group, x_candidate); if (!tmp2) goto fail; @@ -260,13 +282,15 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, * Flip a coin, multiply by the random quadratic residue or the * random quadratic nonresidue and record heads or tails. */ - if (crypto_bignum_is_odd(tmp1)) { - crypto_bignum_mulmod(tmp2, qr, prime, tmp2); - check = 1; - } else { - crypto_bignum_mulmod(tmp2, qnr, prime, tmp2); - check = -1; - } + mask = const_time_eq_u8(crypto_bignum_is_odd(tmp1), 1); + check = const_time_select_s8(mask, 1, -1); + const_time_select_bin(mask, qr_bin, qnr_bin, primebytelen, + qr_or_qnr_bin); + crypto_bignum_deinit(qr_or_qnr, 1); + qr_or_qnr = crypto_bignum_init_set(qr_or_qnr_bin, primebytelen); + if (!qr_or_qnr || + crypto_bignum_mulmod(tmp2, qr_or_qnr, prime, tmp2) < 0) + goto fail; /* * Now it's safe to do legendre, if check is 1 then it's @@ -274,59 +298,12 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, * change result), if check is -1 then it's the opposite test * (multiplying a qr by qnr would make a qnr). */ - if (crypto_bignum_legendre(tmp2, prime) == check) { - if (found == 1) - continue; - - /* need to unambiguously identify the solution */ - is_odd = crypto_bignum_is_odd(rnd); - - /* - * We know x_candidate is a quadratic residue so set - * it here. - */ - if (crypto_ec_point_solve_y_coord(grp->group, grp->pwe, - x_candidate, - is_odd) != 0) { - wpa_printf(MSG_INFO, - "EAP-pwd: Could not solve for y"); - continue; - } - - /* - * If there's a solution to the equation then the point - * must be on the curve so why check again explicitly? - * OpenSSL code says this is required by X9.62. We're - * not X9.62 but it can't hurt just to be sure. - */ - if (!crypto_ec_point_is_on_curve(grp->group, - grp->pwe)) { - wpa_printf(MSG_INFO, - "EAP-pwd: point is not on curve"); - continue; - } - - if (!crypto_bignum_is_one(cofactor)) { - /* make sure the point is not in a small - * sub-group */ - if (crypto_ec_point_mul(grp->group, grp->pwe, - cofactor, - grp->pwe) != 0) { - wpa_printf(MSG_INFO, - "EAP-pwd: cannot multiply generator by order"); - continue; - } - if (crypto_ec_point_is_at_infinity(grp->group, - grp->pwe)) { - wpa_printf(MSG_INFO, - "EAP-pwd: point is at infinity"); - continue; - } - } - wpa_printf(MSG_DEBUG, - "EAP-pwd: found a PWE in %d tries", ctr); - found = 1; - } + res = crypto_bignum_legendre(tmp2, prime); + if (res == -2) + goto fail; + mask = const_time_eq(res, check); + found_ctr = const_time_select_u8(found, found_ctr, ctr); + found |= mask; } if (found == 0) { wpa_printf(MSG_INFO, @@ -334,6 +311,31 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, num); goto fail; } + + /* + * We know x_candidate is a quadratic residue so set it here. + */ + crypto_bignum_deinit(x_candidate, 1); + x_candidate = crypto_bignum_init_set(x_bin, primebytelen); + if (!x_candidate || + crypto_ec_point_solve_y_coord(grp->group, grp->pwe, x_candidate, + is_odd) != 0) { + wpa_printf(MSG_INFO, "EAP-pwd: Could not solve for y"); + goto fail; + } + + /* + * If there's a solution to the equation then the point must be on the + * curve so why check again explicitly? OpenSSL code says this is + * required by X9.62. We're not X9.62 but it can't hurt just to be sure. + */ + if (!crypto_ec_point_is_on_curve(grp->group, grp->pwe)) { + wpa_printf(MSG_INFO, "EAP-pwd: point is not on curve"); + goto fail; + } + + wpa_printf(MSG_DEBUG, "EAP-pwd: found a PWE in %02d tries", found_ctr); + if (0) { fail: crypto_ec_point_deinit(grp->pwe, 1); @@ -341,16 +343,19 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, ret = 1; } /* cleanliness and order.... */ - crypto_bignum_deinit(cofactor, 1); crypto_bignum_deinit(x_candidate, 1); - crypto_bignum_deinit(rnd, 1); crypto_bignum_deinit(pm1, 0); crypto_bignum_deinit(tmp1, 1); crypto_bignum_deinit(tmp2, 1); crypto_bignum_deinit(qr, 1); crypto_bignum_deinit(qnr, 1); + crypto_bignum_deinit(qr_or_qnr, 1); crypto_bignum_deinit(one, 0); - os_free(prfbuf); + bin_clear_free(prfbuf, primebytelen); + os_memset(qr_bin, 0, sizeof(qr_bin)); + os_memset(qnr_bin, 0, sizeof(qnr_bin)); + os_memset(qr_or_qnr_bin, 0, sizeof(qr_or_qnr_bin)); + os_memset(pwe_digest, 0, sizeof(pwe_digest)); return ret; } @@ -416,3 +421,108 @@ int compute_keys(EAP_PWD_group *grp, const struct crypto_bignum *k, return 1; } + + +static int eap_pwd_element_coord_ok(const struct crypto_bignum *prime, + const u8 *buf, size_t len) +{ + struct crypto_bignum *val; + int ok = 1; + + val = crypto_bignum_init_set(buf, len); + if (!val || crypto_bignum_is_zero(val) || + crypto_bignum_cmp(val, prime) >= 0) + ok = 0; + crypto_bignum_deinit(val, 0); + return ok; +} + + +struct crypto_ec_point * eap_pwd_get_element(EAP_PWD_group *group, + const u8 *buf) +{ + struct crypto_ec_point *element; + const struct crypto_bignum *prime; + size_t prime_len; + + prime = crypto_ec_get_prime(group->group); + prime_len = crypto_ec_prime_len(group->group); + + /* RFC 5931, 2.8.5.2.2: 0 < x,y < p */ + if (!eap_pwd_element_coord_ok(prime, buf, prime_len) || + !eap_pwd_element_coord_ok(prime, buf + prime_len, prime_len)) { + wpa_printf(MSG_INFO, "EAP-pwd: Invalid coordinate in element"); + return NULL; + } + + element = crypto_ec_point_from_bin(group->group, buf); + if (!element) { + wpa_printf(MSG_INFO, "EAP-pwd: EC point from element failed"); + return NULL; + } + + /* RFC 5931, 2.8.5.2.2: on curve and not the point at infinity */ + if (!crypto_ec_point_is_on_curve(group->group, element) || + crypto_ec_point_is_at_infinity(group->group, element)) { + wpa_printf(MSG_INFO, "EAP-pwd: Invalid element"); + goto fail; + } + +out: + return element; +fail: + crypto_ec_point_deinit(element, 0); + element = NULL; + goto out; +} + + +struct crypto_bignum * eap_pwd_get_scalar(EAP_PWD_group *group, const u8 *buf) +{ + struct crypto_bignum *scalar; + const struct crypto_bignum *order; + size_t order_len; + + order = crypto_ec_get_order(group->group); + order_len = crypto_ec_order_len(group->group); + + /* RFC 5931, 2.8.5.2: 1 < scalar < r */ + scalar = crypto_bignum_init_set(buf, order_len); + if (!scalar || crypto_bignum_is_zero(scalar) || + crypto_bignum_is_one(scalar) || + crypto_bignum_cmp(scalar, order) >= 0) { + wpa_printf(MSG_INFO, "EAP-pwd: received scalar is invalid"); + crypto_bignum_deinit(scalar, 0); + scalar = NULL; + } + + return scalar; +} + + +int eap_pwd_get_rand_mask(EAP_PWD_group *group, struct crypto_bignum *_rand, + struct crypto_bignum *_mask, + struct crypto_bignum *scalar) +{ + const struct crypto_bignum *order; + int count; + + order = crypto_ec_get_order(group->group); + + /* Select two random values rand,mask such that 1 < rand,mask < r and + * rand + mask mod r > 1. */ + for (count = 0; count < 100; count++) { + if (crypto_bignum_rand(_rand, order) == 0 && + !crypto_bignum_is_zero(_rand) && + crypto_bignum_rand(_mask, order) == 0 && + !crypto_bignum_is_zero(_mask) && + crypto_bignum_add(_rand, _mask, scalar) == 0 && + crypto_bignum_mod(scalar, order, scalar) == 0 && + !crypto_bignum_is_zero(scalar) && + !crypto_bignum_is_one(scalar)) + return 0; + } + + wpa_printf(MSG_INFO, "EAP-pwd: unable to get randomness"); + return -1; +} diff --git a/src/eap_common/eap_pwd_common.h b/src/eap_common/eap_pwd_common.h index 6b07cf8..c48acee 100644 --- a/src/eap_common/eap_pwd_common.h +++ b/src/eap_common/eap_pwd_common.h @@ -67,5 +67,11 @@ int compute_keys(EAP_PWD_group *grp, const struct crypto_bignum *k, struct crypto_hash * eap_pwd_h_init(void); void eap_pwd_h_update(struct crypto_hash *hash, const u8 *data, size_t len); void eap_pwd_h_final(struct crypto_hash *hash, u8 *digest); +struct crypto_ec_point * eap_pwd_get_element(EAP_PWD_group *group, + const u8 *buf); +struct crypto_bignum * eap_pwd_get_scalar(EAP_PWD_group *group, const u8 *buf); +int eap_pwd_get_rand_mask(EAP_PWD_group *group, struct crypto_bignum *_rand, + struct crypto_bignum *_mask, + struct crypto_bignum *scalar); #endif /* EAP_PWD_COMMON_H */ diff --git a/src/eap_common/eap_sake_common.c b/src/eap_common/eap_sake_common.c index 8819541..8ee9e32 100644 --- a/src/eap_common/eap_sake_common.c +++ b/src/eap_common/eap_sake_common.c @@ -1,6 +1,6 @@ /* * EAP server/peer: EAP-SAKE shared routines - * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -201,14 +201,15 @@ int eap_sake_parse_attributes(const u8 *buf, size_t len, * @data2_len: Length of the data2 * @buf: Buffer for the generated pseudo-random key * @buf_len: Number of bytes of key to generate + * Returns: 0 on success or -1 on failure * * This function is used to derive new, cryptographically separate keys from a * given key (e.g., SMS). This is identical to the PRF used in IEEE 802.11i. */ -static void eap_sake_kdf(const u8 *key, size_t key_len, const char *label, - const u8 *data, size_t data_len, - const u8 *data2, size_t data2_len, - u8 *buf, size_t buf_len) +static int eap_sake_kdf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, + const u8 *data2, size_t data2_len, + u8 *buf, size_t buf_len) { u8 counter = 0; size_t pos, plen; @@ -230,17 +231,21 @@ static void eap_sake_kdf(const u8 *key, size_t key_len, const char *label, while (pos < buf_len) { plen = buf_len - pos; if (plen >= SHA1_MAC_LEN) { - hmac_sha1_vector(key, key_len, 4, addr, len, - &buf[pos]); + if (hmac_sha1_vector(key, key_len, 4, addr, len, + &buf[pos]) < 0) + return -1; pos += SHA1_MAC_LEN; } else { - hmac_sha1_vector(key, key_len, 4, addr, len, - hash); + if (hmac_sha1_vector(key, key_len, 4, addr, len, + hash) < 0) + return -1; os_memcpy(&buf[pos], hash, plen); break; } counter++; } + + return 0; } @@ -253,12 +258,13 @@ static void eap_sake_kdf(const u8 *key, size_t key_len, const char *label, * @tek: Buffer for Temporary EAK Keys (TEK-Auth[16] | TEK-Cipher[16]) * @msk: Buffer for 64-byte MSK * @emsk: Buffer for 64-byte EMSK + * Returns: 0 on success or -1 on failure * * This function derives EAP-SAKE keys as defined in RFC 4763, section 3.2.6. */ -void eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b, - const u8 *rand_s, const u8 *rand_p, u8 *tek, u8 *msk, - u8 *emsk) +int eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b, + const u8 *rand_s, const u8 *rand_p, u8 *tek, u8 *msk, + u8 *emsk) { u8 sms_a[EAP_SAKE_SMS_LEN]; u8 sms_b[EAP_SAKE_SMS_LEN]; @@ -268,14 +274,16 @@ void eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b, wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: Root-Secret-A", root_secret_a, EAP_SAKE_ROOT_SECRET_LEN); - eap_sake_kdf(root_secret_a, EAP_SAKE_ROOT_SECRET_LEN, - "SAKE Master Secret A", - rand_p, EAP_SAKE_RAND_LEN, rand_s, EAP_SAKE_RAND_LEN, - sms_a, EAP_SAKE_SMS_LEN); + if (eap_sake_kdf(root_secret_a, EAP_SAKE_ROOT_SECRET_LEN, + "SAKE Master Secret A", + rand_p, EAP_SAKE_RAND_LEN, rand_s, EAP_SAKE_RAND_LEN, + sms_a, EAP_SAKE_SMS_LEN) < 0) + return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: SMS-A", sms_a, EAP_SAKE_SMS_LEN); - eap_sake_kdf(sms_a, EAP_SAKE_SMS_LEN, "Transient EAP Key", - rand_s, EAP_SAKE_RAND_LEN, rand_p, EAP_SAKE_RAND_LEN, - tek, EAP_SAKE_TEK_LEN); + if (eap_sake_kdf(sms_a, EAP_SAKE_SMS_LEN, "Transient EAP Key", + rand_s, EAP_SAKE_RAND_LEN, rand_p, EAP_SAKE_RAND_LEN, + tek, EAP_SAKE_TEK_LEN) < 0) + return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: TEK-Auth", tek, EAP_SAKE_TEK_AUTH_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: TEK-Cipher", @@ -283,18 +291,21 @@ void eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b, wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: Root-Secret-B", root_secret_b, EAP_SAKE_ROOT_SECRET_LEN); - eap_sake_kdf(root_secret_b, EAP_SAKE_ROOT_SECRET_LEN, - "SAKE Master Secret B", - rand_p, EAP_SAKE_RAND_LEN, rand_s, EAP_SAKE_RAND_LEN, - sms_b, EAP_SAKE_SMS_LEN); + if (eap_sake_kdf(root_secret_b, EAP_SAKE_ROOT_SECRET_LEN, + "SAKE Master Secret B", + rand_p, EAP_SAKE_RAND_LEN, rand_s, EAP_SAKE_RAND_LEN, + sms_b, EAP_SAKE_SMS_LEN) < 0) + return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: SMS-B", sms_b, EAP_SAKE_SMS_LEN); - eap_sake_kdf(sms_b, EAP_SAKE_SMS_LEN, "Master Session Key", - rand_s, EAP_SAKE_RAND_LEN, rand_p, EAP_SAKE_RAND_LEN, - key_buf, sizeof(key_buf)); + if (eap_sake_kdf(sms_b, EAP_SAKE_SMS_LEN, "Master Session Key", + rand_s, EAP_SAKE_RAND_LEN, rand_p, EAP_SAKE_RAND_LEN, + key_buf, sizeof(key_buf)) < 0) + return -1; os_memcpy(msk, key_buf, EAP_MSK_LEN); os_memcpy(emsk, key_buf + EAP_MSK_LEN, EAP_EMSK_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: MSK", msk, EAP_MSK_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: EMSK", emsk, EAP_EMSK_LEN); + return 0; } @@ -312,6 +323,7 @@ void eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b, * @eap_len: EAP packet length * @mic_pos: MIC position in the EAP packet (must be [eap .. eap + eap_len]) * @mic: Buffer for the computed 16-byte MIC + * Returns: 0 on success or -1 on failure */ int eap_sake_compute_mic(const u8 *tek_auth, const u8 *rand_s, const u8 *rand_p, @@ -323,6 +335,7 @@ int eap_sake_compute_mic(const u8 *tek_auth, u8 _rand[2 * EAP_SAKE_RAND_LEN]; u8 *tmp, *pos; size_t tmplen; + int ret; tmplen = serverid_len + 1 + peerid_len + 1 + eap_len; tmp = os_malloc(tmplen); @@ -364,14 +377,14 @@ int eap_sake_compute_mic(const u8 *tek_auth, os_memcpy(pos, eap, eap_len); os_memset(pos + (mic_pos - eap), 0, EAP_SAKE_MIC_LEN); - eap_sake_kdf(tek_auth, EAP_SAKE_TEK_AUTH_LEN, - peer ? "Peer MIC" : "Server MIC", - _rand, 2 * EAP_SAKE_RAND_LEN, tmp, tmplen, - mic, EAP_SAKE_MIC_LEN); + ret = eap_sake_kdf(tek_auth, EAP_SAKE_TEK_AUTH_LEN, + peer ? "Peer MIC" : "Server MIC", + _rand, 2 * EAP_SAKE_RAND_LEN, tmp, tmplen, + mic, EAP_SAKE_MIC_LEN); os_free(tmp); - return 0; + return ret; } diff --git a/src/eap_common/eap_sake_common.h b/src/eap_common/eap_sake_common.h index 9e1e757..a817a35 100644 --- a/src/eap_common/eap_sake_common.h +++ b/src/eap_common/eap_sake_common.h @@ -1,6 +1,6 @@ /* * EAP server/peer: EAP-SAKE shared routines - * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -81,9 +81,9 @@ struct eap_sake_parse_attr { int eap_sake_parse_attributes(const u8 *buf, size_t len, struct eap_sake_parse_attr *attr); -void eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b, - const u8 *rand_s, const u8 *rand_p, - u8 *tek, u8 *msk, u8 *emsk); +int eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b, + const u8 *rand_s, const u8 *rand_p, + u8 *tek, u8 *msk, u8 *emsk); int eap_sake_compute_mic(const u8 *tek_auth, const u8 *rand_s, const u8 *rand_p, const u8 *serverid, size_t serverid_len, diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h index d416afd..3a88f2a 100644 --- a/src/eap_peer/eap_config.h +++ b/src/eap_peer/eap_config.h @@ -101,7 +101,7 @@ struct eap_peer_config { * certificate store (My user account) is used, whereas computer store * (Computer account) is used when running wpasvc as a service. */ - u8 *ca_cert; + char *ca_cert; /** * ca_path - Directory path for CA certificate files (PEM) @@ -112,7 +112,7 @@ struct eap_peer_config { * these certificates are added to the list of trusted CAs. ca_cert * may also be included in that case, but it is not required. */ - u8 *ca_path; + char *ca_path; /** * client_cert - File path to client certificate file (PEM/DER) @@ -126,7 +126,7 @@ struct eap_peer_config { * Alternatively, a named configuration blob can be used by setting * this to blob://blob_name. */ - u8 *client_cert; + char *client_cert; /** * private_key - File path to client private key file (PEM/DER/PFX) @@ -153,7 +153,7 @@ struct eap_peer_config { * Alternatively, a named configuration blob can be used by setting * this to blob://blob_name. */ - u8 *private_key; + char *private_key; /** * private_key_passwd - Password for private key file @@ -178,7 +178,7 @@ struct eap_peer_config { * Alternatively, a named configuration blob can be used by setting * this to blob://blob_name. */ - u8 *dh_file; + char *dh_file; /** * subject_match - Constraint for server certificate subject @@ -194,7 +194,49 @@ struct eap_peer_config { * to do a suffix match against a possible domain name in the CN entry. * For such a use case, domain_suffix_match should be used instead. */ - u8 *subject_match; + char *subject_match; + + /** + * check_cert_subject - Constraint for server certificate subject fields + * + * If check_cert_subject is set, the value of every field will be + * checked against the DN of the subject in the authentication server + * certificate. If the values do not match, the certificate verification + * will fail, rejecting the server. This option allows wpa_supplicant to + * match every individual field in the right order against the DN of the + * subject in the server 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 server + * certificate. If OU=XYZ comes first in terms of the order in the + * server certificate (DN field of server certificate + * C=US/O=XX/OU=XYZ/OU=ABC/CN=1234), wpa_supplicant will reject the + * server 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 servers and DN of the subject in the first server certificate is + * (C=US/O=XX/OU=Production Unit) and DN of the subject in the second + * server is (C=US/O=XX/OU=Production Factory). In this case, + * wpa_supplicant will allow both servers because the value of 'OU' + * field in both server certificates matches 'OU' value in + * 'check_cert_subject' up to 'wildcard'. + * + * (Allow all servers, e.g., check_cert_subject=*) + */ + char *check_cert_subject; + + /** + * check_cert_subject2 - Constraint for server certificate subject fields + * + * This field is like check_cert_subject, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. + */ + char *check_cert_subject2; /** * altsubject_match - Constraint for server certificate alt. subject @@ -212,23 +254,26 @@ struct eap_peer_config { * * Following types are supported: EMAIL, DNS, URI */ - u8 *altsubject_match; + char *altsubject_match; /** * domain_suffix_match - Constraint for server domain name * - * If set, this FQDN is used as a suffix match requirement for the - * server certificate in SubjectAltName dNSName element(s). If a - * matching dNSName is found, this constraint is met. If no dNSName - * values are present, this constraint is matched against SubjectName CN - * using same suffix match comparison. Suffix match here means that the - * host/domain name is compared one label at a time starting from the - * top-level domain and all the labels in domain_suffix_match shall be - * included in the certificate. The certificate may include additional - * sub-level labels in addition to the required labels. + * If set, this semicolon deliminated list of FQDNs is used as suffix + * match requirements for the server certificate in SubjectAltName + * dNSName element(s). If a matching dNSName is found against any of the + * specified values, this constraint is met. If no dNSName values are + * present, this constraint is matched against SubjectName CN using same + * suffix match comparison. Suffix match here means that the host/domain + * name is compared case-insentively one label at a time starting from + * the top-level domain and all the labels in domain_suffix_match shall + * be included in the certificate. The certificate may include + * additional sub-level labels in addition to the required labels. * * For example, domain_suffix_match=example.com would match - * test.example.com but would not match test-example.com. + * test.example.com but would not match test-example.com. Multiple + * match options can be specified in following manner: + * example.org;example.com. */ char *domain_suffix_match; @@ -244,6 +289,12 @@ struct eap_peer_config { * no subdomains or wildcard matches are allowed. Case-insensitive * comparison is used, so "Example.com" matches "example.com", but would * not match "test.Example.com". + * + * More than one match string can be provided by using semicolons to + * separate the strings (e.g., example.org;example.com). When multiple + * strings are specified, a match with any one of the values is + * considered a sufficient match for the certificate, i.e., the + * conditions are ORed together. */ char *domain_match; @@ -263,7 +314,7 @@ struct eap_peer_config { * Alternatively, a named configuration blob can be used by setting * this to blob://blob_name. */ - u8 *ca_cert2; + char *ca_cert2; /** * ca_path2 - Directory path for CA certificate files (PEM) (Phase 2) @@ -277,7 +328,7 @@ struct eap_peer_config { * This field is like ca_path, but used for phase 2 (inside * EAP-TTLS/PEAP/FAST tunnel) authentication. */ - u8 *ca_path2; + char *ca_path2; /** * client_cert2 - File path to client certificate file @@ -290,7 +341,7 @@ struct eap_peer_config { * Alternatively, a named configuration blob can be used by setting * this to blob://blob_name. */ - u8 *client_cert2; + char *client_cert2; /** * private_key2 - File path to client private key file @@ -303,7 +354,7 @@ struct eap_peer_config { * Alternatively, a named configuration blob can be used by setting * this to blob://blob_name. */ - u8 *private_key2; + char *private_key2; /** * private_key2_passwd - Password for private key file @@ -324,7 +375,7 @@ struct eap_peer_config { * Alternatively, a named configuration blob can be used by setting * this to blob://blob_name. */ - u8 *dh_file2; + char *dh_file2; /** * subject_match2 - Constraint for server certificate subject @@ -332,7 +383,7 @@ struct eap_peer_config { * This field is like subject_match, but used for phase 2 (inside * EAP-TTLS/PEAP/FAST tunnel) authentication. */ - u8 *subject_match2; + char *subject_match2; /** * altsubject_match2 - Constraint for server certificate alt. subject @@ -340,7 +391,7 @@ struct eap_peer_config { * This field is like altsubject_match, but used for phase 2 (inside * EAP-TTLS/PEAP/FAST tunnel) authentication. */ - u8 *altsubject_match2; + char *altsubject_match2; /** * domain_suffix_match2 - Constraint for server domain name diff --git a/src/eap_peer/eap_fast.c b/src/eap_peer/eap_fast.c index 74cec7d..94ce57d 100644 --- a/src/eap_peer/eap_fast.c +++ b/src/eap_peer/eap_fast.c @@ -250,8 +250,8 @@ static void eap_fast_deinit(struct eap_sm *sm, void *priv) os_memset(data->key_data, 0, EAP_FAST_KEY_LEN); os_memset(data->emsk, 0, EAP_EMSK_LEN); os_free(data->session_id); - wpabuf_free(data->pending_phase2_req); - wpabuf_free(data->pending_resp); + wpabuf_clear_free(data->pending_phase2_req); + wpabuf_clear_free(data->pending_resp); os_free(data); } @@ -486,7 +486,7 @@ static int eap_fast_phase2_request(struct eap_sm *sm, (config->pending_req_identity || config->pending_req_password || config->pending_req_otp || config->pending_req_new_password || config->pending_req_sim)) { - wpabuf_free(data->pending_phase2_req); + wpabuf_clear_free(data->pending_phase2_req); data->pending_phase2_req = wpabuf_alloc_copy(hdr, len); } else if (*resp == NULL) return -1; @@ -801,7 +801,7 @@ static struct wpabuf * eap_fast_process_crypto_binding( ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; data->phase2_success = 0; - wpabuf_free(resp); + wpabuf_clear_free(resp); return NULL; } @@ -815,7 +815,7 @@ static struct wpabuf * eap_fast_process_crypto_binding( } else { wpa_printf(MSG_ERROR, "EAP-FAST: Failed to derive " "Session-Id"); - wpabuf_free(resp); + wpabuf_clear_free(resp); return NULL; } } @@ -1150,7 +1150,7 @@ static int eap_fast_encrypt_response(struct eap_sm *sm, wpa_printf(MSG_INFO, "EAP-FAST: Failed to encrypt a Phase 2 " "frame"); } - wpabuf_free(resp); + wpabuf_clear_free(resp); return 0; } @@ -1328,14 +1328,14 @@ continue_req: wpa_printf(MSG_INFO, "EAP-FAST: Too short Phase 2 " "TLV frame (len=%lu)", (unsigned long) wpabuf_len(in_decrypted)); - wpabuf_free(in_decrypted); + wpabuf_clear_free(in_decrypted); return -1; } res = eap_fast_process_decrypted(sm, data, ret, identifier, in_decrypted, out_data); - wpabuf_free(in_decrypted); + wpabuf_clear_free(in_decrypted); return res; } @@ -1613,7 +1613,7 @@ static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv, if (sm->waiting_ext_cert_check) { wpa_printf(MSG_DEBUG, "EAP-FAST: Waiting external server certificate validation"); - wpabuf_free(data->pending_resp); + wpabuf_clear_free(data->pending_resp); data->pending_resp = resp; return NULL; } @@ -1641,7 +1641,7 @@ static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv, "EAP-FAST: Could not derive keys"); ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; - wpabuf_free(resp); + wpabuf_clear_free(resp); return NULL; } } @@ -1650,7 +1650,7 @@ static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv, /* * Application data included in the handshake message. */ - wpabuf_free(data->pending_phase2_req); + wpabuf_clear_free(data->pending_phase2_req); data->pending_phase2_req = resp; resp = NULL; res = eap_fast_decrypt(sm, data, ret, id, &msg, &resp); @@ -1658,7 +1658,7 @@ static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv, } if (res == 1) { - wpabuf_free(resp); + wpabuf_clear_free(resp); return eap_peer_tls_build_ack(id, EAP_TYPE_FAST, data->fast_version); } @@ -1684,9 +1684,9 @@ static void eap_fast_deinit_for_reauth(struct eap_sm *sm, void *priv) data->phase2_method->deinit_for_reauth(sm, data->phase2_priv); os_free(data->key_block_p); data->key_block_p = NULL; - wpabuf_free(data->pending_phase2_req); + wpabuf_clear_free(data->pending_phase2_req); data->pending_phase2_req = NULL; - wpabuf_free(data->pending_resp); + wpabuf_clear_free(data->pending_resp); data->pending_resp = NULL; } diff --git a/src/eap_peer/eap_mschapv2.c b/src/eap_peer/eap_mschapv2.c index 877495c..249baec 100644 --- a/src/eap_peer/eap_mschapv2.c +++ b/src/eap_peer/eap_mschapv2.c @@ -856,9 +856,13 @@ static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len) /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key, i.e., * peer MS-MPPE-Send-Key | MS-MPPE-Recv-Key */ - get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 1, 0); - get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN, - MSCHAPV2_KEY_LEN, 0, 0); + if (get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 1, + 0) < 0 || + get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN, + MSCHAPV2_KEY_LEN, 0, 0) < 0) { + os_free(key); + return NULL; + } wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", key, key_len); diff --git a/src/eap_peer/eap_peap.c b/src/eap_peer/eap_peap.c index 34075b1..8dcf7cc 100644 --- a/src/eap_peer/eap_peap.c +++ b/src/eap_peer/eap_peap.c @@ -1,6 +1,6 @@ /* * EAP peer method: EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt) - * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -169,7 +169,7 @@ static void * eap_peap_init(struct eap_sm *sm) static void eap_peap_free_key(struct eap_peap_data *data) { if (data->key_data) { - bin_clear_free(data->key_data, EAP_TLS_KEY_LEN); + bin_clear_free(data->key_data, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); data->key_data = NULL; } } @@ -186,9 +186,9 @@ static void eap_peap_deinit(struct eap_sm *sm, void *priv) eap_peer_tls_ssl_deinit(sm, &data->ssl); eap_peap_free_key(data); os_free(data->session_id); - wpabuf_free(data->pending_phase2_req); - wpabuf_free(data->pending_resp); - os_free(data); + wpabuf_clear_free(data->pending_phase2_req); + wpabuf_clear_free(data->pending_resp); + bin_clear_free(data, sizeof(*data)); } @@ -253,7 +253,7 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) { u8 *tk; u8 isk[32], imck[60]; - int resumed; + int resumed, res; /* * Tunnel key (TK) is the first 60 octets of the key generated by @@ -292,9 +292,11 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) * in the end of the label just before ISK; is that just a typo?) */ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40); - if (peap_prfplus(data->peap_version, tk, 40, - "Inner Methods Compound Keys", - isk, sizeof(isk), imck, sizeof(imck)) < 0) + res = peap_prfplus(data->peap_version, tk, 40, + "Inner Methods Compound Keys", + isk, sizeof(isk), imck, sizeof(imck)); + os_memset(isk, 0, sizeof(isk)); + if (res < 0) return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)", imck, sizeof(imck)); @@ -303,6 +305,7 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40); os_memcpy(data->cmk, imck + 40, 20); wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20); + os_memset(imck, 0, sizeof(imck)); return 0; } @@ -382,7 +385,7 @@ static struct wpabuf * eap_tlv_build_result(struct eap_sm *sm, wpabuf_put_be16(msg, status); /* Status */ if (crypto_tlv_used && eap_tlv_add_cryptobinding(sm, data, msg)) { - wpabuf_free(msg); + wpabuf_clear_free(msg); return NULL; } @@ -651,11 +654,11 @@ static int eap_peap_phase2_request(struct eap_sm *sm, if (*resp == NULL) { ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; - wpabuf_free(buf); + wpabuf_clear_free(buf); return -1; } wpabuf_put_buf(*resp, buf); - wpabuf_free(buf); + wpabuf_clear_free(buf); break; } } @@ -728,7 +731,7 @@ static int eap_peap_phase2_request(struct eap_sm *sm, (config->pending_req_identity || config->pending_req_password || config->pending_req_otp || config->pending_req_new_password || config->pending_req_sim)) { - wpabuf_free(data->pending_phase2_req); + wpabuf_clear_free(data->pending_phase2_req); data->pending_phase2_req = wpabuf_alloc_copy(hdr, len); } @@ -807,7 +810,7 @@ continue_req: struct wpabuf *nmsg = wpabuf_alloc(sizeof(struct eap_hdr) + wpabuf_len(in_decrypted)); if (nmsg == NULL) { - wpabuf_free(in_decrypted); + wpabuf_clear_free(in_decrypted); return 0; } nhdr = wpabuf_put(nmsg, sizeof(*nhdr)); @@ -817,7 +820,7 @@ continue_req: nhdr->length = host_to_be16(sizeof(struct eap_hdr) + wpabuf_len(in_decrypted)); - wpabuf_free(in_decrypted); + wpabuf_clear_free(in_decrypted); in_decrypted = nmsg; } @@ -826,7 +829,7 @@ continue_req: wpa_printf(MSG_INFO, "EAP-PEAP: Too short Phase 2 " "EAP frame (len=%lu)", (unsigned long) wpabuf_len(in_decrypted)); - wpabuf_free(in_decrypted); + wpabuf_clear_free(in_decrypted); return 0; } len = be_to_host16(hdr->length); @@ -835,7 +838,7 @@ continue_req: "Phase 2 EAP frame (len=%lu hdr->length=%lu)", (unsigned long) wpabuf_len(in_decrypted), (unsigned long) len); - wpabuf_free(in_decrypted); + wpabuf_clear_free(in_decrypted); return 0; } if (len < wpabuf_len(in_decrypted)) { @@ -852,7 +855,7 @@ continue_req: case EAP_CODE_REQUEST: if (eap_peap_phase2_request(sm, data, ret, in_decrypted, &resp)) { - wpabuf_free(in_decrypted); + wpabuf_clear_free(in_decrypted); wpa_printf(MSG_INFO, "EAP-PEAP: Phase2 Request " "processing failed"); return 0; @@ -872,7 +875,7 @@ continue_req: "completed successfully"); ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; - wpabuf_free(in_decrypted); + wpabuf_clear_free(in_decrypted); return 0; } wpa_printf(MSG_DEBUG, "EAP-PEAP: Version 1 - " @@ -882,7 +885,7 @@ continue_req: ret->methodState = METHOD_DONE; data->phase2_success = 1; if (data->peap_outer_success == 2) { - wpabuf_free(in_decrypted); + wpabuf_clear_free(in_decrypted); wpa_printf(MSG_DEBUG, "EAP-PEAP: Use TLS ACK " "to finish authentication"); return 1; @@ -928,7 +931,7 @@ continue_req: break; } - wpabuf_free(in_decrypted); + wpabuf_clear_free(in_decrypted); if (resp) { int skip_change2 = 0; @@ -955,7 +958,7 @@ continue_req: wpa_printf(MSG_INFO, "EAP-PEAP: Failed to encrypt " "a Phase 2 frame"); } - wpabuf_free(resp); + wpabuf_clear_free(resp); } return 0; @@ -1056,7 +1059,7 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, if (sm->waiting_ext_cert_check) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Waiting external server certificate validation"); - wpabuf_free(data->pending_resp); + wpabuf_clear_free(data->pending_resp); data->pending_resp = resp; return NULL; } @@ -1081,12 +1084,19 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, "key derivation", label); data->key_data = eap_peer_tls_derive_key(sm, &data->ssl, label, - EAP_TLS_KEY_LEN); + NULL, 0, + EAP_TLS_KEY_LEN + + EAP_EMSK_LEN); if (data->key_data) { wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Derived key", data->key_data, EAP_TLS_KEY_LEN); + wpa_hexdump_key(MSG_DEBUG, + "EAP-PEAP: Derived EMSK", + data->key_data + + EAP_TLS_KEY_LEN, + EAP_EMSK_LEN); } else { wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to " "derive key"); @@ -1131,7 +1141,7 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, /* * Application data included in the handshake message. */ - wpabuf_free(data->pending_phase2_req); + wpabuf_clear_free(data->pending_phase2_req); data->pending_phase2_req = resp; resp = NULL; res = eap_peap_decrypt(sm, data, ret, req, &msg, @@ -1144,7 +1154,7 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, } if (res == 1) { - wpabuf_free(resp); + wpabuf_clear_free(resp); return eap_peer_tls_build_ack(id, EAP_TYPE_PEAP, data->peap_version); } @@ -1168,9 +1178,9 @@ static void eap_peap_deinit_for_reauth(struct eap_sm *sm, void *priv) if (data->phase2_priv && data->phase2_method && data->phase2_method->deinit_for_reauth) data->phase2_method->deinit_for_reauth(sm, data->phase2_priv); - wpabuf_free(data->pending_phase2_req); + wpabuf_clear_free(data->pending_phase2_req); data->pending_phase2_req = NULL; - wpabuf_free(data->pending_resp); + wpabuf_clear_free(data->pending_resp); data->pending_resp = NULL; data->crypto_binding_used = 0; } @@ -1257,6 +1267,7 @@ static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) os_memcpy(key, csk, EAP_TLS_KEY_LEN); wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key", key, EAP_TLS_KEY_LEN); + os_memset(csk, 0, sizeof(csk)); } else os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN); @@ -1264,6 +1275,29 @@ static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_peap_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_peap_data *data = priv; + u8 *key; + + if (!data->key_data || !data->phase2_success) + return NULL; + + if (data->crypto_binding_used) { + /* [MS-PEAP] does not define EMSK derivation */ + return NULL; + } + + key = os_memdup(data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN); + if (!key) + return NULL; + + *len = EAP_EMSK_LEN; + + return key; +} + + static u8 * eap_peap_get_session_id(struct eap_sm *sm, void *priv, size_t *len) { struct eap_peap_data *data = priv; @@ -1296,6 +1330,7 @@ int eap_peer_peap_register(void) eap->process = eap_peap_process; eap->isKeyAvailable = eap_peap_isKeyAvailable; eap->getKey = eap_peap_getKey; + eap->get_emsk = eap_peap_get_emsk; eap->get_status = eap_peap_get_status; eap->has_reauth_data = eap_peap_has_reauth_data; eap->deinit_for_reauth = eap_peap_deinit_for_reauth; diff --git a/src/eap_peer/eap_pwd.c b/src/eap_peer/eap_pwd.c index 761c16a..76fcad4 100644 --- a/src/eap_peer/eap_pwd.c +++ b/src/eap_peer/eap_pwd.c @@ -308,10 +308,10 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, const struct wpabuf *reqData, const u8 *payload, size_t payload_len) { - struct crypto_ec_point *K = NULL, *point = NULL; - struct crypto_bignum *mask = NULL, *cofactor = NULL; + struct crypto_ec_point *K = NULL; + struct crypto_bignum *mask = NULL; const u8 *ptr = payload; - u8 *scalar = NULL, *element = NULL; + u8 *scalar, *element; size_t prime_len, order_len; const u8 *password; size_t password_len; @@ -527,34 +527,17 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, data->private_value = crypto_bignum_init(); data->my_element = crypto_ec_point_init(data->grp->group); - cofactor = crypto_bignum_init(); data->my_scalar = crypto_bignum_init(); mask = crypto_bignum_init(); - if (!data->private_value || !data->my_element || !cofactor || + if (!data->private_value || !data->my_element || !data->my_scalar || !mask) { wpa_printf(MSG_INFO, "EAP-PWD (peer): scalar allocation fail"); goto fin; } - if (crypto_ec_cofactor(data->grp->group, cofactor) < 0) { - wpa_printf(MSG_INFO, "EAP-pwd (peer): unable to get cofactor " - "for curve"); + if (eap_pwd_get_rand_mask(data->grp, data->private_value, mask, + data->my_scalar) < 0) goto fin; - } - - if (crypto_bignum_rand(data->private_value, - crypto_ec_get_order(data->grp->group)) < 0 || - crypto_bignum_rand(mask, - crypto_ec_get_order(data->grp->group)) < 0 || - crypto_bignum_add(data->private_value, mask, - data->my_scalar) < 0 || - crypto_bignum_mod(data->my_scalar, - crypto_ec_get_order(data->grp->group), - data->my_scalar) < 0) { - wpa_printf(MSG_INFO, - "EAP-pwd (peer): unable to get randomness"); - goto fin; - } if (crypto_ec_point_mul(data->grp->group, data->grp->pwe, mask, data->my_element) < 0) { @@ -572,43 +555,27 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, /* process the request */ data->k = crypto_bignum_init(); K = crypto_ec_point_init(data->grp->group); - point = crypto_ec_point_init(data->grp->group); - if (!data->k || !K || !point) { + if (!data->k || !K) { wpa_printf(MSG_INFO, "EAP-PWD (peer): peer data allocation " "fail"); goto fin; } /* element, x then y, followed by scalar */ - data->server_element = crypto_ec_point_from_bin(data->grp->group, ptr); + data->server_element = eap_pwd_get_element(data->grp, ptr); if (!data->server_element) { wpa_printf(MSG_INFO, "EAP-PWD (peer): setting peer element " "fail"); goto fin; } ptr += prime_len * 2; - data->server_scalar = crypto_bignum_init_set(ptr, order_len); + data->server_scalar = eap_pwd_get_scalar(data->grp, ptr); if (!data->server_scalar) { wpa_printf(MSG_INFO, "EAP-PWD (peer): setting peer scalar fail"); goto fin; } - /* check to ensure server's element is not in a small sub-group */ - if (!crypto_bignum_is_one(cofactor)) { - if (crypto_ec_point_mul(data->grp->group, data->server_element, - cofactor, point) < 0) { - wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply " - "server element by order!\n"); - goto fin; - } - if (crypto_ec_point_is_at_infinity(data->grp->group, point)) { - wpa_printf(MSG_INFO, "EAP-PWD (peer): server element " - "is at infinity!\n"); - goto fin; - } - } - /* compute the shared key, k */ if (crypto_ec_point_mul(data->grp->group, data->grp->pwe, data->server_scalar, K) < 0 || @@ -621,17 +588,8 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, goto fin; } - /* ensure that the shared key isn't in a small sub-group */ - if (!crypto_bignum_is_one(cofactor)) { - if (crypto_ec_point_mul(data->grp->group, K, cofactor, K) < 0) { - wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply " - "shared key point by order"); - goto fin; - } - } - /* - * This check is strictly speaking just for the case above where + * This check is strictly speaking just for the case where * co-factor > 1 but it was suggested that even though this is probably * never going to happen it is a simple and safe check "just to be * sure" so let's be safe. @@ -649,12 +607,12 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, } /* now do the response */ - scalar = os_zalloc(order_len); - element = os_zalloc(prime_len * 2); - if (!scalar || !element) { - wpa_printf(MSG_INFO, "EAP-PWD (peer): data allocation fail"); + data->outbuf = wpabuf_alloc(2 * prime_len + order_len); + if (data->outbuf == NULL) goto fin; - } + /* We send the element as (x,y) followed by the scalar */ + element = wpabuf_put(data->outbuf, 2 * prime_len); + scalar = wpabuf_put(data->outbuf, order_len); /* * bignums occupy as little memory as possible so one that is @@ -668,21 +626,9 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, goto fin; } - data->outbuf = wpabuf_alloc(order_len + 2 * prime_len); - if (data->outbuf == NULL) - goto fin; - - /* we send the element as (x,y) follwed by the scalar */ - wpabuf_put_data(data->outbuf, element, 2 * prime_len); - wpabuf_put_data(data->outbuf, scalar, order_len); - fin: - os_free(scalar); - os_free(element); crypto_bignum_deinit(mask, 1); - crypto_bignum_deinit(cofactor, 1); crypto_ec_point_deinit(K, 1); - crypto_ec_point_deinit(point, 1); if (data->outbuf == NULL) eap_pwd_state(data, FAILURE); else @@ -986,6 +932,13 @@ eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, * buffer and ACK the fragment */ if (EAP_PWD_GET_MORE_BIT(lm_exch) || data->in_frag_pos) { + if (!data->inbuf) { + wpa_printf(MSG_DEBUG, + "EAP-pwd: No buffer for reassembly"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return NULL; + } data->in_frag_pos += len; if (data->in_frag_pos > wpabuf_size(data->inbuf)) { wpa_printf(MSG_INFO, "EAP-pwd: Buffer overflow attack " @@ -1012,7 +965,7 @@ eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, /* * we're buffering and this is the last fragment */ - if (data->in_frag_pos) { + if (data->in_frag_pos && data->inbuf) { wpa_printf(MSG_DEBUG, "EAP-pwd: Last fragment, %d bytes", (int) len); pos = wpabuf_head_u8(data->inbuf); diff --git a/src/eap_peer/eap_sake.c b/src/eap_peer/eap_sake.c index 0a6ce25..255241f 100644 --- a/src/eap_peer/eap_sake.c +++ b/src/eap_peer/eap_sake.c @@ -1,6 +1,6 @@ /* * EAP peer method: EAP-SAKE (RFC 4763) - * Copyright (c) 2006-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -235,9 +235,13 @@ static struct wpabuf * eap_sake_process_challenge(struct eap_sm *sm, data->serverid_len = attr.serverid_len; } - eap_sake_derive_keys(data->root_secret_a, data->root_secret_b, - data->rand_s, data->rand_p, - (u8 *) &data->tek, data->msk, data->emsk); + if (eap_sake_derive_keys(data->root_secret_a, data->root_secret_b, + data->rand_s, data->rand_p, + (u8 *) &data->tek, data->msk, + data->emsk) < 0) { + wpa_printf(MSG_INFO, "EAP-SAKE: Failed to derive keys"); + return NULL; + } wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Challenge"); diff --git a/src/eap_peer/eap_tls.c b/src/eap_peer/eap_tls.c index cb74702..ffea9d2 100644 --- a/src/eap_peer/eap_tls.c +++ b/src/eap_peer/eap_tls.c @@ -198,6 +198,7 @@ static void eap_tls_success(struct eap_sm *sm, struct eap_tls_data *data, eap_tls_free_key(data); data->key_data = eap_peer_tls_derive_key(sm, &data->ssl, label, + NULL, 0, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); if (data->key_data) { diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c index 7dbd364..cb94c45 100644 --- a/src/eap_peer/eap_tls_common.c +++ b/src/eap_peer/eap_tls_common.c @@ -108,14 +108,15 @@ static void eap_tls_params_flags(struct tls_connection_params *params, static void eap_tls_params_from_conf1(struct tls_connection_params *params, struct eap_peer_config *config) { - params->ca_cert = (char *) config->ca_cert; - params->ca_path = (char *) config->ca_path; - params->client_cert = (char *) config->client_cert; - params->private_key = (char *) config->private_key; - params->private_key_passwd = (char *) config->private_key_passwd; - params->dh_file = (char *) config->dh_file; - params->subject_match = (char *) config->subject_match; - params->altsubject_match = (char *) config->altsubject_match; + params->ca_cert = config->ca_cert; + params->ca_path = config->ca_path; + params->client_cert = config->client_cert; + params->private_key = config->private_key; + params->private_key_passwd = config->private_key_passwd; + params->dh_file = config->dh_file; + params->subject_match = config->subject_match; + params->altsubject_match = config->altsubject_match; + params->check_cert_subject = config->check_cert_subject; params->suffix_match = config->domain_suffix_match; params->domain_match = config->domain_match; params->engine = config->engine; @@ -131,14 +132,15 @@ static void eap_tls_params_from_conf1(struct tls_connection_params *params, static void eap_tls_params_from_conf2(struct tls_connection_params *params, struct eap_peer_config *config) { - params->ca_cert = (char *) config->ca_cert2; - params->ca_path = (char *) config->ca_path2; - params->client_cert = (char *) config->client_cert2; - params->private_key = (char *) config->private_key2; - params->private_key_passwd = (char *) config->private_key2_passwd; - params->dh_file = (char *) config->dh_file2; - params->subject_match = (char *) config->subject_match2; - params->altsubject_match = (char *) config->altsubject_match2; + params->ca_cert = config->ca_cert2; + params->ca_path = config->ca_path2; + params->client_cert = config->client_cert2; + params->private_key = config->private_key2; + params->private_key_passwd = config->private_key2_passwd; + params->dh_file = config->dh_file2; + params->subject_match = config->subject_match2; + params->altsubject_match = config->altsubject_match2; + params->check_cert_subject = config->check_cert_subject2; params->suffix_match = config->domain_suffix_match2; params->domain_match = config->domain_match2; params->engine = config->engine2; @@ -347,6 +349,8 @@ void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data) * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @data: Data for TLS processing * @label: Label string for deriving the keys, e.g., "client EAP encryption" + * @context: Optional extra upper-layer context (max len 2^16) + * @context_len: The length of the context value * @len: Length of the key material to generate (usually 64 for MSK) * Returns: Pointer to allocated key on success or %NULL on failure * @@ -355,9 +359,12 @@ void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data) * different label to bind the key usage into the generated material. * * The caller is responsible for freeing the returned buffer. + * + * Note: To provide the RFC 5705 context, the context variable must be non-NULL. */ u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, - const char *label, size_t len) + const char *label, const u8 *context, + size_t context_len, size_t len) { u8 *out; @@ -365,8 +372,8 @@ u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, if (out == NULL) return NULL; - if (tls_connection_export_key(data->ssl_ctx, data->conn, label, out, - len)) { + if (tls_connection_export_key(data->ssl_ctx, data->conn, label, + context, context_len, out, len)) { os_free(out); return NULL; } @@ -407,7 +414,7 @@ u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm, if (!id) return NULL; method_id = eap_peer_tls_derive_key( - sm, data, "EXPORTER_EAP_TLS_Method-Id", 64); + sm, data, "EXPORTER_EAP_TLS_Method-Id", NULL, 0, 64); if (!method_id) { os_free(id); return NULL; diff --git a/src/eap_peer/eap_tls_common.h b/src/eap_peer/eap_tls_common.h index 306e6a9..5f82529 100644 --- a/src/eap_peer/eap_tls_common.h +++ b/src/eap_peer/eap_tls_common.h @@ -99,7 +99,8 @@ int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, struct eap_peer_config *config, u8 eap_type); void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data); u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, - const char *label, size_t len); + const char *label, const u8 *context, + size_t context_len, size_t len); u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm, struct eap_ssl_data *data, u8 eap_type, size_t *len); diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c index f18788c..1c8dbe2 100644 --- a/src/eap_peer/eap_ttls.c +++ b/src/eap_peer/eap_ttls.c @@ -196,8 +196,8 @@ static void eap_ttls_deinit(struct eap_sm *sm, void *priv) eap_peer_tls_ssl_deinit(sm, &data->ssl); eap_ttls_free_key(data); os_free(data->session_id); - wpabuf_free(data->pending_phase2_req); - wpabuf_free(data->pending_resp); + wpabuf_clear_free(data->pending_phase2_req); + wpabuf_clear_free(data->pending_resp); os_free(data); } @@ -248,7 +248,7 @@ static int eap_ttls_avp_encapsulate(struct wpabuf **resp, u32 avp_code, msg = wpabuf_alloc(sizeof(struct ttls_avp) + wpabuf_len(*resp) + 4); if (msg == NULL) { - wpabuf_free(*resp); + wpabuf_clear_free(*resp); *resp = NULL; return -1; } @@ -258,7 +258,7 @@ static int eap_ttls_avp_encapsulate(struct wpabuf **resp, u32 avp_code, os_memcpy(pos, wpabuf_head(*resp), wpabuf_len(*resp)); pos += wpabuf_len(*resp); AVP_PAD(avp, pos); - wpabuf_free(*resp); + wpabuf_clear_free(*resp); wpabuf_put(msg, pos - avp); *resp = msg; return 0; @@ -271,6 +271,7 @@ static int eap_ttls_v0_derive_key(struct eap_sm *sm, eap_ttls_free_key(data); data->key_data = eap_peer_tls_derive_key(sm, &data->ssl, "ttls keying material", + NULL, 0, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); if (!data->key_data) { @@ -303,7 +304,8 @@ static int eap_ttls_v0_derive_key(struct eap_sm *sm, static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm, struct eap_ttls_data *data, size_t len) { - return eap_peer_tls_derive_key(sm, &data->ssl, "ttls challenge", len); + return eap_peer_tls_derive_key(sm, &data->ssl, "ttls challenge", + NULL, 0, len); } #endif /* CONFIG_FIPS */ @@ -510,7 +512,7 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, challenge = eap_ttls_implicit_challenge( sm, data, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 1); if (challenge == NULL) { - wpabuf_free(msg); + wpabuf_clear_free(msg); wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive " "implicit challenge"); return -1; @@ -529,7 +531,7 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, *pos++ = 0; /* Flags */ if (os_get_random(pos, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN) < 0) { os_free(challenge); - wpabuf_free(msg); + wpabuf_clear_free(msg); wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to get " "random data for peer challenge"); return -1; @@ -543,7 +545,7 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, peer_challenge, pos, data->auth_response, data->master_key)) { os_free(challenge); - wpabuf_free(msg); + wpabuf_clear_free(msg); wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive " "response"); return -1; @@ -604,7 +606,7 @@ static int eap_ttls_phase2_request_mschap(struct eap_sm *sm, challenge = eap_ttls_implicit_challenge( sm, data, EAP_TTLS_MSCHAP_CHALLENGE_LEN + 1); if (challenge == NULL) { - wpabuf_free(msg); + wpabuf_clear_free(msg); wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAP: Failed to derive " "implicit challenge"); return -1; @@ -628,7 +630,7 @@ static int eap_ttls_phase2_request_mschap(struct eap_sm *sm, if (challenge_response(challenge, password, pos)) { wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAP: Failed derive password hash"); - wpabuf_free(msg); + wpabuf_clear_free(msg); os_free(challenge); return -1; } @@ -641,7 +643,7 @@ static int eap_ttls_phase2_request_mschap(struct eap_sm *sm, pos)) { wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAP: Failed derive password"); - wpabuf_free(msg); + wpabuf_clear_free(msg); os_free(challenge); return -1; } @@ -760,7 +762,7 @@ static int eap_ttls_phase2_request_chap(struct eap_sm *sm, challenge = eap_ttls_implicit_challenge( sm, data, EAP_TTLS_CHAP_CHALLENGE_LEN + 1); if (challenge == NULL) { - wpabuf_free(msg); + wpabuf_clear_free(msg); wpa_printf(MSG_ERROR, "EAP-TTLS/CHAP: Failed to derive " "implicit challenge"); return -1; @@ -1073,10 +1075,10 @@ static int eap_ttls_encrypt_response(struct eap_sm *sm, resp, out_data)) { wpa_printf(MSG_INFO, "EAP-TTLS: Failed to encrypt a Phase 2 " "frame"); - wpabuf_free(resp); + wpabuf_clear_free(resp); return -1; } - wpabuf_free(resp); + wpabuf_clear_free(resp); return 0; } @@ -1297,7 +1299,7 @@ static int eap_ttls_process_decrypted(struct eap_sm *sm, config->pending_req_otp || config->pending_req_new_password || config->pending_req_sim) { - wpabuf_free(data->pending_phase2_req); + wpabuf_clear_free(data->pending_phase2_req); data->pending_phase2_req = wpabuf_dup(in_decrypted); } @@ -1340,7 +1342,7 @@ static int eap_ttls_implicit_identity_request(struct eap_sm *sm, * processing when EAP request is re-processed after * user input. */ - wpabuf_free(data->pending_phase2_req); + wpabuf_clear_free(data->pending_phase2_req); data->pending_phase2_req = wpabuf_alloc(0); } @@ -1413,7 +1415,7 @@ static int eap_ttls_decrypt(struct eap_sm *sm, struct eap_ttls_data *data, in_decrypted = data->pending_phase2_req; data->pending_phase2_req = NULL; if (wpabuf_len(in_decrypted) == 0) { - wpabuf_free(in_decrypted); + wpabuf_clear_free(in_decrypted); return eap_ttls_implicit_identity_request( sm, data, ret, identifier, out_data); } @@ -1449,7 +1451,7 @@ continue_req: &parse, in_decrypted, out_data); done: - wpabuf_free(in_decrypted); + wpabuf_clear_free(in_decrypted); os_free(parse.eapdata); if (retval < 0) { @@ -1509,7 +1511,7 @@ static int eap_ttls_process_handshake(struct eap_sm *sm, if (sm->waiting_ext_cert_check) { wpa_printf(MSG_DEBUG, "EAP-TTLS: Waiting external server certificate validation"); - wpabuf_free(data->pending_resp); + wpabuf_clear_free(data->pending_resp); data->pending_resp = *out_data; *out_data = NULL; return 0; @@ -1543,7 +1545,7 @@ static int eap_ttls_process_handshake(struct eap_sm *sm, /* * Application data included in the handshake message. */ - wpabuf_free(data->pending_phase2_req); + wpabuf_clear_free(data->pending_phase2_req); data->pending_phase2_req = *out_data; *out_data = NULL; res = eap_ttls_decrypt(sm, data, ret, identifier, in_data, @@ -1646,7 +1648,7 @@ static struct wpabuf * eap_ttls_process(struct eap_sm *sm, void *priv, /* FIX: what about res == -1? Could just move all error processing into * the other functions and get rid of this res==1 case here. */ if (res == 1) { - wpabuf_free(resp); + wpabuf_clear_free(resp); return eap_peer_tls_build_ack(id, EAP_TYPE_TTLS, data->ttls_version); } @@ -1669,9 +1671,9 @@ static void eap_ttls_deinit_for_reauth(struct eap_sm *sm, void *priv) if (data->phase2_priv && data->phase2_method && data->phase2_method->deinit_for_reauth) data->phase2_method->deinit_for_reauth(sm, data->phase2_priv); - wpabuf_free(data->pending_phase2_req); + wpabuf_clear_free(data->pending_phase2_req); data->pending_phase2_req = NULL; - wpabuf_free(data->pending_resp); + wpabuf_clear_free(data->pending_resp); data->pending_resp = NULL; data->decision_succ = DECISION_FAIL; #ifdef EAP_TNC diff --git a/src/eap_peer/eap_wsc.c b/src/eap_peer/eap_wsc.c index d140c88..92d5a02 100644 --- a/src/eap_peer/eap_wsc.c +++ b/src/eap_peer/eap_wsc.c @@ -255,6 +255,9 @@ static void * eap_wsc_init(struct eap_sm *sm) cfg.new_ap_settings = &new_ap_settings; } + if (os_strstr(phase1, "multi_ap=1")) + cfg.multi_ap_backhaul_sta = 1; + data->wps = wps_init(&cfg); if (data->wps == NULL) { os_free(data); diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h index 45e1212..b130368 100644 --- a/src/eap_server/eap.h +++ b/src/eap_server/eap.h @@ -161,5 +161,6 @@ void eap_server_mschap_rx_callback(struct eap_sm *sm, const char *source, const u8 *username, size_t username_len, const u8 *challenge, const u8 *response); void eap_erp_update_identity(struct eap_sm *sm, const u8 *eap, size_t len); +void eap_user_free(struct eap_user *user); #endif /* EAP_H */ diff --git a/src/eap_server/eap_server.c b/src/eap_server/eap_server.c index b33f632..e8b36e1 100644 --- a/src/eap_server/eap_server.c +++ b/src/eap_server/eap_server.c @@ -25,9 +25,6 @@ #define EAP_MAX_AUTH_ROUNDS 50 -static void eap_user_free(struct eap_user *user); - - /* EAP state machines are described in RFC 4137 */ static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount, @@ -1814,7 +1811,7 @@ int eap_server_sm_step(struct eap_sm *sm) } -static void eap_user_free(struct eap_user *user) +void eap_user_free(struct eap_user *user) { if (user == NULL) return; diff --git a/src/eap_server/eap_server_gpsk.c b/src/eap_server/eap_server_gpsk.c index fb3d117..bebb17f 100644 --- a/src/eap_server/eap_server_gpsk.c +++ b/src/eap_server/eap_server_gpsk.c @@ -181,7 +181,7 @@ static struct wpabuf * eap_gpsk_build_gpsk_3(struct eap_sm *sm, if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor, data->specifier, start, pos - start, pos) < 0) { - os_free(req); + wpabuf_free(req); eap_gpsk_state(data, FAILURE); return NULL; } @@ -379,7 +379,7 @@ static void eap_gpsk_process_gpsk_2(struct eap_sm *sm, data->specifier = WPA_GET_BE16(csuite->specifier); wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_Sel %d:%d", data->vendor, data->specifier); - pos += sizeof(*csuite); + pos += sizeof(*csuite); if (end - pos < 2) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " diff --git a/src/eap_server/eap_server_mschapv2.c b/src/eap_server/eap_server_mschapv2.c index 6c47bb6..e9e03b0 100644 --- a/src/eap_server/eap_server_mschapv2.c +++ b/src/eap_server/eap_server_mschapv2.c @@ -551,9 +551,13 @@ static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len) if (key == NULL) return NULL; /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key */ - get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0, 1); - get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN, - MSCHAPV2_KEY_LEN, 1, 1); + if (get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0, + 1) < 0 || + get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN, + MSCHAPV2_KEY_LEN, 1, 1) < 0) { + os_free(key); + return NULL; + } wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", key, *len); return key; diff --git a/src/eap_server/eap_server_pax.c b/src/eap_server/eap_server_pax.c index 3257789..2e8c1a6 100644 --- a/src/eap_server/eap_server_pax.c +++ b/src/eap_server/eap_server_pax.c @@ -107,9 +107,14 @@ static struct wpabuf * eap_pax_build_std_1(struct eap_sm *sm, data->rand.r.x, EAP_PAX_RAND_LEN); pos = wpabuf_put(req, EAP_PAX_MAC_LEN); - eap_pax_mac(data->mac_id, (u8 *) "", 0, - wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN, - NULL, 0, NULL, 0, pos); + if (eap_pax_mac(data->mac_id, (u8 *) "", 0, + wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN, + NULL, 0, NULL, 0, pos) < 0) { + wpa_printf(MSG_ERROR, "EAP-PAX: Failed to calculate ICV"); + data->state = FAILURE; + wpabuf_free(req); + return NULL; + } wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN); return req; @@ -144,18 +149,28 @@ static struct wpabuf * eap_pax_build_std_3(struct eap_sm *sm, wpabuf_put_be16(req, EAP_PAX_MAC_LEN); pos = wpabuf_put(req, EAP_PAX_MAC_LEN); - eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN, - data->rand.r.y, EAP_PAX_RAND_LEN, - (u8 *) data->cid, data->cid_len, NULL, 0, pos); + if (eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN, + data->rand.r.y, EAP_PAX_RAND_LEN, + (u8 *) data->cid, data->cid_len, NULL, 0, pos) < 0) { + wpa_printf(MSG_ERROR, "EAP-PAX: Failed to calculate MAC"); + data->state = FAILURE; + wpabuf_free(req); + return NULL; + } wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)", pos, EAP_PAX_MAC_LEN); /* Optional ADE could be added here, if needed */ pos = wpabuf_put(req, EAP_PAX_MAC_LEN); - eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, - wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN, - NULL, 0, NULL, 0, pos); + if (eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, + wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN, + NULL, 0, NULL, 0, pos) < 0) { + wpa_printf(MSG_ERROR, "EAP-PAX: Failed to calculate ICV"); + data->state = FAILURE; + wpabuf_free(req); + return NULL; + } wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN); return req; @@ -190,7 +205,7 @@ static Boolean eap_pax_check(struct eap_sm *sm, void *priv, u8 icvbuf[EAP_PAX_ICV_LEN], *icv; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len); - if (pos == NULL || len < sizeof(*resp)) { + if (pos == NULL || len < sizeof(*resp) + EAP_PAX_ICV_LEN) { wpa_printf(MSG_INFO, "EAP-PAX: Invalid frame"); return TRUE; } @@ -264,11 +279,11 @@ static Boolean eap_pax_check(struct eap_sm *sm, void *priv, } icv = wpabuf_mhead_u8(respData) + mlen - EAP_PAX_ICV_LEN; wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN); - eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, - wpabuf_mhead(respData), - wpabuf_len(respData) - EAP_PAX_ICV_LEN, - NULL, 0, NULL, 0, icvbuf); - if (os_memcmp_const(icvbuf, icv, EAP_PAX_ICV_LEN) != 0) { + if (eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, + wpabuf_mhead(respData), + wpabuf_len(respData) - EAP_PAX_ICV_LEN, + NULL, 0, NULL, 0, icvbuf) < 0 || + os_memcmp_const(icvbuf, icv, EAP_PAX_ICV_LEN) != 0) { wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV"); wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV", icvbuf, EAP_PAX_ICV_LEN); @@ -395,11 +410,11 @@ static void eap_pax_process_std_2(struct eap_sm *sm, } data->keys_set = 1; - eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN, - data->rand.r.x, EAP_PAX_RAND_LEN, - data->rand.r.y, EAP_PAX_RAND_LEN, - (u8 *) data->cid, data->cid_len, mac); - if (os_memcmp_const(mac, pos, EAP_PAX_MAC_LEN) != 0) { + if (eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN, + data->rand.r.x, EAP_PAX_RAND_LEN, + data->rand.r.y, EAP_PAX_RAND_LEN, + (u8 *) data->cid, data->cid_len, mac) < 0 || + os_memcmp_const(mac, pos, EAP_PAX_MAC_LEN) != 0) { wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(A, B, CID) in " "PAX_STD-2"); wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected MAC_CK(A, B, CID)", @@ -417,11 +432,11 @@ static void eap_pax_process_std_2(struct eap_sm *sm, return; } wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN); - eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, - wpabuf_head(respData), - wpabuf_len(respData) - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0, - icvbuf); - if (os_memcmp_const(icvbuf, pos, EAP_PAX_ICV_LEN) != 0) { + if (eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, + wpabuf_head(respData), + wpabuf_len(respData) - EAP_PAX_ICV_LEN, NULL, 0, + NULL, 0, icvbuf) < 0 || + os_memcmp_const(icvbuf, pos, EAP_PAX_ICV_LEN) != 0) { wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV in PAX_STD-2"); wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV", icvbuf, EAP_PAX_ICV_LEN); diff --git a/src/eap_server/eap_server_peap.c b/src/eap_server/eap_server_peap.c index 18d31b5..92c0e5e 100644 --- a/src/eap_server/eap_server_peap.c +++ b/src/eap_server/eap_server_peap.c @@ -1,6 +1,6 @@ /* * hostapd / EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt) - * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -324,13 +324,14 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) { u8 *tk; u8 isk[32], imck[60]; + int res; /* * Tunnel key (TK) is the first 60 octets of the key generated by * phase 1 of PEAP (based on TLS). */ tk = eap_server_tls_derive_key(sm, &data->ssl, "client EAP encryption", - EAP_TLS_KEY_LEN); + NULL, 0, EAP_TLS_KEY_LEN); if (tk == NULL) return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60); @@ -358,9 +359,11 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) * in the end of the label just before ISK; is that just a typo?) */ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40); - if (peap_prfplus(data->peap_version, tk, 40, - "Inner Methods Compound Keys", - isk, sizeof(isk), imck, sizeof(imck)) < 0) { + res = peap_prfplus(data->peap_version, tk, 40, + "Inner Methods Compound Keys", + isk, sizeof(isk), imck, sizeof(imck)); + os_memset(isk, 0, sizeof(isk)); + if (res < 0) { os_free(tk); return -1; } @@ -373,6 +376,7 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40); os_memcpy(data->cmk, imck + 40, 20); wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20); + os_memset(imck, 0, sizeof(imck)); return 0; } @@ -756,7 +760,7 @@ static void eap_peap_process_phase2_tlv(struct eap_sm *sm, } else { eap_peap_state(data, FAILURE); } - + } else if (status == EAP_TLV_RESULT_FAILURE) { wpa_printf(MSG_INFO, "EAP-PEAP: TLV Result - Failure " "- requested %s", requested); @@ -1322,14 +1326,17 @@ static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) "key"); } + os_memset(csk, 0, sizeof(csk)); + return eapKeyData; } /* TODO: PEAPv1 - different label in some cases */ eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, - "client EAP encryption", - EAP_TLS_KEY_LEN); + "client EAP encryption", NULL, 0, + EAP_TLS_KEY_LEN + EAP_EMSK_LEN); if (eapKeyData) { + os_memset(eapKeyData + EAP_TLS_KEY_LEN, 0, EAP_EMSK_LEN); *len = EAP_TLS_KEY_LEN; wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key", eapKeyData, EAP_TLS_KEY_LEN); @@ -1341,6 +1348,40 @@ static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_peap_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_peap_data *data = priv; + u8 *eapKeyData, *emsk; + + if (data->state != SUCCESS) + return NULL; + + if (data->crypto_binding_used) { + /* [MS-PEAP] does not define EMSK derivation */ + return NULL; + } + + /* TODO: PEAPv1 - different label in some cases */ + eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, + "client EAP encryption", NULL, 0, + EAP_TLS_KEY_LEN + EAP_EMSK_LEN); + if (eapKeyData) { + emsk = os_memdup(eapKeyData + EAP_TLS_KEY_LEN, EAP_EMSK_LEN); + bin_clear_free(eapKeyData, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); + if (!emsk) + return NULL; + *len = EAP_EMSK_LEN; + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived EMSK", + emsk, EAP_EMSK_LEN); + } else { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to derive EMSK"); + emsk = NULL; + } + + return emsk; +} + + static Boolean eap_peap_isSuccess(struct eap_sm *sm, void *priv) { struct eap_peap_data *data = priv; @@ -1376,6 +1417,7 @@ int eap_server_peap_register(void) eap->process = eap_peap_process; eap->isDone = eap_peap_isDone; eap->getKey = eap_peap_getKey; + eap->get_emsk = eap_peap_get_emsk; eap->isSuccess = eap_peap_isSuccess; eap->getSessionId = eap_peap_get_session_id; diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c index d0fa54a..e720a28 100644 --- a/src/eap_server/eap_server_pwd.c +++ b/src/eap_server/eap_server_pwd.c @@ -236,7 +236,7 @@ static void eap_pwd_build_commit_req(struct eap_sm *sm, struct eap_pwd_data *data, u8 id) { struct crypto_bignum *mask = NULL; - u8 *scalar = NULL, *element = NULL; + u8 *scalar, *element; size_t prime_len, order_len; wpa_printf(MSG_DEBUG, "EAP-pwd: Commit/Request"); @@ -261,18 +261,9 @@ static void eap_pwd_build_commit_req(struct eap_sm *sm, goto fin; } - if (crypto_bignum_rand(data->private_value, - crypto_ec_get_order(data->grp->group)) < 0 || - crypto_bignum_rand(mask, - crypto_ec_get_order(data->grp->group)) < 0 || - crypto_bignum_add(data->private_value, mask, data->my_scalar) < 0 || - crypto_bignum_mod(data->my_scalar, - crypto_ec_get_order(data->grp->group), - data->my_scalar) < 0) { - wpa_printf(MSG_INFO, - "EAP-pwd (server): unable to get randomness"); + if (eap_pwd_get_rand_mask(data->grp, data->private_value, mask, + data->my_scalar) < 0) goto fin; - } if (crypto_ec_point_mul(data->grp->group, data->grp->pwe, mask, data->my_element) < 0) { @@ -288,22 +279,6 @@ static void eap_pwd_build_commit_req(struct eap_sm *sm, goto fin; } - scalar = os_malloc(order_len); - element = os_malloc(prime_len * 2); - if (!scalar || !element) { - wpa_printf(MSG_INFO, "EAP-PWD (server): data allocation fail"); - goto fin; - } - - if (crypto_ec_point_to_bin(data->grp->group, data->my_element, element, - element + prime_len) < 0) { - wpa_printf(MSG_INFO, "EAP-PWD (server): point assignment " - "fail"); - goto fin; - } - - crypto_bignum_to_bin(data->my_scalar, scalar, order_len, order_len); - data->outbuf = wpabuf_alloc(2 * prime_len + order_len + (data->salt ? 1 + data->salt_len : 0)); if (data->outbuf == NULL) @@ -316,13 +291,18 @@ static void eap_pwd_build_commit_req(struct eap_sm *sm, } /* We send the element as (x,y) followed by the scalar */ - wpabuf_put_data(data->outbuf, element, 2 * prime_len); - wpabuf_put_data(data->outbuf, scalar, order_len); + element = wpabuf_put(data->outbuf, 2 * prime_len); + scalar = wpabuf_put(data->outbuf, order_len); + crypto_bignum_to_bin(data->my_scalar, scalar, order_len, order_len); + if (crypto_ec_point_to_bin(data->grp->group, data->my_element, element, + element + prime_len) < 0) { + wpa_printf(MSG_INFO, "EAP-PWD (server): point assignment " + "fail"); + goto fin; + } fin: crypto_bignum_deinit(mask, 1); - os_free(scalar); - os_free(element); if (data->outbuf == NULL) eap_pwd_state(data, FAILURE); } @@ -331,7 +311,7 @@ fin: static void eap_pwd_build_confirm_req(struct eap_sm *sm, struct eap_pwd_data *data, u8 id) { - struct crypto_hash *hash; + struct crypto_hash *hash = NULL; u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr; u16 grp; size_t prime_len, order_len; @@ -412,6 +392,7 @@ static void eap_pwd_build_confirm_req(struct eap_sm *sm, /* all done with the random function */ eap_pwd_h_final(hash, conf); + hash = NULL; os_memcpy(data->my_confirm, conf, SHA256_MAC_LEN); data->outbuf = wpabuf_alloc(SHA256_MAC_LEN); @@ -424,6 +405,7 @@ fin: bin_clear_free(cruft, prime_len * 2); if (data->outbuf == NULL) eap_pwd_state(data, FAILURE); + eap_pwd_h_final(hash, NULL); } @@ -668,8 +650,7 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data, const u8 *payload, size_t payload_len) { const u8 *ptr; - struct crypto_bignum *cofactor = NULL; - struct crypto_ec_point *K = NULL, *point = NULL; + struct crypto_ec_point *K = NULL; int res = 0; size_t prime_len, order_len; @@ -687,50 +668,36 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data, } data->k = crypto_bignum_init(); - cofactor = crypto_bignum_init(); - point = crypto_ec_point_init(data->grp->group); K = crypto_ec_point_init(data->grp->group); - if (!data->k || !cofactor || !point || !K) { + if (!data->k || !K) { wpa_printf(MSG_INFO, "EAP-PWD (server): peer data allocation " "fail"); goto fin; } - if (crypto_ec_cofactor(data->grp->group, cofactor) < 0) { - wpa_printf(MSG_INFO, "EAP-PWD (server): unable to get " - "cofactor for curve"); - goto fin; - } - /* element, x then y, followed by scalar */ ptr = payload; - data->peer_element = crypto_ec_point_from_bin(data->grp->group, ptr); + data->peer_element = eap_pwd_get_element(data->grp, ptr); if (!data->peer_element) { wpa_printf(MSG_INFO, "EAP-PWD (server): setting peer element " "fail"); goto fin; } ptr += prime_len * 2; - data->peer_scalar = crypto_bignum_init_set(ptr, order_len); + data->peer_scalar = eap_pwd_get_scalar(data->grp, ptr); if (!data->peer_scalar) { wpa_printf(MSG_INFO, "EAP-PWD (server): peer data allocation " "fail"); goto fin; } - /* check to ensure peer's element is not in a small sub-group */ - if (!crypto_bignum_is_one(cofactor)) { - if (crypto_ec_point_mul(data->grp->group, data->peer_element, - cofactor, point) != 0) { - wpa_printf(MSG_INFO, "EAP-PWD (server): cannot " - "multiply peer element by order"); - goto fin; - } - if (crypto_ec_point_is_at_infinity(data->grp->group, point)) { - wpa_printf(MSG_INFO, "EAP-PWD (server): peer element " - "is at infinity!\n"); - goto fin; - } + /* detect reflection attacks */ + if (crypto_bignum_cmp(data->my_scalar, data->peer_scalar) == 0 || + crypto_ec_point_cmp(data->grp->group, data->my_element, + data->peer_element) == 0) { + wpa_printf(MSG_INFO, + "EAP-PWD (server): detected reflection attack!"); + goto fin; } /* compute the shared key, k */ @@ -745,18 +712,8 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data, goto fin; } - /* ensure that the shared key isn't in a small sub-group */ - if (!crypto_bignum_is_one(cofactor)) { - if (crypto_ec_point_mul(data->grp->group, K, cofactor, - K) != 0) { - wpa_printf(MSG_INFO, "EAP-PWD (server): cannot " - "multiply shared key point by order!\n"); - goto fin; - } - } - /* - * This check is strictly speaking just for the case above where + * This check is strictly speaking just for the case where * co-factor > 1 but it was suggested that even though this is probably * never going to happen it is a simple and safe check "just to be * sure" so let's be safe. @@ -775,8 +732,6 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data, fin: crypto_ec_point_deinit(K, 1); - crypto_ec_point_deinit(point, 1); - crypto_bignum_deinit(cofactor, 1); if (res) eap_pwd_state(data, PWD_Confirm_Req); @@ -789,7 +744,7 @@ static void eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data, const u8 *payload, size_t payload_len) { - struct crypto_hash *hash; + struct crypto_hash *hash = NULL; u32 cs; u16 grp; u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr; @@ -864,6 +819,7 @@ eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data, /* all done */ eap_pwd_h_final(hash, conf); + hash = NULL; ptr = (u8 *) payload; if (os_memcmp_const(conf, ptr, SHA256_MAC_LEN)) { @@ -883,6 +839,7 @@ eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data, fin: bin_clear_free(cruft, prime_len * 2); + eap_pwd_h_final(hash, NULL); } @@ -955,6 +912,12 @@ static void eap_pwd_process(struct eap_sm *sm, void *priv, * the first and all intermediate fragments have the M bit set */ if (EAP_PWD_GET_MORE_BIT(lm_exch) || data->in_frag_pos) { + if (!data->inbuf) { + wpa_printf(MSG_DEBUG, + "EAP-pwd: No buffer for reassembly"); + eap_pwd_state(data, FAILURE); + return; + } if ((data->in_frag_pos + len) > wpabuf_size(data->inbuf)) { wpa_printf(MSG_DEBUG, "EAP-pwd: Buffer overflow " "attack detected! (%d+%d > %d)", @@ -975,7 +938,7 @@ static void eap_pwd_process(struct eap_sm *sm, void *priv, * last fragment won't have the M bit set (but we're obviously * buffering fragments so that's how we know it's the last) */ - if (data->in_frag_pos) { + if (data->in_frag_pos && data->inbuf) { pos = wpabuf_head_u8(data->inbuf); len = data->in_frag_pos; wpa_printf(MSG_DEBUG, "EAP-pwd: Last fragment, %d bytes", @@ -1075,14 +1038,6 @@ static u8 * eap_pwd_get_session_id(struct eap_sm *sm, void *priv, size_t *len) int eap_server_pwd_register(void) { struct eap_method *eap; - struct timeval tp; - struct timezone tz; - u32 sr; - - sr = 0xdeaddada; - (void) gettimeofday(&tp, &tz); - sr ^= (tp.tv_sec ^ tp.tv_usec); - srandom(sr); eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_PWD, diff --git a/src/eap_server/eap_server_sake.c b/src/eap_server/eap_server_sake.c index 66183f5..2fc2c05 100644 --- a/src/eap_server/eap_server_sake.c +++ b/src/eap_server/eap_server_sake.c @@ -204,7 +204,7 @@ static struct wpabuf * eap_sake_build_confirm(struct eap_sm *sm, { wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC"); data->state = FAILURE; - os_free(msg); + wpabuf_free(msg); return NULL; } @@ -340,16 +340,25 @@ static void eap_sake_process_challenge(struct eap_sm *sm, data->state = FAILURE; return; } - eap_sake_derive_keys(sm->user->password, - sm->user->password + EAP_SAKE_ROOT_SECRET_LEN, - data->rand_s, data->rand_p, - (u8 *) &data->tek, data->msk, data->emsk); - - eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, - sm->server_id, sm->server_id_len, - data->peerid, data->peerid_len, 1, - wpabuf_head(respData), wpabuf_len(respData), - attr.mic_p, mic_p); + if (eap_sake_derive_keys(sm->user->password, + sm->user->password + EAP_SAKE_ROOT_SECRET_LEN, + data->rand_s, data->rand_p, + (u8 *) &data->tek, data->msk, + data->emsk) < 0) { + wpa_printf(MSG_INFO, "EAP-SAKE: Failed to derive keys"); + data->state = FAILURE; + return; + } + + if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, + sm->server_id, sm->server_id_len, + data->peerid, data->peerid_len, 1, + wpabuf_head(respData), wpabuf_len(respData), + attr.mic_p, mic_p) < 0) { + wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC"); + data->state = FAILURE; + return; + } if (os_memcmp_const(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) { wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P"); eap_sake_state(data, FAILURE); @@ -382,11 +391,14 @@ static void eap_sake_process_confirm(struct eap_sm *sm, return; } - eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, - sm->server_id, sm->server_id_len, - data->peerid, data->peerid_len, 1, - wpabuf_head(respData), wpabuf_len(respData), - attr.mic_p, mic_p); + if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, + sm->server_id, sm->server_id_len, + data->peerid, data->peerid_len, 1, + wpabuf_head(respData), wpabuf_len(respData), + attr.mic_p, mic_p) < 0) { + wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC"); + return; + } if (os_memcmp_const(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) { wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P"); eap_sake_state(data, FAILURE); diff --git a/src/eap_server/eap_server_tls.c b/src/eap_server/eap_server_tls.c index 13d2349..357e72a 100644 --- a/src/eap_server/eap_server_tls.c +++ b/src/eap_server/eap_server_tls.c @@ -331,6 +331,7 @@ static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len) else label = "client EAP encryption"; eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, label, + NULL, 0, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); if (eapKeyData) { *len = EAP_TLS_KEY_LEN; @@ -359,6 +360,7 @@ static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) else label = "client EAP encryption"; eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, label, + NULL, 0, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); if (eapKeyData) { emsk = os_malloc(EAP_EMSK_LEN); diff --git a/src/eap_server/eap_server_tls_common.c b/src/eap_server/eap_server_tls_common.c index 4ba7c24..0eca0ff 100644 --- a/src/eap_server/eap_server_tls_common.c +++ b/src/eap_server/eap_server_tls_common.c @@ -107,7 +107,8 @@ void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data) u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, - const char *label, size_t len) + const char *label, const u8 *context, + size_t context_len, size_t len) { u8 *out; @@ -115,8 +116,8 @@ u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, if (out == NULL) return NULL; - if (tls_connection_export_key(sm->ssl_ctx, data->conn, label, out, - len)) { + if (tls_connection_export_key(sm->ssl_ctx, data->conn, label, + context, context_len, out, len)) { os_free(out); return NULL; } @@ -157,7 +158,7 @@ u8 * eap_server_tls_derive_session_id(struct eap_sm *sm, if (!id) return NULL; method_id = eap_server_tls_derive_key( - sm, data, "EXPORTER_EAP_TLS_Method-Id", 64); + sm, data, "EXPORTER_EAP_TLS_Method-Id", NULL, 0, 64); if (!method_id) { os_free(id); return NULL; diff --git a/src/eap_server/eap_server_ttls.c b/src/eap_server/eap_server_ttls.c index b14996b..52bff8a 100644 --- a/src/eap_server/eap_server_ttls.c +++ b/src/eap_server/eap_server_ttls.c @@ -332,7 +332,7 @@ static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm, struct eap_ttls_data *data, size_t len) { return eap_server_tls_derive_key(sm, &data->ssl, "ttls challenge", - len); + NULL, 0, len); } @@ -1268,7 +1268,7 @@ static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len) return NULL; eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, - "ttls keying material", + "ttls keying material", NULL, 0, EAP_TLS_KEY_LEN); if (eapKeyData) { *len = EAP_TLS_KEY_LEN; @@ -1310,7 +1310,7 @@ static u8 * eap_ttls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) return NULL; eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, - "ttls keying material", + "ttls keying material", NULL, 0, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); if (eapKeyData) { emsk = os_malloc(EAP_EMSK_LEN); diff --git a/src/eap_server/eap_tls_common.h b/src/eap_server/eap_tls_common.h index 31f6e72..0b04983 100644 --- a/src/eap_server/eap_tls_common.h +++ b/src/eap_server/eap_tls_common.h @@ -78,7 +78,8 @@ int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, int verify_peer, int eap_type); void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data); u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, - const char *label, size_t len); + const char *label, const u8 *context, + size_t context_len, size_t len); u8 * eap_server_tls_derive_session_id(struct eap_sm *sm, struct eap_ssl_data *data, u8 eap_type, size_t *len); diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c index 9f029b0..a0f27fd 100644 --- a/src/eapol_supp/eapol_supp_sm.c +++ b/src/eapol_supp/eapol_supp_sm.c @@ -189,8 +189,9 @@ static void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx) } if (sm->authWhile | sm->heldWhile | sm->startWhen | sm->idleWhile) { - eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx, - sm); + if (eloop_register_timeout(1, 0, eapol_port_timers_tick, + eloop_ctx, sm) < 0) + sm->timer_tick_enabled = 0; } else { wpa_printf(MSG_DEBUG, "EAPOL: disable timer tick"); sm->timer_tick_enabled = 0; @@ -204,9 +205,9 @@ static void eapol_enable_timer_tick(struct eapol_sm *sm) if (sm->timer_tick_enabled) return; wpa_printf(MSG_DEBUG, "EAPOL: enable timer tick"); - sm->timer_tick_enabled = 1; eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm); - eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm); + if (eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm) == 0) + sm->timer_tick_enabled = 1; } @@ -2141,8 +2142,8 @@ struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx) sm->initialize = FALSE; eapol_sm_step(sm); - sm->timer_tick_enabled = 1; - eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm); + if (eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm) == 0) + sm->timer_tick_enabled = 1; return sm; } diff --git a/src/fst/fst.h b/src/fst/fst.h index 0c0e435..2967491 100644 --- a/src/fst/fst.h +++ b/src/fst/fst.h @@ -19,10 +19,18 @@ #define US_IN_MS 1000 #define LLT_UNIT_US 32 /* See 10.32.2.2 Transitioning between states */ -#define FST_LLT_MS_TO_VAL(m) (((u32) (m)) * US_IN_MS / LLT_UNIT_US) -#define FST_LLT_VAL_TO_MS(v) (((u32) (v)) * LLT_UNIT_US / US_IN_MS) - -#define FST_MAX_LLT_MS FST_LLT_VAL_TO_MS(-1) +/* + * These were originally + * #define FST_LLT_MS_TO_VAL(m) (((u32) (m)) * US_IN_MS / LLT_UNIT_US) + * #define FST_LLT_VAL_TO_MS(v) (((u32) (v)) * LLT_UNIT_US / US_IN_MS) + * #define FST_MAX_LLT_MS FST_LLT_VAL_TO_MS(-1) + * but those can overflow 32-bit unsigned integer, so use alternative defines + * to avoid undefined behavior with such overflow. + * LLT_UNIT_US/US_IN_MS = 32/1000 = 4/125 + */ +#define FST_LLT_MS_TO_VAL(m) (((u32) (m)) * 125 / 4) +#define FST_LLT_VAL_TO_MS(v) (((u32) (v)) * 4 / 125) +#define FST_MAX_LLT_MS (((u32) -1) / 4) #define FST_MAX_PRIO_VALUE ((u8) -1) #define FST_MAX_GROUP_ID_LEN IFNAMSIZ diff --git a/src/lib.rules b/src/lib.rules index 0c79d99..4ec4711 100644 --- a/src/lib.rules +++ b/src/lib.rules @@ -6,6 +6,11 @@ ifndef CFLAGS CFLAGS = -MMD -O2 -Wall -g endif +ifdef TEST_FUZZ +CFLAGS += -DCONFIG_NO_RANDOM_POOL +CFLAGS += -DTEST_FUZZ +endif + CFLAGS += -I.. -I../utils diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c index b4660c4..157bf89 100644 --- a/src/p2p/p2p.c +++ b/src/p2p/p2p.c @@ -1076,7 +1076,7 @@ static int p2p_run_after_scan(struct p2p_data *p2p) p2p->after_scan_tx->bssid, (u8 *) (p2p->after_scan_tx + 1), p2p->after_scan_tx->len, - p2p->after_scan_tx->wait_time); + p2p->after_scan_tx->wait_time, NULL); os_free(p2p->after_scan_tx); p2p->after_scan_tx = NULL; return 1; @@ -1172,9 +1172,9 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, u8 seek_count, const char **seek, int freq) { int res; + struct os_reltime start; p2p_dbg(p2p, "Starting find (type=%d)", type); - os_get_reltime(&p2p->find_start); if (p2p->p2p_scan_running) { p2p_dbg(p2p, "p2p_scan is already running"); } @@ -1258,6 +1258,7 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, if (timeout) eloop_register_timeout(timeout, 0, p2p_find_timeout, p2p, NULL); + os_get_reltime(&start); switch (type) { case P2P_FIND_START_WITH_FULL: if (freq > 0) { @@ -1289,6 +1290,9 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, return -1; } + if (!res) + p2p->find_start = start; + if (res != 0 && p2p->p2p_scan_running) { p2p_dbg(p2p, "Failed to start p2p_scan - another p2p_scan was already running"); /* wait for the previous p2p_scan to complete */ @@ -1470,7 +1474,8 @@ static void p2p_prepare_channel_best(struct p2p_data *p2p) p2p->op_channel = p2p->cfg->op_channel; } else if (p2p_channel_random_social(&p2p->cfg->channels, &p2p->op_reg_class, - &p2p->op_channel) == 0) { + &p2p->op_channel, + NULL, NULL) == 0) { p2p_dbg(p2p, "Select random available social channel (op_class %u channel %u) as operating channel preference", p2p->op_reg_class, p2p->op_channel); } else { @@ -2456,7 +2461,7 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, if (msg.wps_attributes && !p2p_match_dev_type(p2p, msg.wps_attributes)) { /* No match with Requested Device Type */ - p2p_dbg(p2p, "Probe Req requestred Device Type did not match - ignore it"); + p2p_dbg(p2p, "Probe Req requested Device Type did not match - ignore it"); p2p_parse_free(&msg); return P2P_PREQ_NOT_PROCESSED; } @@ -4764,9 +4769,12 @@ void p2p_set_managed_oper(struct p2p_data *p2p, int enabled) int p2p_config_get_random_social(struct p2p_config *p2p, u8 *op_class, - u8 *op_channel) + u8 *op_channel, + struct wpa_freq_range_list *avoid_list, + struct wpa_freq_range_list *disallow_list) { - return p2p_channel_random_social(&p2p->channels, op_class, op_channel); + return p2p_channel_random_social(&p2p->channels, op_class, op_channel, + avoid_list, disallow_list); } @@ -4965,6 +4973,8 @@ int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *buf, size_t len, unsigned int wait_time) { + int res, scheduled; + if (p2p->p2p_scan_running) { p2p_dbg(p2p, "Delay Action frame TX until p2p_scan completes"); if (p2p->after_scan_tx) { @@ -4985,8 +4995,16 @@ int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst, return 0; } - return p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, dst, src, bssid, - buf, len, wait_time); + res = p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, dst, src, bssid, + buf, len, wait_time, &scheduled); + if (res == 0 && scheduled && p2p->in_listen && freq > 0 && + (unsigned int) p2p->drv_in_listen != freq) { + p2p_dbg(p2p, + "Stop listen on %d MHz to allow a frame to be sent immediately on %d MHz", + p2p->drv_in_listen, freq); + p2p_stop_listen_for_freq(p2p, freq); + } + return res; } diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h index dd2c25d..425b037 100644 --- a/src/p2p/p2p.h +++ b/src/p2p/p2p.h @@ -665,6 +665,8 @@ struct p2p_config { * @buf: Frame body (starting from Category field) * @len: Length of buf in octets * @wait_time: How many msec to wait for a response frame + * @scheduled: Return value indicating whether the transmissions was + * scheduled to happen once the radio is available * Returns: 0 on success, -1 on failure * * The Action frame may not be transmitted immediately and the status @@ -675,7 +677,7 @@ struct p2p_config { */ int (*send_action)(void *ctx, unsigned int freq, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *buf, - size_t len, unsigned int wait_time); + size_t len, unsigned int wait_time, int *scheduled); /** * send_action_done - Notify that Action frame sequence was completed @@ -2010,6 +2012,8 @@ void p2p_set_managed_oper(struct p2p_data *p2p, int enabled); * @p2p: P2P config * @op_class: Selected operating class * @op_channel: Selected social channel + * @avoid_list: Channel ranges to try to avoid or %NULL + * @disallow_list: Channel ranges to discard or %NULL * Returns: 0 on success, -1 on failure * * This function is used before p2p_init is called. A random social channel @@ -2017,7 +2021,9 @@ void p2p_set_managed_oper(struct p2p_data *p2p, int enabled); * returned on success. */ int p2p_config_get_random_social(struct p2p_config *p2p, u8 *op_class, - u8 *op_channel); + u8 *op_channel, + struct wpa_freq_range_list *avoid_list, + struct wpa_freq_range_list *disallow_list); int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel, u8 forced); diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c index 2882c6a..63eb2e8 100644 --- a/src/p2p/p2p_build.c +++ b/src/p2p/p2p_build.c @@ -802,7 +802,7 @@ int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id, wpabuf_put_be16(buf, p2p->cfg->config_methods); } - if (wps_build_wfa_ext(buf, 0, NULL, 0) < 0) + if (wps_build_wfa_ext(buf, 0, NULL, 0, 0) < 0) return -1; if (all_attr && p2p->cfg->num_sec_dev_types) { diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c index 16c28a0..aa18af6 100644 --- a/src/p2p/p2p_group.c +++ b/src/p2p/p2p_group.c @@ -941,7 +941,8 @@ int p2p_group_go_discover(struct p2p_group *group, const u8 *dev_id, if (p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, m->addr, group->cfg->interface_addr, group->cfg->interface_addr, - wpabuf_head(req), wpabuf_len(req), 200) < 0) + wpabuf_head(req), wpabuf_len(req), 200, NULL) + < 0) { p2p_dbg(p2p, "Failed to send Action frame"); } diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h index 6a4d751..d2c55c9 100644 --- a/src/p2p/p2p_i.h +++ b/src/p2p/p2p_i.h @@ -707,7 +707,9 @@ void p2p_channels_dump(struct p2p_data *p2p, const char *title, int p2p_channel_select(struct p2p_channels *chans, const int *classes, u8 *op_class, u8 *op_channel); int p2p_channel_random_social(struct p2p_channels *chans, u8 *op_class, - u8 *op_channel); + u8 *op_channel, + struct wpa_freq_range_list *avoid_list, + struct wpa_freq_range_list *disallow_list); /* p2p_parse.c */ void p2p_copy_filter_devname(char *dst, size_t dst_len, diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c index bbba001..77d662a 100644 --- a/src/p2p/p2p_invitation.c +++ b/src/p2p/p2p_invitation.c @@ -488,7 +488,7 @@ void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa, if (*msg.status == P2P_SC_FAIL_NO_COMMON_CHANNELS && p2p->retry_invite_req && p2p_channel_random_social(&p2p->cfg->channels, &p2p->op_reg_class, - &p2p->op_channel) == 0) { + &p2p->op_channel, NULL, NULL) == 0) { p2p->retry_invite_req = 0; p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p->cfg->stop_listen(p2p->cfg->cb_ctx); diff --git a/src/p2p/p2p_utils.c b/src/p2p/p2p_utils.c index 2e2aa8a..1a62a44 100644 --- a/src/p2p/p2p_utils.c +++ b/src/p2p/p2p_utils.c @@ -413,17 +413,30 @@ int p2p_channel_select(struct p2p_channels *chans, const int *classes, int p2p_channel_random_social(struct p2p_channels *chans, u8 *op_class, - u8 *op_channel) + u8 *op_channel, + struct wpa_freq_range_list *avoid_list, + struct wpa_freq_range_list *disallow_list) { u8 chan[4]; unsigned int num_channels = 0; - /* Try to find available social channels from 2.4 GHz */ - if (p2p_channels_includes(chans, 81, 1)) + /* Try to find available social channels from 2.4 GHz. + * If the avoid_list includes any of the 2.4 GHz social channels, that + * channel is not allowed by p2p_channels_includes() rules. However, it + * is assumed to allow minimal traffic for P2P negotiation, so allow it + * here for social channel selection unless explicitly disallowed in the + * disallow_list. */ + if (p2p_channels_includes(chans, 81, 1) || + (freq_range_list_includes(avoid_list, 2412) && + !freq_range_list_includes(disallow_list, 2412))) chan[num_channels++] = 1; - if (p2p_channels_includes(chans, 81, 6)) + if (p2p_channels_includes(chans, 81, 6) || + (freq_range_list_includes(avoid_list, 2437) && + !freq_range_list_includes(disallow_list, 2437))) chan[num_channels++] = 6; - if (p2p_channels_includes(chans, 81, 11)) + if (p2p_channels_includes(chans, 81, 11) || + (freq_range_list_includes(avoid_list, 2462) && + !freq_range_list_includes(disallow_list, 2462))) chan[num_channels++] = 11; /* Try to find available social channels from 60 GHz */ diff --git a/src/pae/ieee802_1x_cp.c b/src/pae/ieee802_1x_cp.c index 8cdce30..1c4dc3e 100644 --- a/src/pae/ieee802_1x_cp.c +++ b/src/pae/ieee802_1x_cp.c @@ -214,6 +214,10 @@ SM_STATE(CP, RECEIVE) SM_ENTRY(CP, RECEIVE); /* RECEIVE state machine not keep with Figure 12-2 in * IEEE Std 802.1X-2010 */ + if (sm->oki) { + ieee802_1x_kay_delete_sas(sm->kay, sm->oki); + os_free(sm->oki); + } sm->oki = sm->lki; sm->oan = sm->lan; sm->otx = sm->ltx; diff --git a/src/pae/ieee802_1x_kay_i.h b/src/pae/ieee802_1x_kay_i.h index 1d1589c..f9cd3f4 100644 --- a/src/pae/ieee802_1x_kay_i.h +++ b/src/pae/ieee802_1x_kay_i.h @@ -39,7 +39,7 @@ struct ieee802_1x_kay; struct ieee802_1x_mka_peer_id { u8 mi[MI_LEN]; be32 mn; -}; +} STRUCT_PACKED; struct ieee802_1x_kay_peer { struct ieee802_1x_mka_sci sci; diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c index aa78cba..b621ada 100644 --- a/src/radius/radius_server.c +++ b/src/radius/radius_server.c @@ -1,6 +1,6 @@ /* * RADIUS authentication server - * Copyright (c) 2005-2009, 2011-2014, Jouni Malinen <j@w1.fi> + * Copyright (c) 2005-2009, 2011-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -676,6 +676,23 @@ static void radius_server_testing_options(struct radius_session *sess, } +#ifdef CONFIG_ERP +static struct eap_server_erp_key * +radius_server_erp_find_key(struct radius_server_data *data, const char *keyname) +{ + struct eap_server_erp_key *erp; + + dl_list_for_each(erp, &data->erp_keys, struct eap_server_erp_key, + list) { + if (os_strcmp(erp->keyname_nai, keyname) == 0) + return erp; + } + + return NULL; +} +#endif /* CONFIG_ERP */ + + static struct radius_session * radius_server_get_new_session(struct radius_server_data *data, struct radius_client *client, @@ -686,7 +703,7 @@ radius_server_get_new_session(struct radius_server_data *data, int res; struct radius_session *sess; struct eap_config eap_conf; - struct eap_user tmp; + struct eap_user *tmp; RADIUS_DEBUG("Creating a new session"); @@ -697,12 +714,27 @@ radius_server_get_new_session(struct radius_server_data *data, } RADIUS_DUMP_ASCII("User-Name", user, user_len); - os_memset(&tmp, 0, sizeof(tmp)); - res = data->get_eap_user(data->conf_ctx, user, user_len, 0, &tmp); - bin_clear_free(tmp.password, tmp.password_len); + tmp = os_zalloc(sizeof(*tmp)); + if (!tmp) + return NULL; + res = data->get_eap_user(data->conf_ctx, user, user_len, 0, tmp); +#ifdef CONFIG_ERP + if (res != 0 && data->erp) { + char *username; + + username = os_zalloc(user_len + 1); + if (username) { + os_memcpy(username, user, user_len); + if (radius_server_erp_find_key(data, username)) + res = 0; + os_free(username); + } + } +#endif /* CONFIG_ERP */ if (res != 0) { RADIUS_DEBUG("User-Name not found from user database"); + eap_user_free(tmp); return NULL; } @@ -710,10 +742,12 @@ radius_server_get_new_session(struct radius_server_data *data, sess = radius_server_new_session(data, client); if (sess == NULL) { RADIUS_DEBUG("Failed to create a new session"); + eap_user_free(tmp); return NULL; } - sess->accept_attr = tmp.accept_attr; - sess->macacl = tmp.macacl; + sess->accept_attr = tmp->accept_attr; + sess->macacl = tmp->macacl; + eap_user_free(tmp); sess->username = os_malloc(user_len * 4 + 1); if (sess->username == NULL) { @@ -2251,7 +2285,7 @@ radius_server_read_clients(const char *client_file, int ipv6) entry->addr.s_addr = addr.s_addr; val = 0; for (i = 0; i < mask; i++) - val |= 1 << (31 - i); + val |= 1U << (31 - i); entry->mask.s_addr = htonl(val); } #ifdef CONFIG_IPV6 @@ -2702,15 +2736,8 @@ radius_server_erp_get_key(void *ctx, const char *keyname) { struct radius_session *sess = ctx; struct radius_server_data *data = sess->server; - struct eap_server_erp_key *erp; - dl_list_for_each(erp, &data->erp_keys, struct eap_server_erp_key, - list) { - if (os_strcmp(erp->keyname_nai, keyname) == 0) - return erp; - } - - return NULL; + return radius_server_erp_find_key(data, keyname); } diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c index fdd5220..d720f7b 100644 --- a/src/rsn_supp/pmksa_cache.c +++ b/src/rsn_supp/pmksa_cache.c @@ -263,7 +263,8 @@ pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa, } pmksa->pmksa_count++; wpa_printf(MSG_DEBUG, "RSN: Added PMKSA cache entry for " MACSTR - " network_ctx=%p", MAC2STR(entry->aa), entry->network_ctx); + " network_ctx=%p akmp=0x%x", MAC2STR(entry->aa), + entry->network_ctx, entry->akmp); wpa_sm_add_pmkid(pmksa->sm, entry->network_ctx, entry->aa, entry->pmkid, entry->fils_cache_id_set ? entry->fils_cache_id : NULL, entry->pmk, entry->pmk_len); diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c index 345b0c8..704c95e 100644 --- a/src/rsn_supp/tdls.c +++ b/src/rsn_supp/tdls.c @@ -1594,7 +1594,7 @@ static int copy_supp_rates(const struct wpa_eapol_ie_parse *kde, peer->supp_rates, sizeof(peer->supp_rates), kde->supp_rates + 2, kde->supp_rates_len - 2, kde->ext_supp_rates ? kde->ext_supp_rates + 2 : NULL, - kde->ext_supp_rates_len - 2); + kde->ext_supp_rates ? kde->ext_supp_rates_len - 2 : 0); return 0; } diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index 18f8c71..9163f61 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -384,6 +384,11 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, if (!sm->cur_pmksa) sm->cur_pmksa = sa; +#ifdef CONFIG_IEEE80211R + } else if (wpa_key_mgmt_ft(sm->key_mgmt) && sm->ft_protocol) { + wpa_printf(MSG_DEBUG, + "FT: Continue 4-way handshake without PMK/PMKID for association using FT protocol"); +#endif /* CONFIG_IEEE80211R */ } else { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to get master session key from " @@ -534,15 +539,25 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, static int wpa_derive_ptk(struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, struct wpa_ptk *ptk) { + const u8 *z = NULL; + size_t z_len = 0; + #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->key_mgmt)) return wpa_derive_ptk_ft(sm, src_addr, key, ptk); #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_DPP2 + if (sm->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(sm->pmk, sm->pmk_len, "Pairwise key expansion", sm->own_addr, sm->bssid, sm->snonce, key->key_nonce, ptk, sm->key_mgmt, - sm->pairwise_cipher); + sm->pairwise_cipher, z, z_len); } @@ -715,7 +730,9 @@ static void wpa_supplicant_key_neg_complete(struct wpa_sm *sm, * likelihood of the first preauth EAPOL-Start frame getting to * the target AP. */ - eloop_register_timeout(1, 0, wpa_sm_start_preauth, sm, NULL); + if (!dl_list_empty(&sm->pmksa_candidates)) + eloop_register_timeout(1, 0, wpa_sm_start_preauth, + sm, NULL); } if (sm->cur_pmksa && sm->cur_pmksa->opportunistic) { @@ -1009,8 +1026,6 @@ static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm, } os_memset(&gd, 0, sizeof(gd)); - wpa_supplicant_key_neg_complete(sm, sm->bssid, - key_info & WPA_KEY_INFO_SECURE); return 0; } @@ -1048,9 +1063,27 @@ static int wpa_supplicant_install_igtk(struct wpa_sm *sm, broadcast_ether_addr, keyidx, 0, igtk->pn, sizeof(igtk->pn), igtk->igtk, len) < 0) { - wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, - "WPA: Failed to configure IGTK to the driver"); - return -1; + if (keyidx == 0x0400 || keyidx == 0x0500) { + /* Assume the AP has broken PMF implementation since it + * seems to have swapped the KeyID bytes. The AP cannot + * be trusted to implement BIP correctly or provide a + * valid IGTK, so do not try to configure this key with + * swapped KeyID bytes. Instead, continue without + * configuring the IGTK so that the driver can drop any + * received group-addressed robust management frames due + * to missing keys. + * + * Normally, this error behavior would result in us + * disconnecting, but there are number of deployed APs + * with this broken behavior, so as an interoperability + * workaround, allow the connection to proceed. */ + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: Ignore IGTK configuration error due to invalid IGTK KeyID byte order"); + } else { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Failed to configure IGTK to the driver"); + return -1; + } } if (wnm_sleep) { @@ -1491,8 +1524,11 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE); if (sm->group_cipher == WPA_CIPHER_GTK_NOT_USED) { - wpa_supplicant_key_neg_complete(sm, sm->bssid, - key_info & WPA_KEY_INFO_SECURE); + /* No GTK to be set to the driver */ + } else if (!ie.gtk && sm->proto == WPA_PROTO_RSN) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: No GTK KDE included in EAPOL-Key msg 3/4"); + goto failed; } else if (ie.gtk && wpa_supplicant_pairwise_gtk(sm, key, ie.gtk, ie.gtk_len, key_info) < 0) { @@ -1507,6 +1543,10 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, goto failed; } + if (sm->group_cipher == WPA_CIPHER_GTK_NOT_USED || ie.gtk) + wpa_supplicant_key_neg_complete(sm, sm->bssid, + key_info & WPA_KEY_INFO_SECURE); + if (ie.gtk) wpa_sm_set_rekey_offload(sm); @@ -1850,7 +1890,15 @@ static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm, wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Invalid EAPOL-Key MIC " "when using TPTK - ignoring TPTK"); +#ifdef TEST_FUZZ + wpa_printf(MSG_INFO, + "TEST: Ignore Key MIC failure for fuzz testing"); + goto continue_fuzz; +#endif /* TEST_FUZZ */ } else { +#ifdef TEST_FUZZ + continue_fuzz: +#endif /* TEST_FUZZ */ ok = 1; sm->tptk_set = 0; sm->ptk_set = 1; @@ -1876,8 +1924,16 @@ static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm, wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Invalid EAPOL-Key MIC - " "dropping packet"); +#ifdef TEST_FUZZ + wpa_printf(MSG_INFO, + "TEST: Ignore Key MIC failure for fuzz testing"); + goto continue_fuzz2; +#endif /* TEST_FUZZ */ return -1; } +#ifdef TEST_FUZZ + continue_fuzz2: +#endif /* TEST_FUZZ */ ok = 1; } @@ -1952,14 +2008,25 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, "WPA: No memory for AES-UNWRAP buffer"); return -1; } +#ifdef TEST_FUZZ + os_memset(buf, 0x11, *key_data_len); +#endif /* TEST_FUZZ */ if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, *key_data_len / 8, key_data, buf)) { +#ifdef TEST_FUZZ + wpa_printf(MSG_INFO, + "TEST: Ignore AES unwrap failure for fuzz testing"); + goto continue_fuzz; +#endif /* TEST_FUZZ */ bin_clear_free(buf, *key_data_len); wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: AES unwrap failed - " "could not decrypt EAPOL-Key key data"); return -1; } +#ifdef TEST_FUZZ + continue_fuzz: +#endif /* TEST_FUZZ */ os_memcpy(key_data, buf, *key_data_len); bin_clear_free(buf, *key_data_len); WPA_PUT_BE16(((u8 *) (key + 1)) + mic_len, *key_data_len); @@ -2608,6 +2675,9 @@ void wpa_sm_deinit(struct wpa_sm *sm) #ifdef CONFIG_OWE crypto_ecdh_deinit(sm->owe_ecdh); #endif /* CONFIG_OWE */ +#ifdef CONFIG_DPP2 + wpabuf_clear_free(sm->dpp_z); +#endif /* CONFIG_DPP2 */ os_free(sm); } @@ -2649,6 +2719,9 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid) wpa_ft_prepare_auth_request(sm, NULL); clear_keys = 0; + sm->ft_protocol = 1; + } else { + sm->ft_protocol = 0; } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_FILS @@ -2713,6 +2786,7 @@ void wpa_sm_notify_disassoc(struct wpa_sm *sm) #endif /* CONFIG_FILS */ #ifdef CONFIG_IEEE80211R sm->ft_reassoc_completed = 0; + sm->ft_protocol = 0; #endif /* CONFIG_IEEE80211R */ /* Keys are not needed in the WPA state machine anymore */ @@ -3959,11 +4033,13 @@ static int fils_ft_build_assoc_req_rsne(struct wpa_sm *sm, struct wpabuf *buf) MAC2STR(sm->r1kh_id)); pos = wpabuf_put(buf, WPA_PMK_NAME_LEN); if (wpa_derive_pmk_r1_name(sm->pmk_r0_name, sm->r1kh_id, sm->own_addr, - pos, use_sha384) < 0) { + sm->pmk_r1_name, use_sha384) < 0) { wpa_printf(MSG_WARNING, "FILS+FT: Could not derive PMKR1Name"); return -1; } - wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR1Name", pos, WPA_PMK_NAME_LEN); + wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR1Name", sm->pmk_r1_name, + WPA_PMK_NAME_LEN); + os_memcpy(pos, sm->pmk_r1_name, WPA_PMK_NAME_LEN); #ifdef CONFIG_IEEE80211W if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) { @@ -4266,6 +4342,24 @@ int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len) } #endif /* CONFIG_OCV */ +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(sm->key_mgmt) && sm->fils_ft_ies) { + struct wpa_ie_data rsn; + + /* Check that PMKR1Name derived by the AP matches */ + if (!elems.rsn_ie || + wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2, + &rsn) < 0 || + !rsn.pmkid || rsn.num_pmkid != 1 || + os_memcmp(rsn.pmkid, sm->pmk_r1_name, + WPA_PMK_NAME_LEN) != 0) { + wpa_printf(MSG_DEBUG, + "FILS+FT: No RSNE[PMKR1Name] match in AssocResp"); + goto fail; + } + } +#endif /* CONFIG_IEEE80211R */ + /* Key Delivery */ if (!elems.key_delivery) { wpa_printf(MSG_DEBUG, "FILS: No Key Delivery element"); @@ -4587,3 +4681,14 @@ void wpa_sm_set_fils_cache_id(struct wpa_sm *sm, const u8 *fils_cache_id) } #endif /* CONFIG_FILS */ } + + +#ifdef CONFIG_DPP2 +void wpa_sm_set_dpp_z(struct wpa_sm *sm, const struct wpabuf *z) +{ + if (sm) { + wpabuf_clear_free(sm->dpp_z); + sm->dpp_z = z ? wpabuf_dup(z) : NULL; + } +} +#endif /* CONFIG_DPP2 */ diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h index 9eee383..8903f8e 100644 --- a/src/rsn_supp/wpa.h +++ b/src/rsn_supp/wpa.h @@ -465,5 +465,6 @@ int owe_process_assoc_resp(struct wpa_sm *sm, const u8 *bssid, void wpa_sm_set_reset_fils_completed(struct wpa_sm *sm, int set); void wpa_sm_set_fils_cache_id(struct wpa_sm *sm, const u8 *fils_cache_id); +void wpa_sm_set_dpp_z(struct wpa_sm *sm, const struct wpabuf *z); #endif /* WPA_H */ diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h index e1a213b..0c5955c 100644 --- a/src/rsn_supp/wpa_i.h +++ b/src/rsn_supp/wpa_i.h @@ -126,8 +126,9 @@ struct wpa_sm { u8 r0kh_id[FT_R0KH_ID_MAX_LEN]; size_t r0kh_id_len; u8 r1kh_id[FT_R1KH_ID_LEN]; - int ft_completed; - int ft_reassoc_completed; + unsigned int ft_completed:1; + unsigned int ft_reassoc_completed:1; + unsigned int ft_protocol:1; int over_the_ds_in_progress; u8 target_ap[ETH_ALEN]; /* over-the-DS target AP */ int set_ptk_after_assoc; @@ -168,6 +169,10 @@ struct wpa_sm { struct crypto_ecdh *owe_ecdh; u16 owe_group; #endif /* CONFIG_OWE */ + +#ifdef CONFIG_DPP2 + struct wpabuf *dpp_z; +#endif /* CONFIG_DPP2 */ }; diff --git a/src/tls/asn1.c b/src/tls/asn1.c index cec1092..822f87c 100644 --- a/src/tls/asn1.c +++ b/src/tls/asn1.c @@ -31,6 +31,10 @@ int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr) pos = buf; end = buf + len; + if (pos >= end) { + wpa_printf(MSG_DEBUG, "ASN.1: No room for Identifier"); + return -1; + } hdr->identifier = *pos++; hdr->class = hdr->identifier >> 6; hdr->constructed = !!(hdr->identifier & (1 << 5)); @@ -51,6 +55,10 @@ int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr) } else hdr->tag = hdr->identifier & 0x1f; + if (pos >= end) { + wpa_printf(MSG_DEBUG, "ASN.1: No room for Length"); + return -1; + } tmp = *pos++; if (tmp & 0x80) { if (tmp == 0xff) { diff --git a/src/tls/bignum.c b/src/tls/bignum.c index f3baafe..1a87c82 100644 --- a/src/tls/bignum.c +++ b/src/tls/bignum.c @@ -119,10 +119,10 @@ int bignum_cmp(const struct bignum *a, const struct bignum *b) /** - * bignum_cmd_d - Compare bignum to standard integer + * bignum_cmp_d - Compare bignum to standard integer * @a: Bignum from bignum_init() * @b: Small integer - * Returns: 0 on success, -1 on failure + * Returns: -1 if a < b, 0 if a == b, 1 if a > b */ int bignum_cmp_d(const struct bignum *a, unsigned long b) { diff --git a/src/tls/tlsv1_client.c b/src/tls/tlsv1_client.c index 76e1974..a147a54 100644 --- a/src/tls/tlsv1_client.c +++ b/src/tls/tlsv1_client.c @@ -1,6 +1,6 @@ /* * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246) - * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -514,6 +514,8 @@ int tlsv1_client_established(struct tlsv1_client *conn) * tlsv1_client_prf - Use TLS-PRF to derive keying material * @conn: TLSv1 client connection data from tlsv1_client_init() * @label: Label (e.g., description of the key) for PRF + * @context: Optional extra upper-layer context (max len 2^16) + * @context_len: The length of the context value * @server_random_first: seed is 0 = client_random|server_random, * 1 = server_random|client_random * @out: Buffer for output data from TLS-PRF @@ -521,13 +523,26 @@ int tlsv1_client_established(struct tlsv1_client *conn) * Returns: 0 on success, -1 on failure */ int tlsv1_client_prf(struct tlsv1_client *conn, const char *label, + const u8 *context, size_t context_len, int server_random_first, u8 *out, size_t out_len) { - u8 seed[2 * TLS_RANDOM_LEN]; + u8 *seed, *pos; + size_t seed_len = 2 * TLS_RANDOM_LEN; + int res; if (conn->state != ESTABLISHED) return -1; + if (context_len > 65535) + return -1; + + if (context) + seed_len += 2 + context_len; + + seed = os_malloc(seed_len); + if (!seed) + return -1; + if (server_random_first) { os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN); os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, @@ -538,9 +553,18 @@ int tlsv1_client_prf(struct tlsv1_client *conn, const char *label, TLS_RANDOM_LEN); } - return tls_prf(conn->rl.tls_version, - conn->master_secret, TLS_MASTER_SECRET_LEN, - label, seed, 2 * TLS_RANDOM_LEN, out, out_len); + if (context) { + pos = seed + 2 * TLS_RANDOM_LEN; + WPA_PUT_BE16(pos, context_len); + pos += 2; + os_memcpy(pos, context, context_len); + } + + res = tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, + label, seed, seed_len, out, out_len); + os_free(seed); + return res; } diff --git a/src/tls/tlsv1_client.h b/src/tls/tlsv1_client.h index 40fa6c7..7fcc256 100644 --- a/src/tls/tlsv1_client.h +++ b/src/tls/tlsv1_client.h @@ -1,6 +1,6 @@ /* * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246) - * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -19,6 +19,7 @@ struct tlsv1_client * tlsv1_client_init(void); void tlsv1_client_deinit(struct tlsv1_client *conn); int tlsv1_client_established(struct tlsv1_client *conn); int tlsv1_client_prf(struct tlsv1_client *conn, const char *label, + const u8 *context, size_t context_len, int server_random_first, u8 *out, size_t out_len); u8 * tlsv1_client_handshake(struct tlsv1_client *conn, const u8 *in_data, size_t in_len, diff --git a/src/tls/tlsv1_client_read.c b/src/tls/tlsv1_client_read.c index e66f1a9..80874e5 100644 --- a/src/tls/tlsv1_client_read.c +++ b/src/tls/tlsv1_client_read.c @@ -290,7 +290,7 @@ static void tls_peer_cert_event(struct tlsv1_client *conn, int depth, return; os_memset(&ev, 0, sizeof(ev)); - if (conn->cred->cert_probe || conn->cert_in_cb) { + if ((conn->cred && conn->cred->cert_probe) || conn->cert_in_cb) { cert_buf = wpabuf_alloc_copy(cert->cert_start, cert->cert_len); ev.peer_cert.cert = cert_buf; diff --git a/src/tls/tlsv1_client_write.c b/src/tls/tlsv1_client_write.c index 04d895e..4a1147b 100644 --- a/src/tls/tlsv1_client_write.c +++ b/src/tls/tlsv1_client_write.c @@ -72,6 +72,9 @@ u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len) *out_len = 0; os_get_time(&now); +#ifdef TEST_FUZZ + now.sec = 0xfffefdfc; +#endif /* TEST_FUZZ */ WPA_PUT_BE32(conn->client_random, now.sec); if (random_get_bytes(conn->client_random + 4, TLS_RANDOM_LEN - 4)) { wpa_printf(MSG_ERROR, "TLSv1: Could not generate " diff --git a/src/tls/tlsv1_server.c b/src/tls/tlsv1_server.c index 5406969..12dcc85 100644 --- a/src/tls/tlsv1_server.c +++ b/src/tls/tlsv1_server.c @@ -1,6 +1,6 @@ /* * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246) - * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -164,7 +164,8 @@ u8 * tlsv1_server_handshake(struct tlsv1_server *conn, /* need more data */ wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not " "yet supported"); - tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); goto failed; } ct = pos[0]; @@ -204,6 +205,7 @@ failed: msg = tlsv1_server_send_alert(conn, conn->alert_level, conn->alert_description, out_len); + conn->write_alerts++; } return msg; @@ -296,6 +298,7 @@ int tlsv1_server_decrypt(struct tlsv1_server *conn, } tlsv1_server_log(conn, "Received alert %d:%d", out_pos[0], out_pos[1]); + conn->read_alerts++; if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) { /* Continue processing */ pos += used; @@ -459,6 +462,8 @@ int tlsv1_server_established(struct tlsv1_server *conn) * tlsv1_server_prf - Use TLS-PRF to derive keying material * @conn: TLSv1 server connection data from tlsv1_server_init() * @label: Label (e.g., description of the key) for PRF + * @context: Optional extra upper-layer context (max len 2^16) + * @context_len: The length of the context value * @server_random_first: seed is 0 = client_random|server_random, * 1 = server_random|client_random * @out: Buffer for output data from TLS-PRF @@ -466,13 +471,26 @@ int tlsv1_server_established(struct tlsv1_server *conn) * Returns: 0 on success, -1 on failure */ int tlsv1_server_prf(struct tlsv1_server *conn, const char *label, + const u8 *context, size_t context_len, int server_random_first, u8 *out, size_t out_len) { - u8 seed[2 * TLS_RANDOM_LEN]; + u8 *seed, *pos; + size_t seed_len = 2 * TLS_RANDOM_LEN; + int res; if (conn->state != ESTABLISHED) return -1; + if (context_len > 65535) + return -1; + + if (context) + seed_len += 2 + context_len; + + seed = os_malloc(seed_len); + if (!seed) + return -1; + if (server_random_first) { os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN); os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, @@ -483,9 +501,18 @@ int tlsv1_server_prf(struct tlsv1_server *conn, const char *label, TLS_RANDOM_LEN); } - return tls_prf(conn->rl.tls_version, - conn->master_secret, TLS_MASTER_SECRET_LEN, - label, seed, 2 * TLS_RANDOM_LEN, out, out_len); + if (context) { + pos = seed + 2 * TLS_RANDOM_LEN; + WPA_PUT_BE16(pos, context_len); + pos += 2; + os_memcpy(pos, context, context_len); + } + + res = tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, + label, seed, seed_len, out, out_len); + os_free(seed); + return res; } @@ -708,6 +735,24 @@ void tlsv1_server_set_log_cb(struct tlsv1_server *conn, } +int tlsv1_server_get_failed(struct tlsv1_server *conn) +{ + return conn->state == FAILED; +} + + +int tlsv1_server_get_read_alerts(struct tlsv1_server *conn) +{ + return conn->read_alerts; +} + + +int tlsv1_server_get_write_alerts(struct tlsv1_server *conn) +{ + return conn->write_alerts; +} + + #ifdef CONFIG_TESTING_OPTIONS void tlsv1_server_set_test_flags(struct tlsv1_server *conn, u32 flags) { diff --git a/src/tls/tlsv1_server.h b/src/tls/tlsv1_server.h index 10e7699..c9c0875 100644 --- a/src/tls/tlsv1_server.h +++ b/src/tls/tlsv1_server.h @@ -1,6 +1,6 @@ /* * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246) - * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -19,6 +19,7 @@ struct tlsv1_server * tlsv1_server_init(struct tlsv1_credentials *cred); void tlsv1_server_deinit(struct tlsv1_server *conn); int tlsv1_server_established(struct tlsv1_server *conn); int tlsv1_server_prf(struct tlsv1_server *conn, const char *label, + const u8 *context, size_t context_len, int server_random_first, u8 *out, size_t out_len); u8 * tlsv1_server_handshake(struct tlsv1_server *conn, const u8 *in_data, size_t in_len, size_t *out_len); @@ -48,6 +49,10 @@ void tlsv1_server_set_session_ticket_cb(struct tlsv1_server *conn, void tlsv1_server_set_log_cb(struct tlsv1_server *conn, void (*cb)(void *ctx, const char *msg), void *ctx); +int tlsv1_server_get_failed(struct tlsv1_server *conn); +int tlsv1_server_get_read_alerts(struct tlsv1_server *conn); +int tlsv1_server_get_write_alerts(struct tlsv1_server *conn); + void tlsv1_server_set_test_flags(struct tlsv1_server *conn, u32 flags); #endif /* TLSV1_SERVER_H */ diff --git a/src/tls/tlsv1_server_i.h b/src/tls/tlsv1_server_i.h index 29c6678..2622585 100644 --- a/src/tls/tlsv1_server_i.h +++ b/src/tls/tlsv1_server_i.h @@ -30,6 +30,8 @@ struct tlsv1_server { u8 alert_level; u8 alert_description; + int read_alerts, write_alerts; + struct crypto_public_key *client_rsa_key; struct tls_verify_hash verify; diff --git a/src/tls/tlsv1_server_read.c b/src/tls/tlsv1_server_read.c index 4aa8a01..e957678 100644 --- a/src/tls/tlsv1_server_read.c +++ b/src/tls/tlsv1_server_read.c @@ -139,8 +139,11 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, pos = in_data; left = *in_len; - if (left < 4) + if (left < 4) { + tlsv1_server_log(conn, + "Truncated handshake message (expected ClientHello)"); goto decode_error; + } /* HandshakeType msg_type */ if (*pos != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) { @@ -157,8 +160,12 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, pos += 3; left -= 4; - if (len > left) + if (len > left) { + tlsv1_server_log(conn, + "Truncated ClientHello (len=%d left=%d)", + (int) len, (int) left); goto decode_error; + } /* body - ClientHello */ @@ -166,8 +173,10 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, end = pos + len; /* ProtocolVersion client_version */ - if (end - pos < 2) + if (end - pos < 2) { + tlsv1_server_log(conn, "Truncated ClientHello/client_version"); goto decode_error; + } conn->client_version = WPA_GET_BE16(pos); tlsv1_server_log(conn, "Client version %d.%d", conn->client_version >> 8, @@ -196,8 +205,10 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, tls_version_str(conn->rl.tls_version)); /* Random random */ - if (end - pos < TLS_RANDOM_LEN) + if (end - pos < TLS_RANDOM_LEN) { + tlsv1_server_log(conn, "Truncated ClientHello/client_random"); goto decode_error; + } os_memcpy(conn->client_random, pos, TLS_RANDOM_LEN); pos += TLS_RANDOM_LEN; @@ -205,25 +216,36 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, conn->client_random, TLS_RANDOM_LEN); /* SessionID session_id */ - if (end - pos < 1) + if (end - pos < 1) { + tlsv1_server_log(conn, "Truncated ClientHello/session_id len"); goto decode_error; - if (end - pos < 1 + *pos || *pos > TLS_SESSION_ID_MAX_LEN) + } + if (end - pos < 1 + *pos || *pos > TLS_SESSION_ID_MAX_LEN) { + tlsv1_server_log(conn, "Truncated ClientHello/session_id"); goto decode_error; + } wpa_hexdump(MSG_MSGDUMP, "TLSv1: client session_id", pos + 1, *pos); pos += 1 + *pos; /* TODO: add support for session resumption */ /* CipherSuite cipher_suites<2..2^16-1> */ - if (end - pos < 2) + if (end - pos < 2) { + tlsv1_server_log(conn, + "Truncated ClientHello/cipher_suites len"); goto decode_error; + } num_suites = WPA_GET_BE16(pos); pos += 2; - if (end - pos < num_suites) + if (end - pos < num_suites) { + tlsv1_server_log(conn, "Truncated ClientHello/cipher_suites"); goto decode_error; + } wpa_hexdump(MSG_MSGDUMP, "TLSv1: client cipher suites", pos, num_suites); - if (num_suites & 1) + if (num_suites & 1) { + tlsv1_server_log(conn, "Odd len ClientHello/cipher_suites"); goto decode_error; + } num_suites /= 2; cipher_suite = 0; @@ -259,11 +281,17 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, conn->cipher_suite = cipher_suite; /* CompressionMethod compression_methods<1..2^8-1> */ - if (end - pos < 1) + if (end - pos < 1) { + tlsv1_server_log(conn, + "Truncated ClientHello/compression_methods len"); goto decode_error; + } num_suites = *pos++; - if (end - pos < num_suites) + if (end - pos < num_suites) { + tlsv1_server_log(conn, + "Truncated ClientHello/compression_methods"); goto decode_error; + } wpa_hexdump(MSG_MSGDUMP, "TLSv1: client compression_methods", pos, num_suites); compr_null_found = 0; @@ -1217,6 +1245,7 @@ static int tls_process_client_finished(struct tlsv1_server *conn, u8 ct, if (os_memcmp_const(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) { tlsv1_server_log(conn, "Mismatch in verify_data"); + conn->state = FAILED; return -1; } diff --git a/src/tls/tlsv1_server_write.c b/src/tls/tlsv1_server_write.c index bdc6c11..8d36cf1 100644 --- a/src/tls/tlsv1_server_write.c +++ b/src/tls/tlsv1_server_write.c @@ -26,7 +26,7 @@ static size_t tls_server_cert_chain_der_len(struct tlsv1_server *conn) size_t len = 0; struct x509_certificate *cert; - cert = conn->cred->cert; + cert = conn->cred ? conn->cred->cert : NULL; while (cert) { len += 3 + cert->cert_len; if (x509_certificate_self_signed(cert)) @@ -53,6 +53,9 @@ static int tls_write_server_hello(struct tlsv1_server *conn, pos += TLS_RECORD_HEADER_LEN; os_get_time(&now); +#ifdef TEST_FUZZ + now.sec = 0xfffefdfc; +#endif /* TEST_FUZZ */ WPA_PUT_BE32(conn->server_random, now.sec); if (random_get_bytes(conn->server_random + 4, TLS_RANDOM_LEN - 4)) { wpa_printf(MSG_ERROR, "TLSv1: Could not generate " diff --git a/src/tls/x509v3.c b/src/tls/x509v3.c index f80c9a3..fa4d442 100644 --- a/src/tls/x509v3.c +++ b/src/tls/x509v3.c @@ -532,6 +532,8 @@ void x509_name_string(struct x509_name *name, char *buf, size_t len) } done: + if (pos < end) + *pos = '\0'; end[-1] = '\0'; } diff --git a/src/utils/Makefile b/src/utils/Makefile index 52efc53..1ee2bee 100644 --- a/src/utils/Makefile +++ b/src/utils/Makefile @@ -19,6 +19,7 @@ LIB_OBJS= \ common.o \ crc32.o \ ip_addr.o \ + json.o \ radiotap.o \ trace.o \ uuid.o \ diff --git a/src/utils/base64.c b/src/utils/base64.c index 8eb4ba1..53a92f4 100644 --- a/src/utils/base64.c +++ b/src/utils/base64.c @@ -1,12 +1,13 @@ /* * Base64 encoding/decoding (RFC1341) - * Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi> + * Copyright (c) 2005-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" +#include <stdint.h> #include "os.h" #include "base64.h" @@ -27,6 +28,8 @@ static unsigned char * base64_gen_encode(const unsigned char *src, size_t len, size_t olen; int line_len; + if (len >= SIZE_MAX / 4) + return NULL; olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */ if (add_pad) olen += olen / 72; /* line feeds */ diff --git a/src/utils/common.c b/src/utils/common.c index 1eb3370..b9c8bfd 100644 --- a/src/utils/common.c +++ b/src/utils/common.c @@ -1,6 +1,6 @@ /* * wpa_supplicant/hostapd / common helper functions, etc. - * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -1073,7 +1073,8 @@ size_t utf8_unescape(const char *inp, size_t in_size, in_size--; } - while (in_size--) { + while (in_size) { + in_size--; if (res_size >= out_size) return 0; @@ -1084,8 +1085,9 @@ size_t utf8_unescape(const char *inp, size_t in_size, return res_size; case '\\': - if (!in_size--) + if (!in_size) return 0; + in_size--; inp++; /* fall through */ @@ -1116,7 +1118,8 @@ size_t utf8_escape(const char *inp, size_t in_size, if (!in_size) in_size = os_strlen(inp); - while (in_size--) { + while (in_size) { + in_size--; if (res_size++ >= out_size) return 0; @@ -1221,3 +1224,28 @@ u8 rssi_to_rcpi(int rssi) return 220; return (rssi + 110) * 2; } + + +char * get_param(const char *cmd, const char *param) +{ + const char *pos, *end; + char *val; + size_t len; + + pos = os_strstr(cmd, param); + if (!pos) + return NULL; + + pos += os_strlen(param); + end = os_strchr(pos, ' '); + if (end) + len = end - pos; + else + len = os_strlen(pos); + val = os_malloc(len + 1); + if (!val) + return NULL; + os_memcpy(val, pos, len); + val[len] = '\0'; + return val; +} diff --git a/src/utils/common.h b/src/utils/common.h index f824d00..792a30a 100644 --- a/src/utils/common.h +++ b/src/utils/common.h @@ -567,6 +567,7 @@ int is_ctrl_char(char c); int str_starts(const char *str, const char *start); u8 rssi_to_rcpi(int rssi); +char * get_param(const char *cmd, const char *param); /* * gcc 4.4 ends up generating strict-aliasing warnings about some very common diff --git a/src/utils/const_time.h b/src/utils/const_time.h new file mode 100644 index 0000000..ab8f611 --- /dev/null +++ b/src/utils/const_time.h @@ -0,0 +1,191 @@ +/* + * Helper functions for constant time operations + * Copyright (c) 2019, The Linux Foundation + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + * + * These helper functions can be used to implement logic that needs to minimize + * externally visible differences in execution path by avoiding use of branches, + * avoiding early termination or other time differences, and forcing same memory + * access pattern regardless of values. + */ + +#ifndef CONST_TIME_H +#define CONST_TIME_H + + +#if defined(__clang__) +#define NO_UBSAN_UINT_OVERFLOW \ + __attribute__((no_sanitize("unsigned-integer-overflow"))) +#else +#define NO_UBSAN_UINT_OVERFLOW +#endif + + +/** + * const_time_fill_msb - Fill all bits with MSB value + * @val: Input value + * Returns: Value with all the bits set to the MSB of the input val + */ +static inline unsigned int const_time_fill_msb(unsigned int val) +{ + /* Move the MSB to LSB and multiple by -1 to fill in all bits. */ + return (val >> (sizeof(val) * 8 - 1)) * ~0U; +} + + +/* Returns: -1 if val is zero; 0 if val is not zero */ +static inline unsigned int const_time_is_zero(unsigned int val) + NO_UBSAN_UINT_OVERFLOW +{ + /* Set MSB to 1 for 0 and fill rest of bits with the MSB value */ + return const_time_fill_msb(~val & (val - 1)); +} + + +/* Returns: -1 if a == b; 0 if a != b */ +static inline unsigned int const_time_eq(unsigned int a, unsigned int b) +{ + return const_time_is_zero(a ^ b); +} + + +/* Returns: -1 if a == b; 0 if a != b */ +static inline u8 const_time_eq_u8(unsigned int a, unsigned int b) +{ + return (u8) const_time_eq(a, b); +} + + +/** + * const_time_eq_bin - Constant time memory comparison + * @a: First buffer to compare + * @b: Second buffer to compare + * @len: Number of octets to compare + * Returns: -1 if buffers are equal, 0 if not + * + * This function is meant for comparing passwords or hash values where + * difference in execution time or memory access pattern could provide external + * observer information about the location of the difference in the memory + * buffers. The return value does not behave like memcmp(), i.e., + * const_time_eq_bin() cannot be used to sort items into a defined order. Unlike + * memcmp(), the execution time of const_time_eq_bin() does not depend on the + * contents of the compared memory buffers, but only on the total compared + * length. + */ +static inline unsigned int const_time_eq_bin(const void *a, const void *b, + size_t len) +{ + const u8 *aa = a; + const u8 *bb = b; + size_t i; + u8 res = 0; + + for (i = 0; i < len; i++) + res |= aa[i] ^ bb[i]; + + return const_time_is_zero(res); +} + + +/** + * const_time_select - Constant time unsigned int selection + * @mask: 0 (false) or -1 (true) to identify which value to select + * @true_val: Value to select for the true case + * @false_val: Value to select for the false case + * Returns: true_val if mask == -1, false_val if mask == 0 + */ +static inline unsigned int const_time_select(unsigned int mask, + unsigned int true_val, + unsigned int false_val) +{ + return (mask & true_val) | (~mask & false_val); +} + + +/** + * const_time_select_int - Constant time int selection + * @mask: 0 (false) or -1 (true) to identify which value to select + * @true_val: Value to select for the true case + * @false_val: Value to select for the false case + * Returns: true_val if mask == -1, false_val if mask == 0 + */ +static inline int const_time_select_int(unsigned int mask, int true_val, + int false_val) +{ + return (int) const_time_select(mask, (unsigned int) true_val, + (unsigned int) false_val); +} + + +/** + * const_time_select_u8 - Constant time u8 selection + * @mask: 0 (false) or -1 (true) to identify which value to select + * @true_val: Value to select for the true case + * @false_val: Value to select for the false case + * Returns: true_val if mask == -1, false_val if mask == 0 + */ +static inline u8 const_time_select_u8(u8 mask, u8 true_val, u8 false_val) +{ + return (u8) const_time_select(mask, true_val, false_val); +} + + +/** + * const_time_select_s8 - Constant time s8 selection + * @mask: 0 (false) or -1 (true) to identify which value to select + * @true_val: Value to select for the true case + * @false_val: Value to select for the false case + * Returns: true_val if mask == -1, false_val if mask == 0 + */ +static inline s8 const_time_select_s8(u8 mask, s8 true_val, s8 false_val) +{ + return (s8) const_time_select(mask, (unsigned int) true_val, + (unsigned int) false_val); +} + + +/** + * const_time_select_bin - Constant time binary buffer selection copy + * @mask: 0 (false) or -1 (true) to identify which value to copy + * @true_val: Buffer to copy for the true case + * @false_val: Buffer to copy for the false case + * @len: Number of octets to copy + * @dst: Destination buffer for the copy + * + * This function copies the specified buffer into the destination buffer using + * operations with identical memory access pattern regardless of which buffer + * is being copied. + */ +static inline void const_time_select_bin(u8 mask, const u8 *true_val, + const u8 *false_val, size_t len, + u8 *dst) +{ + size_t i; + + for (i = 0; i < len; i++) + dst[i] = const_time_select_u8(mask, true_val[i], false_val[i]); +} + + +static inline int const_time_memcmp(const void *a, const void *b, size_t len) +{ + const u8 *aa = a; + const u8 *bb = b; + int diff, res = 0; + unsigned int mask; + + if (len == 0) + return 0; + do { + len--; + diff = (int) aa[len] - (int) bb[len]; + mask = const_time_is_zero((unsigned int) diff); + res = const_time_select_int(mask, res, diff); + } while (len); + + return res; +} + +#endif /* CONST_TIME_H */ diff --git a/src/utils/json.c b/src/utils/json.c index b9130d3..b644339 100644 --- a/src/utils/json.c +++ b/src/utils/json.c @@ -103,6 +103,11 @@ static char * json_parse_string(const char **json_pos, const char *end) return str; case '\\': pos++; + if (pos >= end) { + wpa_printf(MSG_DEBUG, + "JSON: Truncated \\ escape"); + goto fail; + } switch (*pos) { case '"': case '\\': @@ -165,6 +170,8 @@ static int json_parse_number(const char **json_pos, const char *end, break; } } + if (pos == end) + pos--; if (pos < *json_pos) return -1; len = pos - *json_pos + 1; diff --git a/src/utils/list.h b/src/utils/list.h index ee2f485..85aa5e3 100644 --- a/src/utils/list.h +++ b/src/utils/list.h @@ -1,6 +1,6 @@ /* * Doubly-linked list - * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2009-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -76,8 +76,8 @@ static inline unsigned int dl_list_len(struct dl_list *list) dl_list_entry((list)->prev, type, member)) #define dl_list_for_each(item, list, type, member) \ - for (item = dl_list_entry((list)->next, type, member); \ - &item->member != (list); \ + for (item = dl_list_first((list), type, member); \ + item && item != dl_list_entry((list), type, member); \ item = dl_list_entry(item->member.next, type, member)) #define dl_list_for_each_safe(item, n, list, type, member) \ diff --git a/src/utils/os_internal.c b/src/utils/os_internal.c index ed6eb3c..474c8a3 100644 --- a/src/utils/os_internal.c +++ b/src/utils/os_internal.c @@ -430,22 +430,6 @@ int os_strncmp(const char *s1, const char *s2, size_t n) } -char * os_strncpy(char *dest, const char *src, size_t n) -{ - char *d = dest; - - while (n--) { - *d = *src; - if (*src == '\0') - break; - d++; - src++; - } - - return dest; -} - - size_t os_strlcpy(char *dest, const char *src, size_t siz) { const char *s = src; diff --git a/src/utils/os_none.c b/src/utils/os_none.c index e74f206..5e0a3ad 100644 --- a/src/utils/os_none.c +++ b/src/utils/os_none.c @@ -218,12 +218,6 @@ int os_strncmp(const char *s1, const char *s2, size_t n) } -char * os_strncpy(char *dest, const char *src, size_t n) -{ - return dest; -} - - size_t os_strlcpy(char *dest, const char *src, size_t size) { return 0; diff --git a/src/utils/os_unix.c b/src/utils/os_unix.c index 1894fcd..800c507 100644 --- a/src/utils/os_unix.c +++ b/src/utils/os_unix.c @@ -1,6 +1,6 @@ /* * OS specific functions for UNIX/POSIX systems - * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2005-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -250,6 +250,13 @@ void os_daemonize_terminate(const char *pid_file) int os_get_random(unsigned char *buf, size_t len) { +#ifdef TEST_FUZZ + size_t i; + + for (i = 0; i < len; i++) + buf[i] = i & 0xff; + return 0; +#else /* TEST_FUZZ */ FILE *f; size_t rc; @@ -266,6 +273,7 @@ int os_get_random(unsigned char *buf, size_t len) fclose(f); return rc != len ? -1 : 0; +#endif /* TEST_FUZZ */ } @@ -512,7 +520,7 @@ void * os_memdup(const void *src, size_t len) { void *r = os_malloc(len); - if (r) + if (r && src) os_memcpy(r, src, len); return r; } diff --git a/src/utils/utils_module_tests.c b/src/utils/utils_module_tests.c index 1b8ff82..3af4fcd 100644 --- a/src/utils/utils_module_tests.c +++ b/src/utils/utils_module_tests.c @@ -9,6 +9,7 @@ #include "utils/includes.h" #include "utils/common.h" +#include "utils/const_time.h" #include "common/ieee802_11_defs.h" #include "utils/bitfield.h" #include "utils/ext_password.h" @@ -919,6 +920,294 @@ static int json_tests(void) } +static int const_time_tests(void) +{ + struct const_time_fill_msb_test { + unsigned int val; + unsigned int expected; + } const_time_fill_msb_tests[] = { + { 0, 0 }, + { 1, 0 }, + { 2, 0 }, + { 1 << (sizeof(unsigned int) * 8 - 1), ~0 }, + { ~0 - 1, ~0 }, + { ~0, ~0 } + }; + struct const_time_is_zero_test { + unsigned int val; + unsigned int expected; + } const_time_is_zero_tests[] = { + { 0, ~0 }, + { 1, 0 }, + { 2, 0 }, + { 1 << (sizeof(unsigned int) * 8 - 1), 0 }, + { ~0 - 1, 0 }, + { ~0, 0 } + }; + struct const_time_eq_test { + unsigned int a; + unsigned int b; + unsigned int expected; + unsigned int expected_u8; + } const_time_eq_tests[] = { + { 0, 1, 0, 0 }, + { 1, 2, 0, 0 }, + { 1, 1, ~0, 0xff }, + { ~0, ~0, ~0, 0xff }, + { ~0, ~0 - 1, 0, 0 }, + { 0, 0, ~0, 0xff } + }; + struct const_time_eq_bin_test { + u8 *a; + u8 *b; + size_t len; + unsigned int expected; + } const_time_eq_bin_tests[] = { + { (u8 *) "", (u8 *) "", 0, ~0 }, + { (u8 *) "abcde", (u8 *) "abcde", 5, ~0 }, + { (u8 *) "abcde", (u8 *) "Abcde", 5, 0 }, + { (u8 *) "abcde", (u8 *) "aBcde", 5, 0 }, + { (u8 *) "abcde", (u8 *) "abCde", 5, 0 }, + { (u8 *) "abcde", (u8 *) "abcDe", 5, 0 }, + { (u8 *) "abcde", (u8 *) "abcdE", 5, 0 }, + { (u8 *) "\x00", (u8 *) "\x01", 1, 0 }, + { (u8 *) "\x00", (u8 *) "\x80", 1, 0 }, + { (u8 *) "\x00", (u8 *) "\x00", 1, ~0 } + }; + struct const_time_select_test { + unsigned int mask; + unsigned int true_val; + unsigned int false_val; + unsigned int expected; + } const_time_select_tests[] = { + { ~0, ~0, ~0, ~0 }, + { 0, ~0, ~0, ~0 }, + { ~0, ~0, 0, ~0 }, + { 0, ~0, 0, 0 }, + { ~0, 0xaaaaaaaa, 0x55555555, 0xaaaaaaaa }, + { 0, 0xaaaaaaaa, 0x55555555, 0x55555555 }, + { ~0, 3, 3, 3 }, + { 0, 3, 3, 3 }, + { ~0, 1, 2, 1 }, + { 0, 1, 2, 2 } + }; + struct const_time_select_int_test { + unsigned int mask; + int true_val; + int false_val; + int expected; + } const_time_select_int_tests[] = { + { ~0, -128, 127, -128 }, + { 0, -128, 127, 127 }, + { ~0, -2147483648, 2147483647, -2147483648 }, + { 0, -2147483648, 2147483647, 2147483647 }, + { ~0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { ~0, -1, 1, -1 }, + { 0, -1, 1, 1 } + }; + struct const_time_select_u8_test { + u8 mask; + u8 true_val; + u8 false_val; + u8 expected; + } const_time_select_u8_tests[] = { + { ~0, ~0, ~0, ~0 }, + { 0, ~0, ~0, ~0 }, + { ~0, ~0, 0, ~0 }, + { 0, ~0, 0, 0 }, + { ~0, 0xaa, 0x55, 0xaa }, + { 0, 0xaa, 0x55, 0x55 }, + { ~0, 1, 2, 1 }, + { 0, 1, 2, 2 } + }; + struct const_time_select_s8_test { + u8 mask; + s8 true_val; + s8 false_val; + s8 expected; + } const_time_select_s8_tests[] = { + { ~0, -128, 127, -128 }, + { 0, -128, 127, 127 }, + { ~0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { ~0, -1, 1, -1 }, + { 0, -1, 1, 1 } + }; + struct const_time_select_bin_test { + u8 mask; + u8 *true_val; + u8 *false_val; + size_t len; + u8 *expected; + } const_time_select_bin_tests[] = { + { ~0, (u8 *) "abcde", (u8 *) "ABCDE", 5, (u8 *) "abcde" }, + { 0, (u8 *) "abcde", (u8 *) "ABCDE", 5, (u8 *) "ABCDE" }, + { ~0, (u8 *) "", (u8 *) "", 0, (u8 *) "" }, + { 0, (u8 *) "", (u8 *) "", 0, (u8 *) "" } + }; + struct const_time_memcmp_test { + char *a; + char *b; + size_t len; + int expected; + } const_time_memcmp_tests[] = { + { "abcde", "abcde", 5, 0 }, + { "abcde", "bbcde", 5, -1 }, + { "bbcde", "abcde", 5, 1 }, + { "accde", "abcde", 5, 1 }, + { "abcee", "abcde", 5, 1 }, + { "abcdf", "abcde", 5, 1 }, + { "cbcde", "aXXXX", 5, 2 }, + { "a", "d", 1, -3 }, + { "", "", 0, 0 } + }; + unsigned int i; + int ret = 0; + + wpa_printf(MSG_INFO, "constant time tests"); + + for (i = 0; i < ARRAY_SIZE(const_time_fill_msb_tests); i++) { + struct const_time_fill_msb_test *test; + + test = &const_time_fill_msb_tests[i]; + if (const_time_fill_msb(test->val) != test->expected) { + wpa_printf(MSG_ERROR, + "const_time_fill_msb(0x%x) test failed", + test->val); + ret = -1; + } + } + + for (i = 0; i < ARRAY_SIZE(const_time_is_zero_tests); i++) { + struct const_time_is_zero_test *test; + + test = &const_time_is_zero_tests[i]; + if (const_time_is_zero(test->val) != test->expected) { + wpa_printf(MSG_ERROR, + "const_time_is_zero(0x%x) test failed", + test->val); + ret = -1; + } + } + + for (i = 0; i < ARRAY_SIZE(const_time_eq_tests); i++) { + struct const_time_eq_test *test; + + test = &const_time_eq_tests[i]; + if (const_time_eq(test->a, test->b) != test->expected) { + wpa_printf(MSG_ERROR, + "const_time_eq(0x%x,0x%x) test failed", + test->a, test->b); + ret = -1; + } + if (const_time_eq_u8(test->a, test->b) != test->expected_u8) { + wpa_printf(MSG_ERROR, + "const_time_eq_u8(0x%x,0x%x) test failed", + test->a, test->b); + ret = -1; + } + } + + for (i = 0; i < ARRAY_SIZE(const_time_eq_bin_tests); i++) { + struct const_time_eq_bin_test *test; + + test = &const_time_eq_bin_tests[i]; + if (const_time_eq_bin(test->a, test->b, test->len) != + test->expected) { + wpa_printf(MSG_ERROR, + "const_time_eq_bin(len=%u) test failed", + (unsigned int) test->len); + ret = -1; + } + } + + for (i = 0; i < ARRAY_SIZE(const_time_select_tests); i++) { + struct const_time_select_test *test; + + test = &const_time_select_tests[i]; + if (const_time_select(test->mask, test->true_val, + test->false_val) != test->expected) { + wpa_printf(MSG_ERROR, + "const_time_select(0x%x,0x%x,0x%x) test failed", + test->mask, test->true_val, test->false_val); + ret = -1; + } + } + + for (i = 0; i < ARRAY_SIZE(const_time_select_int_tests); i++) { + struct const_time_select_int_test *test; + + test = &const_time_select_int_tests[i]; + if (const_time_select_int(test->mask, test->true_val, + test->false_val) != test->expected) { + wpa_printf(MSG_ERROR, + "const_time_select_int(0x%x,%d,%d) test failed", + test->mask, test->true_val, test->false_val); + ret = -1; + } + } + + for (i = 0; i < ARRAY_SIZE(const_time_select_u8_tests); i++) { + struct const_time_select_u8_test *test; + + test = &const_time_select_u8_tests[i]; + if (const_time_select_u8(test->mask, test->true_val, + test->false_val) != test->expected) { + wpa_printf(MSG_ERROR, + "const_time_select_u8(0x%x,0x%x,0x%x) test failed", + test->mask, test->true_val, test->false_val); + ret = -1; + } + } + + for (i = 0; i < ARRAY_SIZE(const_time_select_s8_tests); i++) { + struct const_time_select_s8_test *test; + + test = &const_time_select_s8_tests[i]; + if (const_time_select_s8(test->mask, test->true_val, + test->false_val) != test->expected) { + wpa_printf(MSG_ERROR, + "const_time_select_s8(0x%x,0x%x,0x%x) test failed", + test->mask, test->true_val, test->false_val); + ret = -1; + } + } + + for (i = 0; i < ARRAY_SIZE(const_time_select_bin_tests); i++) { + struct const_time_select_bin_test *test; + u8 dst[100]; + + test = &const_time_select_bin_tests[i]; + const_time_select_bin(test->mask, test->true_val, + test->false_val, test->len, dst); + if (os_memcmp(dst, test->expected, test->len) != 0) { + wpa_printf(MSG_ERROR, + "const_time_select_bin(0x%x,%u) test failed", + test->mask, (unsigned int) test->len); + ret = -1; + } + } + + for (i = 0; i < ARRAY_SIZE(const_time_memcmp_tests); i++) { + struct const_time_memcmp_test *test; + int res; + + test = &const_time_memcmp_tests[i]; + res = const_time_memcmp(test->a, test->b, test->len); + if (res != test->expected) { + wpa_printf(MSG_ERROR, + "const_time_memcmp(%s,%s,%d) test failed (%d != %d)", + test->a, test->b, (int) test->len, + res, test->expected); + ret = -1; + } + } + + return ret; +} + + int utils_module_tests(void) { int ret = 0; @@ -936,6 +1225,7 @@ int utils_module_tests(void) ip_addr_tests() < 0 || eloop_tests() < 0 || json_tests() < 0 || + const_time_tests() < 0 || int_array_tests() < 0) ret = -1; diff --git a/src/wps/wps.c b/src/wps/wps.c index 8d22827..484df26 100644 --- a/src/wps/wps.c +++ b/src/wps/wps.c @@ -145,6 +145,8 @@ struct wps_data * wps_init(const struct wps_config *cfg) data->peer_pubkey_hash_set = 1; } + data->multi_ap_backhaul_sta = cfg->multi_ap_backhaul_sta; + return data; } @@ -430,7 +432,7 @@ struct wpabuf * wps_build_assoc_req_ie(enum wps_request_type req_type) if (wps_build_version(ie) || wps_build_req_type(ie, req_type) || - wps_build_wfa_ext(ie, 0, NULL, 0)) { + wps_build_wfa_ext(ie, 0, NULL, 0, 0)) { wpabuf_free(ie); return NULL; } @@ -464,7 +466,7 @@ struct wpabuf * wps_build_assoc_resp_ie(void) if (wps_build_version(ie) || wps_build_resp_type(ie, WPS_RESP_AP) || - wps_build_wfa_ext(ie, 0, NULL, 0)) { + wps_build_wfa_ext(ie, 0, NULL, 0, 0)) { wpabuf_free(ie); return NULL; } @@ -516,7 +518,7 @@ struct wpabuf * wps_build_probe_req_ie(u16 pw_id, struct wps_device_data *dev, wps_build_model_name(dev, ie) || wps_build_model_number(dev, ie) || wps_build_dev_name(dev, ie) || - wps_build_wfa_ext(ie, req_type == WPS_REQ_ENROLLEE, NULL, 0) || + wps_build_wfa_ext(ie, req_type == WPS_REQ_ENROLLEE, NULL, 0, 0) || wps_build_req_dev_type(dev, ie, num_req_dev_types, req_dev_types) || wps_build_secondary_dev_type(dev, ie) diff --git a/src/wps/wps.h b/src/wps/wps.h index 2505d2d..14ce863 100644 --- a/src/wps/wps.h +++ b/src/wps/wps.h @@ -100,6 +100,7 @@ struct wps_device_data { struct wpabuf *vendor_ext[MAX_WPS_VENDOR_EXTENSIONS]; int p2p; + u8 multi_ap_ext; }; /** @@ -187,6 +188,12 @@ struct wps_config { * peer_pubkey_hash - Peer public key hash or %NULL if not known */ const u8 *peer_pubkey_hash; + + /** + * multi_ap_backhaul_sta - Whether this is a Multi-AP backhaul STA + * enrollee + */ + int multi_ap_backhaul_sta; }; struct wps_data * wps_init(const struct wps_config *cfg); @@ -395,6 +402,37 @@ struct wps_registrar_config { * PSK is set for a network. */ int force_per_enrollee_psk; + + /** + * multi_ap_backhaul_ssid - SSID to supply to a Multi-AP backhaul + * enrollee + * + * This SSID is used by the Registrar to fill in information for + * Credentials when the enrollee advertises it is a Multi-AP backhaul + * STA. + */ + const u8 *multi_ap_backhaul_ssid; + + /** + * multi_ap_backhaul_ssid_len - Length of multi_ap_backhaul_ssid in + * octets + */ + size_t multi_ap_backhaul_ssid_len; + + /** + * multi_ap_backhaul_network_key - The Network Key (PSK) for the + * Multi-AP backhaul enrollee. + * + * This key can be either the ASCII passphrase (8..63 characters) or the + * 32-octet PSK (64 hex characters). + */ + const u8 *multi_ap_backhaul_network_key; + + /** + * multi_ap_backhaul_network_key_len - Length of + * multi_ap_backhaul_network_key in octets + */ + size_t multi_ap_backhaul_network_key_len; }; diff --git a/src/wps/wps_attr_build.c b/src/wps/wps_attr_build.c index 7dfa95b..4e872f3 100644 --- a/src/wps/wps_attr_build.c +++ b/src/wps/wps_attr_build.c @@ -204,7 +204,8 @@ int wps_build_version(struct wpabuf *msg) int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll, - const u8 *auth_macs, size_t auth_macs_count) + const u8 *auth_macs, size_t auth_macs_count, + u8 multi_ap_subelem) { u8 *len; @@ -245,6 +246,14 @@ int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll, MAC2STR(&auth_macs[i * ETH_ALEN])); } + if (multi_ap_subelem) { + wpa_printf(MSG_DEBUG, "WPS: * Multi-AP (0x%x)", + multi_ap_subelem); + wpabuf_put_u8(msg, WFA_ELEM_MULTI_AP); + wpabuf_put_u8(msg, 1); /* length */ + wpabuf_put_u8(msg, multi_ap_subelem); + } + WPA_PUT_BE16(len, (u8 *) wpabuf_put(msg, 0) - len - 2); #ifdef CONFIG_WPS_TESTING diff --git a/src/wps/wps_attr_parse.c b/src/wps/wps_attr_parse.c index 756d57e..fd51635 100644 --- a/src/wps/wps_attr_parse.c +++ b/src/wps/wps_attr_parse.c @@ -67,6 +67,17 @@ static int wps_set_vendor_ext_wfa_subelem(struct wps_parse_attr *attr, } attr->registrar_configuration_methods = pos; break; + case WFA_ELEM_MULTI_AP: + if (len != 1) { + wpa_printf(MSG_DEBUG, + "WPS: Invalid Multi-AP Extension length %u", + len); + return -1; + } + attr->multi_ap_ext = *pos; + wpa_printf(MSG_DEBUG, "WPS: Multi-AP Extension 0x%02x", + attr->multi_ap_ext); + break; default: wpa_printf(MSG_MSGDUMP, "WPS: Skipped unknown WFA Vendor " "Extension subelement %u", id); diff --git a/src/wps/wps_attr_parse.h b/src/wps/wps_attr_parse.h index 8188fe9..4de27b2 100644 --- a/src/wps/wps_attr_parse.h +++ b/src/wps/wps_attr_parse.h @@ -97,6 +97,7 @@ struct wps_parse_attr { const u8 *cred[MAX_CRED_COUNT]; const u8 *req_dev_type[MAX_REQ_DEV_TYPE_COUNT]; const u8 *vendor_ext[MAX_WPS_PARSE_VENDOR_EXT]; + u8 multi_ap_ext; }; int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr); diff --git a/src/wps/wps_common.c b/src/wps/wps_common.c index bcae1ba..747dc47 100644 --- a/src/wps/wps_common.c +++ b/src/wps/wps_common.c @@ -374,7 +374,7 @@ struct wpabuf * wps_get_oob_cred(struct wps_context *wps, int rf_band, (rf_band && wps_build_rf_bands_attr(plain, rf_band)) || (channel && wps_build_ap_channel(plain, channel)) || wps_build_mac_addr(plain, wps->dev.mac_addr) || - wps_build_wfa_ext(plain, 0, NULL, 0)) { + wps_build_wfa_ext(plain, 0, NULL, 0, 0)) { os_free(data.new_psk); wpabuf_clear_free(plain); return NULL; @@ -421,7 +421,7 @@ struct wpabuf * wps_build_nfc_pw_token(u16 dev_pw_id, if (wps_build_oob_dev_pw(data, dev_pw_id, pubkey, wpabuf_head(dev_pw), wpabuf_len(dev_pw)) || - wps_build_wfa_ext(data, 0, NULL, 0)) { + wps_build_wfa_ext(data, 0, NULL, 0, 0)) { wpa_printf(MSG_ERROR, "WPS: Failed to build NFC password " "token"); wpabuf_clear_free(data); @@ -586,7 +586,7 @@ struct wpabuf * wps_build_wsc_ack(struct wps_data *wps) wps_build_msg_type(msg, WPS_WSC_ACK) || wps_build_enrollee_nonce(wps, msg) || wps_build_registrar_nonce(wps, msg) || - wps_build_wfa_ext(msg, 0, NULL, 0)) { + wps_build_wfa_ext(msg, 0, NULL, 0, 0)) { wpabuf_free(msg); return NULL; } @@ -610,7 +610,7 @@ struct wpabuf * wps_build_wsc_nack(struct wps_data *wps) wps_build_enrollee_nonce(wps, msg) || wps_build_registrar_nonce(wps, msg) || wps_build_config_error(msg, wps->config_error) || - wps_build_wfa_ext(msg, 0, NULL, 0)) { + wps_build_wfa_ext(msg, 0, NULL, 0, 0)) { wpabuf_free(msg); return NULL; } @@ -726,7 +726,7 @@ struct wpabuf * wps_build_nfc_handover_req(struct wps_context *ctx, if (wps_build_oob_dev_pw(msg, DEV_PW_NFC_CONNECTION_HANDOVER, nfc_dh_pubkey, NULL, 0) || wps_build_uuid_e(msg, ctx->uuid) || - wps_build_wfa_ext(msg, 0, NULL, 0)) { + wps_build_wfa_ext(msg, 0, NULL, 0, 0)) { wpabuf_free(msg); return NULL; } @@ -809,7 +809,7 @@ struct wpabuf * wps_build_nfc_handover_sel(struct wps_context *ctx, wps_build_ssid(msg, ctx) || wps_build_ap_freq(msg, freq) || (bssid && wps_build_mac_addr(msg, bssid)) || - wps_build_wfa_ext(msg, 0, NULL, 0)) { + wps_build_wfa_ext(msg, 0, NULL, 0, 0)) { wpabuf_free(msg); return NULL; } @@ -848,7 +848,7 @@ struct wpabuf * wps_build_nfc_handover_req_p2p(struct wps_context *ctx, wps_build_rf_bands(&ctx->dev, msg, 0) || wps_build_serial_number(&ctx->dev, msg) || wps_build_uuid_e(msg, ctx->uuid) || - wps_build_wfa_ext(msg, 0, NULL, 0)) { + wps_build_wfa_ext(msg, 0, NULL, 0, 0)) { wpabuf_free(msg); return NULL; } @@ -900,7 +900,7 @@ struct wpabuf * wps_build_nfc_handover_sel_p2p(struct wps_context *ctx, wps_build_rf_bands(&ctx->dev, msg, 0) || wps_build_serial_number(&ctx->dev, msg) || wps_build_uuid_e(msg, ctx->uuid) || - wps_build_wfa_ext(msg, 0, NULL, 0)) { + wps_build_wfa_ext(msg, 0, NULL, 0, 0)) { wpabuf_free(msg); return NULL; } diff --git a/src/wps/wps_defs.h b/src/wps/wps_defs.h index 301864d..9fccb4e 100644 --- a/src/wps/wps_defs.h +++ b/src/wps/wps_defs.h @@ -152,7 +152,8 @@ enum { WFA_ELEM_NETWORK_KEY_SHAREABLE = 0x02, WFA_ELEM_REQUEST_TO_ENROLL = 0x03, WFA_ELEM_SETTINGS_DELAY_TIME = 0x04, - WFA_ELEM_REGISTRAR_CONFIGURATION_METHODS = 0x05 + WFA_ELEM_REGISTRAR_CONFIGURATION_METHODS = 0x05, + WFA_ELEM_MULTI_AP = 0x06 }; /* Device Password ID */ diff --git a/src/wps/wps_dev_attr.c b/src/wps/wps_dev_attr.c index 0d01211..b209fea 100644 --- a/src/wps/wps_dev_attr.c +++ b/src/wps/wps_dev_attr.c @@ -390,6 +390,14 @@ int wps_process_os_version(struct wps_device_data *dev, const u8 *ver) } +void wps_process_vendor_ext_m1(struct wps_device_data *dev, const u8 ext) +{ + dev->multi_ap_ext = ext; + wpa_printf(MSG_DEBUG, "WPS: Multi-AP extension value %02x", + dev->multi_ap_ext); +} + + int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands) { if (bands == NULL) { diff --git a/src/wps/wps_dev_attr.h b/src/wps/wps_dev_attr.h index c9034ad..a4b4173 100644 --- a/src/wps/wps_dev_attr.h +++ b/src/wps/wps_dev_attr.h @@ -29,6 +29,7 @@ int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg); int wps_process_device_attrs(struct wps_device_data *dev, struct wps_parse_attr *attr); int wps_process_os_version(struct wps_device_data *dev, const u8 *ver); +void wps_process_vendor_ext_m1(struct wps_device_data *dev, const u8 ext); int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands); void wps_device_data_free(struct wps_device_data *dev); int wps_build_vendor_ext(struct wps_device_data *dev, struct wpabuf *msg); diff --git a/src/wps/wps_enrollee.c b/src/wps/wps_enrollee.c index 4175077..80ed603 100644 --- a/src/wps/wps_enrollee.c +++ b/src/wps/wps_enrollee.c @@ -105,6 +105,7 @@ static struct wpabuf * wps_build_m1(struct wps_data *wps) { struct wpabuf *msg; u16 config_methods; + u8 multi_ap_backhaul_sta = 0; if (random_get_bytes(wps->nonce_e, WPS_NONCE_LEN) < 0) return NULL; @@ -134,6 +135,9 @@ static struct wpabuf * wps_build_m1(struct wps_data *wps) WPS_CONFIG_PHY_PUSHBUTTON); } + if (wps->multi_ap_backhaul_sta) + multi_ap_backhaul_sta = MULTI_AP_BACKHAUL_STA; + if (wps_build_version(msg) || wps_build_msg_type(msg, WPS_M1) || wps_build_uuid_e(msg, wps->uuid_e) || @@ -152,7 +156,7 @@ static struct wpabuf * wps_build_m1(struct wps_data *wps) wps_build_dev_password_id(msg, wps->dev_pw_id) || wps_build_config_error(msg, WPS_CFG_NO_ERROR) || wps_build_os_version(&wps->wps->dev, msg) || - wps_build_wfa_ext(msg, 0, NULL, 0) || + wps_build_wfa_ext(msg, 0, NULL, 0, multi_ap_backhaul_sta) || wps_build_vendor_ext_m1(&wps->wps->dev, msg)) { wpabuf_free(msg); return NULL; @@ -190,7 +194,7 @@ static struct wpabuf * wps_build_m3(struct wps_data *wps) wps_build_msg_type(msg, WPS_M3) || wps_build_registrar_nonce(wps, msg) || wps_build_e_hash(wps, msg) || - wps_build_wfa_ext(msg, 0, NULL, 0) || + wps_build_wfa_ext(msg, 0, NULL, 0, 0) || wps_build_authenticator(wps, msg)) { wpabuf_free(msg); return NULL; @@ -223,7 +227,7 @@ static struct wpabuf * wps_build_m5(struct wps_data *wps) wps_build_e_snonce1(wps, plain) || wps_build_key_wrap_auth(wps, plain) || wps_build_encr_settings(wps, msg, plain) || - wps_build_wfa_ext(msg, 0, NULL, 0) || + wps_build_wfa_ext(msg, 0, NULL, 0, 0) || wps_build_authenticator(wps, msg)) { wpabuf_clear_free(plain); wpabuf_free(msg); @@ -393,7 +397,7 @@ static struct wpabuf * wps_build_m7(struct wps_data *wps) (wps->wps->ap && wps_build_ap_settings(wps, plain)) || wps_build_key_wrap_auth(wps, plain) || wps_build_encr_settings(wps, msg, plain) || - wps_build_wfa_ext(msg, 0, NULL, 0) || + wps_build_wfa_ext(msg, 0, NULL, 0, 0) || wps_build_authenticator(wps, msg)) { wpabuf_clear_free(plain); wpabuf_free(msg); @@ -430,7 +434,7 @@ static struct wpabuf * wps_build_wsc_done(struct wps_data *wps) wps_build_msg_type(msg, WPS_WSC_DONE) || wps_build_enrollee_nonce(wps, msg) || wps_build_registrar_nonce(wps, msg) || - wps_build_wfa_ext(msg, 0, NULL, 0)) { + wps_build_wfa_ext(msg, 0, NULL, 0, 0)) { wpabuf_free(msg); return NULL; } diff --git a/src/wps/wps_er.c b/src/wps/wps_er.c index affd6a4..06a8fda 100644 --- a/src/wps/wps_er.c +++ b/src/wps/wps_er.c @@ -1530,7 +1530,7 @@ void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id, wps_er_build_selected_registrar(msg, sel_reg) || wps_er_build_dev_password_id(msg, dev_passwd_id) || wps_er_build_sel_reg_config_methods(msg, sel_reg_config_methods) || - wps_build_wfa_ext(msg, 0, auth_macs, count) || + wps_build_wfa_ext(msg, 0, auth_macs, count, 0) || wps_er_build_uuid_r(msg, er->wps->uuid)) { wpabuf_free(msg); return; @@ -2048,7 +2048,7 @@ struct wpabuf * wps_er_config_token_from_cred(struct wps_context *wps, data.wps = wps; data.use_cred = cred; if (wps_build_cred(&data, ret) || - wps_build_wfa_ext(ret, 0, NULL, 0)) { + wps_build_wfa_ext(ret, 0, NULL, 0, 0)) { wpabuf_free(ret); return NULL; } diff --git a/src/wps/wps_i.h b/src/wps/wps_i.h index fe0c60b..2cf22d4 100644 --- a/src/wps/wps_i.h +++ b/src/wps/wps_i.h @@ -125,6 +125,8 @@ struct wps_data { int pbc_in_m1; struct wps_nfc_pw_token *nfc_pw_token; + + int multi_ap_backhaul_sta; }; @@ -163,7 +165,8 @@ int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg, struct wpabuf *plain); int wps_build_version(struct wpabuf *msg); int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll, - const u8 *auth_macs, size_t auth_macs_count); + const u8 *auth_macs, size_t auth_macs_count, + u8 multi_ap_subelem); int wps_build_msg_type(struct wpabuf *msg, enum wps_msg_type msg_type); int wps_build_enrollee_nonce(struct wps_data *wps, struct wpabuf *msg); int wps_build_registrar_nonce(struct wps_data *wps, struct wpabuf *msg); diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c index 379925e..0ac5b28 100644 --- a/src/wps/wps_registrar.c +++ b/src/wps/wps_registrar.c @@ -188,6 +188,37 @@ struct wps_registrar { #ifdef WPS_WORKAROUNDS struct os_reltime pbc_ignore_start; #endif /* WPS_WORKAROUNDS */ + + /** + * multi_ap_backhaul_ssid - SSID to supply to a Multi-AP backhaul + * enrollee + * + * This SSID is used by the Registrar to fill in information for + * Credentials when the enrollee advertises it is a Multi-AP backhaul + * STA. + */ + u8 multi_ap_backhaul_ssid[SSID_MAX_LEN]; + + /** + * multi_ap_backhaul_ssid_len - Length of multi_ap_backhaul_ssid in + * octets + */ + size_t multi_ap_backhaul_ssid_len; + + /** + * multi_ap_backhaul_network_key - The Network Key (PSK) for the + * Multi-AP backhaul enrollee. + * + * This key can be either the ASCII passphrase (8..63 characters) or the + * 32-octet PSK (64 hex characters). + */ + u8 *multi_ap_backhaul_network_key; + + /** + * multi_ap_backhaul_network_key_len - Length of + * multi_ap_backhaul_network_key in octets + */ + size_t multi_ap_backhaul_network_key_len; }; @@ -667,6 +698,22 @@ wps_registrar_init(struct wps_context *wps, reg->dualband = cfg->dualband; reg->force_per_enrollee_psk = cfg->force_per_enrollee_psk; + if (cfg->multi_ap_backhaul_ssid) { + os_memcpy(reg->multi_ap_backhaul_ssid, + cfg->multi_ap_backhaul_ssid, + cfg->multi_ap_backhaul_ssid_len); + reg->multi_ap_backhaul_ssid_len = + cfg->multi_ap_backhaul_ssid_len; + } + if (cfg->multi_ap_backhaul_network_key) { + reg->multi_ap_backhaul_network_key = + os_memdup(cfg->multi_ap_backhaul_network_key, + cfg->multi_ap_backhaul_network_key_len); + if (reg->multi_ap_backhaul_network_key) + reg->multi_ap_backhaul_network_key_len = + cfg->multi_ap_backhaul_network_key_len; + } + if (wps_set_ie(reg)) { wps_registrar_deinit(reg); return NULL; @@ -704,6 +751,8 @@ void wps_registrar_deinit(struct wps_registrar *reg) eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); wps_registrar_flush(reg); wpabuf_clear_free(reg->extra_cred); + bin_clear_free(reg->multi_ap_backhaul_network_key, + reg->multi_ap_backhaul_network_key_len); os_free(reg); } @@ -1281,7 +1330,7 @@ static int wps_set_ie(struct wps_registrar *reg) wps_build_sel_reg_config_methods(reg, beacon) || wps_build_sel_pbc_reg_uuid_e(reg, beacon) || (reg->dualband && wps_build_rf_bands(®->wps->dev, beacon, 0)) || - wps_build_wfa_ext(beacon, 0, auth_macs, count) || + wps_build_wfa_ext(beacon, 0, auth_macs, count, 0) || wps_build_vendor_ext(®->wps->dev, beacon)) { wpabuf_free(beacon); wpabuf_free(probe); @@ -1311,7 +1360,7 @@ static int wps_set_ie(struct wps_registrar *reg) wps_build_device_attrs(®->wps->dev, probe) || wps_build_probe_config_methods(reg, probe) || (reg->dualband && wps_build_rf_bands(®->wps->dev, probe, 0)) || - wps_build_wfa_ext(probe, 0, auth_macs, count) || + wps_build_wfa_ext(probe, 0, auth_macs, count, 0) || wps_build_vendor_ext(®->wps->dev, probe)) { wpabuf_free(beacon); wpabuf_free(probe); @@ -1592,6 +1641,7 @@ int wps_build_credential_wrap(struct wpabuf *msg, int wps_build_cred(struct wps_data *wps, struct wpabuf *msg) { struct wpabuf *cred; + struct wps_registrar *reg = wps->wps->registrar; if (wps->wps->registrar->skip_cred_build) goto skip_cred_build; @@ -1603,6 +1653,29 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg) } os_memset(&wps->cred, 0, sizeof(wps->cred)); + if (wps->peer_dev.multi_ap_ext == MULTI_AP_BACKHAUL_STA && + reg->multi_ap_backhaul_ssid_len) { + wpa_printf(MSG_DEBUG, "WPS: Use backhaul STA credentials"); + os_memcpy(wps->cred.ssid, reg->multi_ap_backhaul_ssid, + reg->multi_ap_backhaul_ssid_len); + wps->cred.ssid_len = reg->multi_ap_backhaul_ssid_len; + /* Backhaul is always WPA2PSK */ + wps->cred.auth_type = WPS_AUTH_WPA2PSK; + wps->cred.encr_type = WPS_ENCR_AES; + /* Set MAC address in the Credential to be the Enrollee's MAC + * address + */ + os_memcpy(wps->cred.mac_addr, wps->mac_addr_e, ETH_ALEN); + if (reg->multi_ap_backhaul_network_key) { + os_memcpy(wps->cred.key, + reg->multi_ap_backhaul_network_key, + reg->multi_ap_backhaul_network_key_len); + wps->cred.key_len = + reg->multi_ap_backhaul_network_key_len; + } + goto use_provided; + } + os_memcpy(wps->cred.ssid, wps->wps->ssid, wps->wps->ssid_len); wps->cred.ssid_len = wps->wps->ssid_len; @@ -1845,7 +1918,7 @@ static struct wpabuf * wps_build_m2(struct wps_data *wps) wps_build_config_error(msg, WPS_CFG_NO_ERROR) || wps_build_dev_password_id(msg, wps->dev_pw_id) || wps_build_os_version(&wps->wps->dev, msg) || - wps_build_wfa_ext(msg, 0, NULL, 0)) { + wps_build_wfa_ext(msg, 0, NULL, 0, 0)) { wpabuf_free(msg); return NULL; } @@ -1913,7 +1986,7 @@ static struct wpabuf * wps_build_m2d(struct wps_data *wps) wps_build_assoc_state(wps, msg) || wps_build_config_error(msg, err) || wps_build_os_version(&wps->wps->dev, msg) || - wps_build_wfa_ext(msg, 0, NULL, 0)) { + wps_build_wfa_ext(msg, 0, NULL, 0, 0)) { wpabuf_free(msg); return NULL; } @@ -1949,7 +2022,7 @@ static struct wpabuf * wps_build_m4(struct wps_data *wps) wps_build_r_snonce1(wps, plain) || wps_build_key_wrap_auth(wps, plain) || wps_build_encr_settings(wps, msg, plain) || - wps_build_wfa_ext(msg, 0, NULL, 0) || + wps_build_wfa_ext(msg, 0, NULL, 0, 0) || wps_build_authenticator(wps, msg)) { wpabuf_clear_free(plain); wpabuf_free(msg); @@ -1984,7 +2057,7 @@ static struct wpabuf * wps_build_m6(struct wps_data *wps) wps_build_r_snonce2(wps, plain) || wps_build_key_wrap_auth(wps, plain) || wps_build_encr_settings(wps, msg, plain) || - wps_build_wfa_ext(msg, 0, NULL, 0) || + wps_build_wfa_ext(msg, 0, NULL, 0, 0) || wps_build_authenticator(wps, msg)) { wpabuf_clear_free(plain); wpabuf_free(msg); @@ -2021,7 +2094,7 @@ static struct wpabuf * wps_build_m8(struct wps_data *wps) (!wps->wps->ap && !wps->er && wps_build_ap_settings(wps, plain)) || wps_build_key_wrap_auth(wps, plain) || wps_build_encr_settings(wps, msg, plain) || - wps_build_wfa_ext(msg, 0, NULL, 0) || + wps_build_wfa_ext(msg, 0, NULL, 0, 0) || wps_build_authenticator(wps, msg)) { wpabuf_clear_free(plain); wpabuf_clear_free(msg); @@ -2705,6 +2778,7 @@ static enum wps_process_res wps_process_m1(struct wps_data *wps, wps->use_psk_key = 1; } #endif /* WPS_WORKAROUNDS */ + wps_process_vendor_ext_m1(&wps->peer_dev, attr->multi_ap_ext); wps->state = SEND_M2; return WPS_CONTINUE; diff --git a/src/wps/wps_upnp.c b/src/wps/wps_upnp.c index 0c458c6..ca893a4 100644 --- a/src/wps/wps_upnp.c +++ b/src/wps/wps_upnp.c @@ -599,7 +599,7 @@ static struct wpabuf * build_fake_wsc_ack(void) wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE); wpabuf_put_be16(msg, WPS_NONCE_LEN); wpabuf_put(msg, WPS_NONCE_LEN); - if (wps_build_wfa_ext(msg, 0, NULL, 0)) { + if (wps_build_wfa_ext(msg, 0, NULL, 0, 0)) { wpabuf_free(msg); return NULL; } diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk index d63e4c4..5296931 100644 --- a/wpa_supplicant/Android.mk +++ b/wpa_supplicant/Android.mk @@ -259,6 +259,9 @@ NEED_SHA512=y NEED_JSON=y NEED_GAS_SERVER=y NEED_BASE64=y +ifdef CONFIG_DPP2 +L_CFLAGS += -DCONFIG_DPP2 +endif endif ifdef CONFIG_OWE @@ -1422,44 +1425,25 @@ endif OBJS += ctrl_iface.c ctrl_iface_$(CONFIG_CTRL_IFACE).c endif -ifdef CONFIG_CTRL_IFACE_DBUS -DBUS=y -DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS -DDBUS_API_SUBJECT_TO_CHANGE -DBUS_OBJS += dbus/dbus_old.c dbus/dbus_old_handlers.c -ifdef CONFIG_WPS -DBUS_OBJS += dbus/dbus_old_handlers_wps.c -endif -DBUS_OBJS += dbus/dbus_dict_helpers.c -DBUS_CFLAGS += $(DBUS_INCLUDE) -endif - ifdef CONFIG_CTRL_IFACE_DBUS_NEW -DBUS=y -DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_NEW -DBUS_OBJS ?= dbus/dbus_dict_helpers.c -DBUS_OBJS += dbus/dbus_new_helpers.c -DBUS_OBJS += dbus/dbus_new.c dbus/dbus_new_handlers.c +L_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_NEW +OBJS += dbus/dbus_dict_helpers.c +OBJS += dbus/dbus_new_helpers.c +OBJS += dbus/dbus_new.c dbus/dbus_new_handlers.c +OBJS += dbus/dbus_common.c ifdef CONFIG_WPS -DBUS_OBJS += dbus/dbus_new_handlers_wps.c +OBJS += dbus/dbus_new_handlers_wps.c endif ifdef CONFIG_P2P -DBUS_OBJS += dbus/dbus_new_handlers_p2p.c +OBJS += dbus/dbus_new_handlers_p2p.c endif ifdef CONFIG_CTRL_IFACE_DBUS_INTRO -DBUS_OBJS += dbus/dbus_new_introspect.c -DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_INTRO -endif -DBUS_CFLAGS += $(DBUS_INCLUDE) +OBJS += dbus/dbus_new_introspect.c +L_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_INTRO endif - -ifdef DBUS -DBUS_CFLAGS += -DCONFIG_DBUS -DBUS_OBJS += dbus/dbus_common.c +L_CFLAGS += $(DBUS_INCLUDE) endif -OBJS += $(DBUS_OBJS) -L_CFLAGS += $(DBUS_CFLAGS) - ifdef CONFIG_CTRL_IFACE_BINDER WPA_SUPPLICANT_USE_BINDER=y L_CFLAGS += -DCONFIG_BINDER -DCONFIG_CTRL_IFACE_BINDER diff --git a/wpa_supplicant/ChangeLog b/wpa_supplicant/ChangeLog index bf4daaa..89119e7 100644 --- a/wpa_supplicant/ChangeLog +++ b/wpa_supplicant/ChangeLog @@ -1,5 +1,74 @@ ChangeLog for wpa_supplicant +2019-04-21 - v2.8 + * SAE changes + - added support for SAE Password Identifier + - changed default configuration to enable only groups 19, 20, 21 + (i.e., disable groups 25 and 26) and disable all unsuitable groups + completely based on REVmd changes + - do not regenerate PWE unnecessarily when the AP uses the + anti-clogging token mechanisms + - fixed some association cases where both SAE and FT-SAE were enabled + on both the station and the selected AP + - started to prefer FT-SAE over SAE AKM if both are enabled + - started to prefer FT-SAE over FT-PSK if both are enabled + - fixed FT-SAE when SAE PMKSA caching is used + - 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) + * EAP-pwd changes + - minimize timing and memory use differences in PWE derivation + [https://w1.fi/security/2019-2/] (CVE-2019-9495) + - verify server scalar/element + [https://w1.fi/security/2019-4/] (CVE-2019-9499) + - 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) + * fixed CONFIG_IEEE80211R=y (FT) build without CONFIG_FILS=y + * Hotspot 2.0 changes + - do not indicate release number that is higher than the one + AP supports + - added support for release number 3 + - enable PMF automatically for network profiles created from + credentials + * fixed OWE network profile saving + * fixed DPP network profile saving + * added support for RSN operating channel validation + (CONFIG_OCV=y and network profile parameter ocv=1) + * added Multi-AP backhaul STA support + * fixed build with LibreSSL + * number of MKA/MACsec fixes and extensions + * extended domain_match and domain_suffix_match to allow list of values + * fixed dNSName matching in domain_match and domain_suffix_match when + using wolfSSL + * started to prefer FT-EAP-SHA384 over WPA-EAP-SUITE-B-192 AKM if both + are enabled + * extended nl80211 Connect and external authentication to support + SAE, FT-SAE, FT-EAP-SHA384 + * fixed KEK2 derivation for FILS+FT + * extended client_cert file to allow loading of a chain of PEM + encoded certificates + * extended beacon reporting functionality + * extended D-Bus interface with number of new properties + * fixed a regression in FT-over-DS with mac80211-based drivers + * OpenSSL: allow systemwide policies to be overridden + * extended driver flags indication for separate 802.1X and PSK + 4-way handshake offload capability + * added support for random P2P Device/Interface Address use + * 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) + * removed support for the old D-Bus interface (CONFIG_CTRL_IFACE_DBUS) + * extended domain_match and domain_suffix_match to allow list of values + * added a RSN workaround for misbehaving PMF APs that advertise + IGTK/BIP KeyID using incorrect byte order + * fixed PTK rekeying with FILS and FT + 2018-12-02 - v2.7 * fixed WPA packet number reuse with replayed messages and key reinstallation diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index e55e062..e81238e 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -55,7 +55,6 @@ ALL += systemd/wpa_supplicant.service ALL += systemd/wpa_supplicant@.service ALL += systemd/wpa_supplicant-nl80211@.service ALL += systemd/wpa_supplicant-wired@.service -ALL += dbus/fi.epitest.hostap.WPASupplicant.service ALL += dbus/fi.w1.wpa_supplicant1.service ifdef CONFIG_BUILD_WPA_CLIENT_SO ALL += libwpa_client.so @@ -292,6 +291,9 @@ NEED_SHA512=y NEED_JSON=y NEED_GAS_SERVER=y NEED_BASE64=y +ifdef CONFIG_DPP2 +CFLAGS += -DCONFIG_DPP2 +endif endif ifdef CONFIG_OWE @@ -1576,35 +1578,17 @@ endif OBJS += ctrl_iface.o ctrl_iface_$(CONFIG_CTRL_IFACE).o endif -ifdef CONFIG_CTRL_IFACE_DBUS -DBUS=y -DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS -DDBUS_API_SUBJECT_TO_CHANGE -DBUS_OBJS += dbus/dbus_old.o dbus/dbus_old_handlers.o -ifdef CONFIG_WPS -DBUS_OBJS += dbus/dbus_old_handlers_wps.o -endif -DBUS_OBJS += dbus/dbus_dict_helpers.o -ifndef DBUS_LIBS -DBUS_LIBS := $(shell $(PKG_CONFIG) --libs dbus-1) -endif -ifndef DBUS_INCLUDE -DBUS_INCLUDE := $(shell $(PKG_CONFIG) --cflags dbus-1) -endif -DBUS_CFLAGS += $(DBUS_INCLUDE) -DBUS_INTERFACE=fi.epitest.hostap.WPASupplicant -endif - ifdef CONFIG_CTRL_IFACE_DBUS_NEW -DBUS=y -DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_NEW -DBUS_OBJS ?= dbus/dbus_dict_helpers.o -DBUS_OBJS += dbus/dbus_new_helpers.o -DBUS_OBJS += dbus/dbus_new.o dbus/dbus_new_handlers.o +CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_NEW +OBJS += dbus/dbus_dict_helpers.o +OBJS += dbus/dbus_new_helpers.o +OBJS += dbus/dbus_new.o dbus/dbus_new_handlers.o +OBJS += dbus/dbus_common.o ifdef CONFIG_WPS -DBUS_OBJS += dbus/dbus_new_handlers_wps.o +OBJS += dbus/dbus_new_handlers_wps.o endif ifdef CONFIG_P2P -DBUS_OBJS += dbus/dbus_new_handlers_p2p.o +OBJS += dbus/dbus_new_handlers_p2p.o endif ifndef DBUS_LIBS DBUS_LIBS := $(shell $(PKG_CONFIG) --libs dbus-1) @@ -1613,21 +1597,12 @@ ifndef DBUS_INCLUDE DBUS_INCLUDE := $(shell $(PKG_CONFIG) --cflags dbus-1) endif ifdef CONFIG_CTRL_IFACE_DBUS_INTRO -DBUS_OBJS += dbus/dbus_new_introspect.o -DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_INTRO -endif -DBUS_CFLAGS += $(DBUS_INCLUDE) -DBUS_INTERFACE=fi.w1.wpa_supplicant1 +OBJS += dbus/dbus_new_introspect.o +CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_INTRO endif - -ifdef DBUS -DBUS_CFLAGS += -DCONFIG_DBUS -DBUS_OBJS += dbus/dbus_common.o -endif - -OBJS += $(DBUS_OBJS) -CFLAGS += $(DBUS_CFLAGS) +CFLAGS += $(DBUS_INCLUDE) LIBS += $(DBUS_LIBS) +endif ifdef CONFIG_READLINE OBJS_c += ../src/utils/edit_readline.o @@ -1990,13 +1965,11 @@ else endif %.service: %.service.in - $(Q)sed -e 's|\@BINDIR\@|$(BINDIR)|g' \ - -e 's|\@DBUS_INTERFACE\@|$(DBUS_INTERFACE)|g' $< >$@ + $(Q)sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@ @$(E) " sed" $< %@.service: %.service.arg.in - $(Q)sed -e 's|\@BINDIR\@|$(BINDIR)|g' \ - -e 's|\@DBUS_INTERFACE\@|$(DBUS_INTERFACE)|g' $< >$@ + $(Q)sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@ @$(E) " sed" $< wpa_supplicant.exe: wpa_supplicant diff --git a/wpa_supplicant/android.config b/wpa_supplicant/android.config index ab80263..6536c11 100644 --- a/wpa_supplicant/android.config +++ b/wpa_supplicant/android.config @@ -91,9 +91,6 @@ CONFIG_EAP_PEAP=y CONFIG_EAP_TTLS=y # EAP-FAST -# Note: If OpenSSL is used as the TLS library, OpenSSL 1.0 or newer is needed -# for EAP-FAST support. Older OpenSSL releases would need to be patched, e.g., -# with openssl-0.9.8x-tls-extensions.patch, to add the needed functions. #CONFIG_EAP_FAST=y # EAP-GTC @@ -330,10 +327,6 @@ CONFIG_IEEE80211W=y #CONFIG_NDIS_EVENTS_INTEGRATED=y #PLATFORMSDKLIB="/opt/Program Files/Microsoft Platform SDK/Lib" -# Add support for old DBus control interface -# (fi.epitest.hostap.WPASupplicant) -#CONFIG_CTRL_IFACE_DBUS=y - # Add support for new DBus control interface # (fi.w1.hostap.wpa_supplicant1) #CONFIG_CTRL_IFACE_DBUS_NEW=y @@ -490,8 +483,8 @@ CONFIG_P2P=y # Enable TDLS support CONFIG_TDLS=y -# Wi-Fi Direct -# This can be used to enable Wi-Fi Direct extensions for P2P using an external +# Wi-Fi Display +# This can be used to enable Wi-Fi Display extensions for P2P using an external # program to control the additional information exchanges in the messages. CONFIG_WIFI_DISPLAY=y diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c index 453c99d..4e19169 100644 --- a/wpa_supplicant/ap.c +++ b/wpa_supplicant/ap.c @@ -334,10 +334,13 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, list[8] = -1; } conf->supported_rates = list; + } + #ifdef CONFIG_IEEE80211AX + if (ssid->mode == WPAS_MODE_P2P_GO || + ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) conf->ieee80211ax = ssid->he; #endif /* CONFIG_IEEE80211AX */ - } bss->isolate = !wpa_s->conf->p2p_intra_bss; bss->force_per_enrollee_psk = wpa_s->global->p2p_per_sta_psk; diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index b4e9952..2058175 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -2257,6 +2257,7 @@ static const struct parse_data ssid_fields[] = { { STR_KEYe(private_key_passwd) }, { STRe(dh_file) }, { STRe(subject_match) }, + { STRe(check_cert_subject) }, { STRe(altsubject_match) }, { STRe(domain_suffix_match) }, { STRe(domain_match) }, @@ -2267,6 +2268,7 @@ static const struct parse_data ssid_fields[] = { { STR_KEYe(private_key2_passwd) }, { STRe(dh_file2) }, { STRe(subject_match2) }, + { STRe(check_cert_subject2) }, { STRe(altsubject_match2) }, { STRe(domain_suffix_match2) }, { STRe(domain_match2) }, @@ -2525,6 +2527,7 @@ static void eap_peer_config_free(struct eap_peer_config *eap) str_clear_free(eap->private_key_passwd); os_free(eap->dh_file); os_free(eap->subject_match); + os_free(eap->check_cert_subject); os_free(eap->altsubject_match); os_free(eap->domain_suffix_match); os_free(eap->domain_match); @@ -2535,6 +2538,7 @@ static void eap_peer_config_free(struct eap_peer_config *eap) str_clear_free(eap->private_key2_passwd); os_free(eap->dh_file2); os_free(eap->subject_match2); + os_free(eap->check_cert_subject2); os_free(eap->altsubject_match2); os_free(eap->domain_suffix_match2); os_free(eap->domain_match2); @@ -4753,6 +4757,7 @@ static const struct global_parse_data global_fields[] = { { FUNC(os_version), CFG_CHANGED_OS_VERSION }, { STR(config_methods), CFG_CHANGED_CONFIG_METHODS }, { INT_RANGE(wps_cred_processing, 0, 2), 0 }, + { INT_RANGE(wps_cred_add_sae, 0, 1), 0 }, { FUNC(wps_vendor_ext_m1), CFG_CHANGED_VENDOR_EXTENSION }, #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h index 15d8c48..abbd8c9 100644 --- a/wpa_supplicant/config.h +++ b/wpa_supplicant/config.h @@ -745,6 +745,16 @@ struct wpa_config { */ int wps_cred_processing; + /** + * wps_cred_add_sae - Whether to enable SAE automatically for WPS + * + * 0 = only add the explicitly listed WPA2-PSK configuration + * 1 = add both the WPA2-PSK and SAE configuration and enable PMF so + * that the station gets configured in WPA3-Personal transition mode + * (supports both WPA2-Personal (PSK) and WPA3-Personal (SAE) APs). + */ + int wps_cred_add_sae; + #define MAX_SEC_DEVICE_TYPES 5 /** * sec_device_types - Secondary Device Types (P2P) diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c index 9b14699..26f6ee1 100644 --- a/wpa_supplicant/config_file.c +++ b/wpa_supplicant/config_file.c @@ -493,7 +493,7 @@ static void write_str(FILE *f, const char *field, struct wpa_ssid *ssid) if (value == NULL) return; fprintf(f, "\t%s=%s\n", field, value); - os_free(value); + str_clear_free(value); } @@ -782,6 +782,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) STR(private_key_passwd); STR(dh_file); STR(subject_match); + STR(check_cert_subject); STR(altsubject_match); STR(domain_suffix_match); STR(domain_match); @@ -792,6 +793,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) STR(private_key2_passwd); STR(dh_file2); STR(subject_match2); + STR(check_cert_subject2); STR(altsubject_match2); STR(domain_suffix_match2); STR(domain_match2); @@ -1187,6 +1189,9 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) if (config->wps_cred_processing) fprintf(f, "wps_cred_processing=%d\n", config->wps_cred_processing); + if (config->wps_cred_add_sae) + fprintf(f, "wps_cred_add_sae=%d\n", + config->wps_cred_add_sae); if (config->wps_vendor_ext_m1) { int i, len = wpabuf_len(config->wps_vendor_ext_m1); const u8 *p = wpabuf_head_u8(config->wps_vendor_ext_m1); diff --git a/wpa_supplicant/config_winreg.c b/wpa_supplicant/config_winreg.c index 0ce1830..6328e91 100644 --- a/wpa_supplicant/config_winreg.c +++ b/wpa_supplicant/config_winreg.c @@ -255,6 +255,8 @@ static int wpa_config_read_global(struct wpa_config *config, HKEY hk) errors++; wpa_config_read_reg_dword(hk, TEXT("wps_cred_processing"), &config->wps_cred_processing); + wpa_config_read_reg_dword(hk, TEXT("wps_cred_add_sae"), + &config->wps_cred_add_sae); #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P config->p2p_ssid_postfix = wpa_config_read_reg_string( @@ -604,6 +606,8 @@ static int wpa_config_write_global(struct wpa_config *config, HKEY hk) } wpa_config_write_reg_dword(hk, TEXT("wps_cred_processing"), config->wps_cred_processing, 0); + wpa_config_write_reg_dword(hk, TEXT("wps_cred_add_sae"), + config->wps_cred_add_sae, 0); #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P wpa_config_write_reg_string(hk, "p2p_ssid_postfix", @@ -892,6 +896,7 @@ static int wpa_config_write_network(HKEY hk, struct wpa_ssid *ssid, int id) STR(private_key_passwd); STR(dh_file); STR(subject_match); + STR(check_cert_subject); STR(altsubject_match); STR(ca_cert2); STR(ca_path2); @@ -900,6 +905,7 @@ static int wpa_config_write_network(HKEY hk, struct wpa_ssid *ssid, int id) STR(private_key2_passwd); STR(dh_file2); STR(subject_match2); + STR(check_cert_subject2); STR(altsubject_match2); STR(phase1); STR(phase2); diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 7882dcf..198ac56 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -1168,8 +1168,11 @@ static int wpa_supplicant_ctrl_iface_wps_pbc(struct wpa_supplicant *wpa_s, #ifdef CONFIG_AP u8 *_p2p_dev_addr = NULL; #endif /* CONFIG_AP */ + char *pos; + int multi_ap = 0; - if (cmd == NULL || os_strcmp(cmd, "any") == 0) { + if (!cmd || os_strcmp(cmd, "any") == 0 || + os_strncmp(cmd, "any ", 4) == 0) { _bssid = NULL; #ifdef CONFIG_P2P } else if (os_strncmp(cmd, "p2p_dev_addr=", 13) == 0) { @@ -1181,18 +1184,29 @@ static int wpa_supplicant_ctrl_iface_wps_pbc(struct wpa_supplicant *wpa_s, } _p2p_dev_addr = p2p_dev_addr; #endif /* CONFIG_P2P */ + } else if (os_strncmp(cmd, "multi_ap=", 9) == 0) { + _bssid = NULL; + multi_ap = atoi(cmd + 9); } else if (hwaddr_aton(cmd, bssid)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PBC: invalid BSSID '%s'", cmd); return -1; } + if (cmd) { + pos = os_strstr(cmd, " multi_ap="); + if (pos) { + pos += 10; + multi_ap = atoi(pos); + } + } + #ifdef CONFIG_AP if (wpa_s->ap_iface) return wpa_supplicant_ap_wps_pbc(wpa_s, _bssid, _p2p_dev_addr); #endif /* CONFIG_AP */ - return wpas_wps_start_pbc(wpa_s, _bssid, 0); + return wpas_wps_start_pbc(wpa_s, _bssid, 0, multi_ap); } @@ -2118,6 +2132,18 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, pos += ret; } + if (wpa_s->connection_set && + (wpa_s->connection_ht || wpa_s->connection_vht || + wpa_s->connection_he)) { + ret = os_snprintf(pos, end - pos, + "wifi_generation=%u\n", + wpa_s->connection_he ? 6 : + (wpa_s->connection_vht ? 5 : 4)); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + #ifdef CONFIG_AP if (wpa_s->ap_iface) { pos += ap_ctrl_iface_wpa_get_status(wpa_s, pos, @@ -3994,6 +4020,14 @@ static int ctrl_iface_get_capability_key_mgmt(int res, char *strict, } #endif /* CONFIG_IEEE80211R */ #endif /* CONFIG_FILS */ +#ifdef CONFIG_IEEE80211R + if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK) { + ret = os_snprintf(pos, end - pos, " FT-PSK"); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_SAE if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SAE) { ret = os_snprintf(pos, end - pos, " SAE"); @@ -4409,6 +4443,19 @@ static int wpa_supplicant_ctrl_iface_get_capability( return res; } +#ifdef CONFIG_DPP + if (os_strcmp(field, "dpp") == 0) { +#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); @@ -5049,10 +5096,11 @@ static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s, bss = NULL; dl_list_for_each(tmp, &wpa_s->bss_id, struct wpa_bss, list_id) { - if (i-- == 0) { + if (i == 0) { bss = tmp; break; } + i--; } } @@ -6383,6 +6431,8 @@ static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd) wpa_s->p2p_go_acs_band = HOSTAPD_MODE_IEEE80211ANY; wpa_s->p2p_go_do_acs = 1; } + } else { + wpa_s->p2p_go_do_acs = 0; } #endif /* CONFIG_ACS */ @@ -7665,7 +7715,7 @@ static int wpas_ctrl_iface_get_pref_freq_list( wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_PREF_FREQ_LIST iface_type=%d (%s)", - iface_type, buf); + iface_type, cmd); ret = wpa_drv_get_pref_freq_list(wpa_s, iface_type, &num, freq_list); if (ret) @@ -10626,7 +10676,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "DPP_BOOTSTRAP_GEN ", 18) == 0) { int res; - res = wpas_dpp_bootstrap_gen(wpa_s, buf + 18); + res = dpp_bootstrap_gen(wpa_s->dpp, buf + 18); if (res < 0) { reply_len = -1; } else { @@ -10635,12 +10685,12 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, reply_len = -1; } } else if (os_strncmp(buf, "DPP_BOOTSTRAP_REMOVE ", 21) == 0) { - if (wpas_dpp_bootstrap_remove(wpa_s, buf + 21) < 0) + if (dpp_bootstrap_remove(wpa_s->dpp, buf + 21) < 0) reply_len = -1; } else if (os_strncmp(buf, "DPP_BOOTSTRAP_GET_URI ", 22) == 0) { const char *uri; - uri = wpas_dpp_bootstrap_get_uri(wpa_s, atoi(buf + 22)); + uri = dpp_bootstrap_get_uri(wpa_s->dpp, atoi(buf + 22)); if (!uri) { reply_len = -1; } else { @@ -10649,8 +10699,8 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, reply_len = -1; } } else if (os_strncmp(buf, "DPP_BOOTSTRAP_INFO ", 19) == 0) { - reply_len = wpas_dpp_bootstrap_info(wpa_s, atoi(buf + 19), - reply, reply_size); + reply_len = dpp_bootstrap_info(wpa_s->dpp, atoi(buf + 19), + reply, reply_size); } else if (os_strncmp(buf, "DPP_AUTH_INIT ", 14) == 0) { if (wpas_dpp_auth_init(wpa_s, buf + 13) < 0) reply_len = -1; @@ -10663,7 +10713,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "DPP_CONFIGURATOR_ADD", 20) == 0) { int res; - res = wpas_dpp_configurator_add(wpa_s, buf + 20); + res = dpp_configurator_add(wpa_s->dpp, buf + 20); if (res < 0) { reply_len = -1; } else { @@ -10672,14 +10722,15 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, reply_len = -1; } } else if (os_strncmp(buf, "DPP_CONFIGURATOR_REMOVE ", 24) == 0) { - if (wpas_dpp_configurator_remove(wpa_s, buf + 24) < 0) + if (dpp_configurator_remove(wpa_s->dpp, buf + 24) < 0) reply_len = -1; } else if (os_strncmp(buf, "DPP_CONFIGURATOR_SIGN ", 22) == 0) { if (wpas_dpp_configurator_sign(wpa_s, buf + 21) < 0) reply_len = -1; } else if (os_strncmp(buf, "DPP_CONFIGURATOR_GET_KEY ", 25) == 0) { - reply_len = wpas_dpp_configurator_get_key(wpa_s, atoi(buf + 25), - reply, reply_size); + reply_len = dpp_configurator_get_key_id(wpa_s->dpp, + atoi(buf + 25), + reply, reply_size); } else if (os_strncmp(buf, "DPP_PKEX_ADD ", 13) == 0) { int res; diff --git a/wpa_supplicant/dbus/Makefile b/wpa_supplicant/dbus/Makefile index f355ebe..4d87004 100644 --- a/wpa_supplicant/dbus/Makefile +++ b/wpa_supplicant/dbus/Makefile @@ -36,7 +36,6 @@ CFLAGS += -DCONFIG_WPS endif CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_NEW -CFLAGS += -DCONFIG_CTRL_IFACE_DBUS ifndef DBUS_LIBS DBUS_LIBS := $(shell $(PKG_CONFIG) --libs dbus-1) @@ -54,8 +53,6 @@ CFLAGS += $(DBUS_INCLUDE) LIB_OBJS= \ dbus_common.o \ - dbus_old.o \ - dbus_old_handlers.o \ dbus_new.o \ dbus_new_handlers.o \ dbus_new_helpers.o \ @@ -63,7 +60,6 @@ LIB_OBJS= \ dbus_dict_helpers.o ifdef CONFIG_WPS -LIB_OBJS += dbus_old_handlers_wps.o LIB_OBJS += dbus_new_handlers_wps.o endif diff --git a/wpa_supplicant/dbus/dbus-wpa_supplicant.conf b/wpa_supplicant/dbus/dbus-wpa_supplicant.conf index 382dcb3..e81b495 100644 --- a/wpa_supplicant/dbus/dbus-wpa_supplicant.conf +++ b/wpa_supplicant/dbus/dbus-wpa_supplicant.conf @@ -3,11 +3,6 @@ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> <busconfig> <policy user="root"> - <allow own="fi.epitest.hostap.WPASupplicant"/> - - <allow send_destination="fi.epitest.hostap.WPASupplicant"/> - <allow send_interface="fi.epitest.hostap.WPASupplicant"/> - <allow own="fi.w1.wpa_supplicant1"/> <allow send_destination="fi.w1.wpa_supplicant1"/> @@ -15,9 +10,6 @@ <allow receive_sender="fi.w1.wpa_supplicant1" receive_type="signal"/> </policy> <policy context="default"> - <deny own="fi.epitest.hostap.WPASupplicant"/> - <deny send_destination="fi.epitest.hostap.WPASupplicant"/> - <deny own="fi.w1.wpa_supplicant1"/> <deny send_destination="fi.w1.wpa_supplicant1"/> <deny receive_sender="fi.w1.wpa_supplicant1" receive_type="signal"/> diff --git a/wpa_supplicant/dbus/dbus_common.c b/wpa_supplicant/dbus/dbus_common.c index 7ef6cad..efa6c7b 100644 --- a/wpa_supplicant/dbus/dbus_common.c +++ b/wpa_supplicant/dbus/dbus_common.c @@ -16,7 +16,6 @@ #include "dbus_common.h" #include "dbus_common_i.h" #include "dbus_new.h" -#include "dbus_old.h" #include "../wpa_supplicant_i.h" @@ -351,9 +350,6 @@ struct wpas_dbus_priv * wpas_dbus_init(struct wpa_global *global) #ifdef CONFIG_CTRL_IFACE_DBUS_NEW wpas_dbus_ctrl_iface_init(priv) < 0 || #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ -#ifdef CONFIG_CTRL_IFACE_DBUS - wpa_supplicant_dbus_ctrl_iface_init(priv) < 0 || -#endif /* CONFIG_CTRL_IFACE_DBUS */ wpas_dbus_init_common_finish(priv) < 0) { wpas_dbus_deinit(priv); return NULL; @@ -372,9 +368,5 @@ void wpas_dbus_deinit(struct wpas_dbus_priv *priv) wpas_dbus_ctrl_iface_deinit(priv); #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ -#ifdef CONFIG_CTRL_IFACE_DBUS - /* TODO: is any deinit needed? */ -#endif /* CONFIG_CTRL_IFACE_DBUS */ - wpas_dbus_deinit_common(priv); } diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c index 9f44f69..8cdd885 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c +++ b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c @@ -2693,7 +2693,7 @@ DBusMessage * wpas_dbus_handler_p2p_delete_service( if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) goto error; - if (wpa_dbus_dict_has_dict_entry(&iter_dict)) { + while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) goto error; @@ -2705,26 +2705,27 @@ DBusMessage * wpas_dbus_handler_p2p_delete_service( bonjour = 1; else goto error_clear; - wpa_dbus_dict_entry_clear(&entry); - } - } - if (upnp == 1) { - while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { - if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) - goto error; - if (os_strcmp(entry.key, "version") == 0 && - entry.type == DBUS_TYPE_INT32) - version = entry.uint32_value; - else if (os_strcmp(entry.key, "service") == 0 && - entry.type == DBUS_TYPE_STRING) { - os_free(service); - service = os_strdup(entry.str_value); - } else + } else if (os_strcmp(entry.key, "version") == 0 && + entry.type == DBUS_TYPE_INT32) { + version = entry.uint32_value; + } else if (os_strcmp(entry.key, "service") == 0 && + entry.type == DBUS_TYPE_STRING) { + os_free(service); + service = os_strdup(entry.str_value); + } else if (os_strcmp(entry.key, "query") == 0) { + if (entry.type != DBUS_TYPE_ARRAY || + entry.array_type != DBUS_TYPE_BYTE) goto error_clear; - - wpa_dbus_dict_entry_clear(&entry); + wpabuf_free(query); + query = wpabuf_alloc_copy(entry.bytearray_value, + entry.array_len); + } else { + goto error_clear; } + wpa_dbus_dict_entry_clear(&entry); + } + if (upnp == 1) { if (version <= 0 || service == NULL) goto error; @@ -2732,24 +2733,6 @@ DBusMessage * wpas_dbus_handler_p2p_delete_service( if (ret != 0) goto error; } else if (bonjour == 1) { - while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { - if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) - goto error; - - if (os_strcmp(entry.key, "query") == 0) { - if (entry.type != DBUS_TYPE_ARRAY || - entry.array_type != DBUS_TYPE_BYTE) - goto error_clear; - wpabuf_free(query); - query = wpabuf_alloc_copy( - entry.bytearray_value, - entry.array_len); - } else - goto error_clear; - - wpa_dbus_dict_entry_clear(&entry); - } - if (query == NULL) goto error; diff --git a/wpa_supplicant/dbus/dbus_new_handlers_wps.c b/wpa_supplicant/dbus/dbus_new_handlers_wps.c index 19c1a61..1594daf 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers_wps.c +++ b/wpa_supplicant/dbus/dbus_new_handlers_wps.c @@ -293,7 +293,7 @@ DBusMessage * wpas_dbus_handler_wps_start(DBusMessage *message, message, "invalid PIN"); } } else { - ret = wpas_wps_start_pbc(wpa_s, params.bssid, 0); + ret = wpas_wps_start_pbc(wpa_s, params.bssid, 0, 0); } if (ret < 0) { diff --git a/wpa_supplicant/dbus/dbus_old.c b/wpa_supplicant/dbus/dbus_old.c deleted file mode 100644 index 88227af..0000000 --- a/wpa_supplicant/dbus/dbus_old.c +++ /dev/null @@ -1,745 +0,0 @@ -/* - * WPA Supplicant / dbus-based control interface - * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc. - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -#include "includes.h" -#include <dbus/dbus.h> - -#include "common.h" -#include "eloop.h" -#include "wps/wps.h" -#include "../config.h" -#include "../wpa_supplicant_i.h" -#include "../bss.h" -#include "dbus_old.h" -#include "dbus_old_handlers.h" -#include "dbus_common_i.h" - - -/** - * wpas_dbus_decompose_object_path - Decompose an interface object path into parts - * @path: The dbus object path - * @network: (out) the configured network this object path refers to, if any - * @bssid: (out) the scanned bssid this object path refers to, if any - * Returns: The object path of the network interface this path refers to - * - * For a given object path, decomposes the object path into object id, network, - * and BSSID parts, if those parts exist. - */ -char * wpas_dbus_decompose_object_path(const char *path, char **network, - char **bssid) -{ - const unsigned int dev_path_prefix_len = - strlen(WPAS_DBUS_PATH_INTERFACES "/"); - char *obj_path_only; - char *next_sep; - - /* Be a bit paranoid about path */ - if (!path || strncmp(path, WPAS_DBUS_PATH_INTERFACES "/", - dev_path_prefix_len)) - return NULL; - - /* Ensure there's something at the end of the path */ - if ((path + dev_path_prefix_len)[0] == '\0') - return NULL; - - obj_path_only = os_strdup(path); - if (obj_path_only == NULL) - return NULL; - - next_sep = strchr(obj_path_only + dev_path_prefix_len, '/'); - if (next_sep != NULL) { - const char *net_part = strstr(next_sep, - WPAS_DBUS_NETWORKS_PART "/"); - const char *bssid_part = strstr(next_sep, - WPAS_DBUS_BSSIDS_PART "/"); - - if (network && net_part) { - /* Deal with a request for a configured network */ - const char *net_name = net_part + - strlen(WPAS_DBUS_NETWORKS_PART "/"); - *network = NULL; - if (strlen(net_name)) - *network = os_strdup(net_name); - } else if (bssid && bssid_part) { - /* Deal with a request for a scanned BSSID */ - const char *bssid_name = bssid_part + - strlen(WPAS_DBUS_BSSIDS_PART "/"); - if (strlen(bssid_name)) - *bssid = os_strdup(bssid_name); - else - *bssid = NULL; - } - - /* Cut off interface object path before "/" */ - *next_sep = '\0'; - } - - return obj_path_only; -} - - -/** - * wpas_dbus_new_invalid_iface_error - Return a new invalid interface error message - * @message: Pointer to incoming dbus message this error refers to - * Returns: A dbus error message - * - * Convenience function to create and return an invalid interface error - */ -DBusMessage * wpas_dbus_new_invalid_iface_error(DBusMessage *message) -{ - return dbus_message_new_error( - message, WPAS_ERROR_INVALID_IFACE, - "wpa_supplicant knows nothing about this interface."); -} - - -/** - * wpas_dbus_new_invalid_network_error - Return a new invalid network error message - * @message: Pointer to incoming dbus message this error refers to - * Returns: a dbus error message - * - * Convenience function to create and return an invalid network error - */ -DBusMessage * wpas_dbus_new_invalid_network_error(DBusMessage *message) -{ - return dbus_message_new_error(message, WPAS_ERROR_INVALID_NETWORK, - "The requested network does not exist."); -} - - -/** - * wpas_dbus_new_invalid_bssid_error - Return a new invalid bssid error message - * @message: Pointer to incoming dbus message this error refers to - * Returns: a dbus error message - * - * Convenience function to create and return an invalid bssid error - */ -static DBusMessage * wpas_dbus_new_invalid_bssid_error(DBusMessage *message) -{ - return dbus_message_new_error(message, WPAS_ERROR_INVALID_BSSID, - "The BSSID requested was invalid."); -} - - -/** - * wpas_dispatch_network_method - dispatch messages for configured networks - * @message: the incoming dbus message - * @wpa_s: a network interface's data - * @network_id: id of the configured network we're interested in - * Returns: a reply dbus message, or a dbus error message - * - * This function dispatches all incoming dbus messages for configured networks. - */ -static DBusMessage * wpas_dispatch_network_method(DBusMessage *message, - struct wpa_supplicant *wpa_s, - int network_id) -{ - DBusMessage *reply = NULL; - const char *method = dbus_message_get_member(message); - struct wpa_ssid *ssid; - - ssid = wpa_config_get_network(wpa_s->conf, network_id); - if (ssid == NULL) - return wpas_dbus_new_invalid_network_error(message); - - if (!strcmp(method, "set")) - reply = wpas_dbus_iface_set_network(message, wpa_s, ssid); - else if (!strcmp(method, "enable")) - reply = wpas_dbus_iface_enable_network(message, wpa_s, ssid); - else if (!strcmp(method, "disable")) - reply = wpas_dbus_iface_disable_network(message, wpa_s, ssid); - - return reply; -} - - -/** - * wpas_dispatch_bssid_method - dispatch messages for scanned networks - * @message: the incoming dbus message - * @wpa_s: a network interface's data - * @bssid: bssid of the scanned network we're interested in - * Returns: a reply dbus message, or a dbus error message - * - * This function dispatches all incoming dbus messages for scanned networks. - */ -static DBusMessage * wpas_dispatch_bssid_method(DBusMessage *message, - struct wpa_supplicant *wpa_s, - const char *bssid_txt) -{ - u8 bssid[ETH_ALEN]; - struct wpa_bss *bss; - - if (hexstr2bin(bssid_txt, bssid, ETH_ALEN) < 0) - return wpas_dbus_new_invalid_bssid_error(message); - - bss = wpa_bss_get_bssid(wpa_s, bssid); - if (bss == NULL) - return wpas_dbus_new_invalid_bssid_error(message); - - /* Dispatch the method call against the scanned bssid */ - if (os_strcmp(dbus_message_get_member(message), "properties") == 0) - return wpas_dbus_bssid_properties(message, wpa_s, bss); - - return NULL; -} - - -/** - * wpas_iface_message_handler - Dispatch messages for interfaces or networks - * @connection: Connection to the system message bus - * @message: An incoming dbus message - * @user_data: A pointer to a dbus control interface data structure - * Returns: Whether or not the message was handled - * - * This function dispatches all incoming dbus messages for network interfaces, - * or objects owned by them, such as scanned BSSIDs and configured networks. - */ -static DBusHandlerResult wpas_iface_message_handler(DBusConnection *connection, - DBusMessage *message, - void *user_data) -{ - struct wpa_supplicant *wpa_s = user_data; - const char *method = dbus_message_get_member(message); - const char *path = dbus_message_get_path(message); - const char *msg_interface = dbus_message_get_interface(message); - char *iface_obj_path = NULL; - char *network = NULL; - char *bssid = NULL; - DBusMessage *reply = NULL; - - /* Caller must specify a message interface */ - if (!msg_interface) - goto out; - - wpa_printf(MSG_MSGDUMP, "dbus[old/iface]: %s.%s (%s) [%s]", - msg_interface, method, path, - dbus_message_get_signature(message)); - - iface_obj_path = wpas_dbus_decompose_object_path(path, &network, - &bssid); - if (iface_obj_path == NULL) { - reply = wpas_dbus_new_invalid_iface_error(message); - goto out; - } - - /* Make sure the message's object path actually refers to the - * wpa_supplicant structure it's supposed to (which is wpa_s) - */ - if (wpa_supplicant_get_iface_by_dbus_path(wpa_s->global, - iface_obj_path) != wpa_s) { - reply = wpas_dbus_new_invalid_iface_error(message); - goto out; - } - - if (network && !strcmp(msg_interface, WPAS_DBUS_IFACE_NETWORK)) { - /* A method for one of this interface's configured networks */ - int nid = strtoul(network, NULL, 10); - - if (errno != EINVAL) - reply = wpas_dispatch_network_method(message, wpa_s, - nid); - else - reply = wpas_dbus_new_invalid_network_error(message); - } else if (bssid && !strcmp(msg_interface, WPAS_DBUS_IFACE_BSSID)) { - /* A method for one of this interface's scanned BSSIDs */ - reply = wpas_dispatch_bssid_method(message, wpa_s, bssid); - } else if (!strcmp(msg_interface, WPAS_DBUS_IFACE_INTERFACE)) { - /* A method for an interface only. */ - if (!strcmp(method, "scan")) - reply = wpas_dbus_iface_scan(message, wpa_s); - else if (!strcmp(method, "scanResults")) - reply = wpas_dbus_iface_scan_results(message, wpa_s); - else if (!strcmp(method, "addNetwork")) - reply = wpas_dbus_iface_add_network(message, wpa_s); - else if (!strcmp(method, "removeNetwork")) - reply = wpas_dbus_iface_remove_network(message, wpa_s); - else if (!strcmp(method, "selectNetwork")) - reply = wpas_dbus_iface_select_network(message, wpa_s); - else if (!strcmp(method, "capabilities")) - reply = wpas_dbus_iface_capabilities(message, wpa_s); - else if (!strcmp(method, "disconnect")) - reply = wpas_dbus_iface_disconnect(message, wpa_s); - else if (!strcmp(method, "setAPScan")) - reply = wpas_dbus_iface_set_ap_scan(message, wpa_s); - else if (!strcmp(method, "setSmartcardModules")) - reply = wpas_dbus_iface_set_smartcard_modules(message, - wpa_s); - else if (!strcmp(method, "state")) - reply = wpas_dbus_iface_get_state(message, wpa_s); - else if (!strcmp(method, "scanning")) - reply = wpas_dbus_iface_get_scanning(message, wpa_s); -#ifndef CONFIG_NO_CONFIG_BLOBS - else if (!strcmp(method, "setBlobs")) - reply = wpas_dbus_iface_set_blobs(message, wpa_s); - else if (!strcmp(method, "removeBlobs")) - reply = wpas_dbus_iface_remove_blobs(message, wpa_s); -#endif /* CONFIG_NO_CONFIG_BLOBS */ -#ifdef CONFIG_WPS - else if (os_strcmp(method, "wpsPbc") == 0) - reply = wpas_dbus_iface_wps_pbc(message, wpa_s); - else if (os_strcmp(method, "wpsPin") == 0) - reply = wpas_dbus_iface_wps_pin(message, wpa_s); - else if (os_strcmp(method, "wpsReg") == 0) - reply = wpas_dbus_iface_wps_reg(message, wpa_s); -#endif /* CONFIG_WPS */ - else if (os_strcmp(method, "flush") == 0) - reply = wpas_dbus_iface_flush(message, wpa_s); - } - - /* If the message was handled, send back the reply */ -out: - if (reply) { - if (!dbus_message_get_no_reply(message)) - dbus_connection_send(connection, reply, NULL); - dbus_message_unref(reply); - } - - os_free(iface_obj_path); - os_free(network); - os_free(bssid); - return reply ? DBUS_HANDLER_RESULT_HANDLED : - DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - - -/** - * wpas_message_handler - dispatch incoming dbus messages - * @connection: connection to the system message bus - * @message: an incoming dbus message - * @user_data: a pointer to a dbus control interface data structure - * Returns: whether or not the message was handled - * - * This function dispatches all incoming dbus messages to the correct - * handlers, depending on what the message's target object path is, - * and what the method call is. - */ -static DBusHandlerResult wpas_message_handler(DBusConnection *connection, - DBusMessage *message, void *user_data) -{ - struct wpas_dbus_priv *ctrl_iface = user_data; - const char *method; - const char *path; - const char *msg_interface; - DBusMessage *reply = NULL; - - method = dbus_message_get_member(message); - path = dbus_message_get_path(message); - msg_interface = dbus_message_get_interface(message); - if (!method || !path || !ctrl_iface || !msg_interface) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - - wpa_printf(MSG_MSGDUMP, "dbus[old]: %s.%s (%s) [%s]", - msg_interface, method, path, - dbus_message_get_signature(message)); - - /* Validate the method interface */ - if (strcmp(msg_interface, WPAS_DBUS_INTERFACE) != 0) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - - if (!strcmp(path, WPAS_DBUS_PATH)) { - /* dispatch methods against our global dbus interface here */ - if (!strcmp(method, "addInterface")) { - reply = wpas_dbus_global_add_interface( - message, ctrl_iface->global); - } else if (!strcmp(method, "removeInterface")) { - reply = wpas_dbus_global_remove_interface( - message, ctrl_iface->global); - } else if (!strcmp(method, "getInterface")) { - reply = wpas_dbus_global_get_interface( - message, ctrl_iface->global); - } else if (!strcmp(method, "setDebugParams")) { - reply = wpas_dbus_global_set_debugparams( - message, ctrl_iface->global); - } - } - - /* If the message was handled, send back the reply */ - if (reply) { - if (!dbus_message_get_no_reply(message)) - dbus_connection_send(connection, reply, NULL); - dbus_message_unref(reply); - } - - return reply ? DBUS_HANDLER_RESULT_HANDLED : - DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - - -/** - * wpa_supplicant_dbus_notify_scan_results - Send a scan results signal - * @wpa_s: %wpa_supplicant network interface data - * Returns: 0 on success, -1 on failure - * - * Notify listeners that this interface has updated scan results. - */ -void wpa_supplicant_dbus_notify_scan_results(struct wpa_supplicant *wpa_s) -{ - struct wpas_dbus_priv *iface = wpa_s->global->dbus; - DBusMessage *_signal; - - /* Do nothing if the control interface is not turned on */ - if (iface == NULL || !wpa_s->dbus_path) - return; - - _signal = dbus_message_new_signal(wpa_s->dbus_path, - WPAS_DBUS_IFACE_INTERFACE, - "ScanResultsAvailable"); - if (_signal == NULL) { - wpa_printf(MSG_ERROR, - "dbus: Not enough memory to send scan results signal"); - return; - } - dbus_connection_send(iface->con, _signal, NULL); - dbus_message_unref(_signal); -} - - -/** - * wpa_supplicant_dbus_notify_state_change - Send a state change signal - * @wpa_s: %wpa_supplicant network interface data - * @new_state: new state wpa_supplicant is entering - * @old_state: old state wpa_supplicant is leaving - * Returns: 0 on success, -1 on failure - * - * Notify listeners that wpa_supplicant has changed state - */ -void wpa_supplicant_dbus_notify_state_change(struct wpa_supplicant *wpa_s, - enum wpa_states new_state, - enum wpa_states old_state) -{ - struct wpas_dbus_priv *iface; - DBusMessage *_signal = NULL; - const char *new_state_str, *old_state_str; - - if (wpa_s->dbus_path == NULL) - return; /* Skip signal since D-Bus setup is not yet ready */ - - /* Do nothing if the control interface is not turned on */ - if (wpa_s->global == NULL) - return; - iface = wpa_s->global->dbus; - if (iface == NULL) - return; - - /* Only send signal if state really changed */ - if (new_state == old_state) - return; - - _signal = dbus_message_new_signal(wpa_s->dbus_path, - WPAS_DBUS_IFACE_INTERFACE, - "StateChange"); - if (_signal == NULL) { - wpa_printf(MSG_ERROR, - "dbus: %s: could not create dbus signal; likely out of memory", - __func__); - return; - } - - new_state_str = wpa_supplicant_state_txt(new_state); - old_state_str = wpa_supplicant_state_txt(old_state); - - if (!dbus_message_append_args(_signal, - DBUS_TYPE_STRING, &new_state_str, - DBUS_TYPE_STRING, &old_state_str, - DBUS_TYPE_INVALID)) { - wpa_printf(MSG_ERROR, - "dbus: %s: Not enough memory to construct state change signal", - __func__); - goto out; - } - - dbus_connection_send(iface->con, _signal, NULL); - -out: - dbus_message_unref(_signal); -} - - -/** - * wpa_supplicant_dbus_notify_scanning - send scanning status - * @wpa_s: %wpa_supplicant network interface data - * Returns: 0 on success, -1 on failure - * - * Notify listeners of interface scanning state changes - */ -void wpa_supplicant_dbus_notify_scanning(struct wpa_supplicant *wpa_s) -{ - struct wpas_dbus_priv *iface = wpa_s->global->dbus; - DBusMessage *_signal; - dbus_bool_t scanning = wpa_s->scanning ? TRUE : FALSE; - - /* Do nothing if the control interface is not turned on */ - if (iface == NULL || !wpa_s->dbus_path) - return; - - _signal = dbus_message_new_signal(wpa_s->dbus_path, - WPAS_DBUS_IFACE_INTERFACE, - "Scanning"); - if (_signal == NULL) { - wpa_printf(MSG_ERROR, - "dbus: Not enough memory to send scan results signal"); - return; - } - - if (dbus_message_append_args(_signal, - DBUS_TYPE_BOOLEAN, &scanning, - DBUS_TYPE_INVALID)) { - dbus_connection_send(iface->con, _signal, NULL); - } else { - wpa_printf(MSG_ERROR, - "dbus: Not enough memory to construct signal"); - } - dbus_message_unref(_signal); -} - - -#ifdef CONFIG_WPS -void wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s, - const struct wps_credential *cred) -{ - struct wpas_dbus_priv *iface; - DBusMessage *_signal = NULL; - - /* Do nothing if the control interface is not turned on */ - if (wpa_s->global == NULL) - return; - iface = wpa_s->global->dbus; - if (iface == NULL || !wpa_s->dbus_path) - return; - - _signal = dbus_message_new_signal(wpa_s->dbus_path, - WPAS_DBUS_IFACE_INTERFACE, - "WpsCred"); - if (_signal == NULL) { - wpa_printf(MSG_ERROR, - "dbus: %s: Could not create dbus signal; likely out of memory", - __func__); - return; - } - - if (!dbus_message_append_args(_signal, - DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, - &cred->cred_attr, cred->cred_attr_len, - DBUS_TYPE_INVALID)) { - wpa_printf(MSG_ERROR, - "dbus: %s: Not enough memory to construct signal", - __func__); - goto out; - } - - dbus_connection_send(iface->con, _signal, NULL); - -out: - dbus_message_unref(_signal); -} -#else /* CONFIG_WPS */ -void wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s, - const struct wps_credential *cred) -{ -} -#endif /* CONFIG_WPS */ - -void wpa_supplicant_dbus_notify_certification(struct wpa_supplicant *wpa_s, - int depth, const char *subject, - const char *cert_hash, - const struct wpabuf *cert) -{ - struct wpas_dbus_priv *iface; - DBusMessage *_signal = NULL; - const char *hash; - const char *cert_hex; - int cert_hex_len; - - /* Do nothing if the control interface is not turned on */ - if (wpa_s->global == NULL) - return; - iface = wpa_s->global->dbus; - if (iface == NULL || !wpa_s->dbus_path) - return; - - _signal = dbus_message_new_signal(wpa_s->dbus_path, - WPAS_DBUS_IFACE_INTERFACE, - "Certification"); - if (_signal == NULL) { - wpa_printf(MSG_ERROR, - "dbus: %s: Could not create dbus signal; likely out of memory", - __func__); - return; - } - - hash = cert_hash ? cert_hash : ""; - cert_hex = cert ? wpabuf_head(cert) : ""; - cert_hex_len = cert ? wpabuf_len(cert) : 0; - - if (!dbus_message_append_args(_signal, - DBUS_TYPE_INT32, &depth, - DBUS_TYPE_STRING, &subject, - DBUS_TYPE_STRING, &hash, - DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, - &cert_hex, cert_hex_len, - DBUS_TYPE_INVALID)) { - wpa_printf(MSG_ERROR, - "dbus: %s: Not enough memory to construct signal", - __func__); - goto out; - } - - dbus_connection_send(iface->con, _signal, NULL); - -out: - dbus_message_unref(_signal); - -} - - -/** - * wpa_supplicant_dbus_ctrl_iface_init - Initialize dbus control interface - * @global: Pointer to global data from wpa_supplicant_init() - * Returns: 0 on success, -1 on failure - * - * Initialize the dbus control interface and start receiving commands from - * external programs over the bus. - */ -int wpa_supplicant_dbus_ctrl_iface_init(struct wpas_dbus_priv *iface) -{ - DBusError error; - int ret = -1; - DBusObjectPathVTable wpas_vtable = { - NULL, &wpas_message_handler, NULL, NULL, NULL, NULL - }; - - /* Register the message handler for the global dbus interface */ - if (!dbus_connection_register_object_path(iface->con, - WPAS_DBUS_PATH, &wpas_vtable, - iface)) { - wpa_printf(MSG_ERROR, "dbus: Could not set up message handler"); - return -1; - } - - /* Register our service with the message bus */ - dbus_error_init(&error); - switch (dbus_bus_request_name(iface->con, WPAS_DBUS_SERVICE, - 0, &error)) { - case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER: - ret = 0; - break; - case DBUS_REQUEST_NAME_REPLY_EXISTS: - case DBUS_REQUEST_NAME_REPLY_IN_QUEUE: - case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER: - wpa_printf(MSG_ERROR, - "dbus: Could not request service name: already registered"); - break; - default: - wpa_printf(MSG_ERROR, - "dbus: Could not request service name: %s %s", - error.name, error.message); - break; - } - dbus_error_free(&error); - - if (ret != 0) - return -1; - - wpa_printf(MSG_DEBUG, "Providing DBus service '" WPAS_DBUS_SERVICE - "'."); - - return 0; -} - - -/** - * wpas_dbus_register_new_iface - Register a new interface with dbus - * @wpa_s: %wpa_supplicant interface description structure to register - * Returns: 0 on success, -1 on error - * - * Registers a new interface with dbus and assigns it a dbus object path. - */ -int wpas_dbus_register_iface(struct wpa_supplicant *wpa_s) -{ - struct wpas_dbus_priv *ctrl_iface = wpa_s->global->dbus; - DBusConnection * con; - u32 next; - DBusObjectPathVTable vtable = { - NULL, &wpas_iface_message_handler, NULL, NULL, NULL, NULL - }; - - /* Do nothing if the control interface is not turned on */ - if (ctrl_iface == NULL) - return 0; - - con = ctrl_iface->con; - next = ctrl_iface->next_objid++; - - /* Create and set the interface's object path */ - wpa_s->dbus_path = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX); - if (wpa_s->dbus_path == NULL) - return -1; - os_snprintf(wpa_s->dbus_path, WPAS_DBUS_OBJECT_PATH_MAX, - WPAS_DBUS_PATH_INTERFACES "/%u", - next); - - /* Register the message handler for the interface functions */ - if (!dbus_connection_register_fallback(con, wpa_s->dbus_path, &vtable, - wpa_s)) { - wpa_printf(MSG_ERROR, - "dbus: Could not set up message handler for interface %s", - wpa_s->ifname); - return -1; - } - - return 0; -} - - -/** - * wpas_dbus_unregister_iface - Unregister an interface from dbus - * @wpa_s: wpa_supplicant interface structure - * Returns: 0 on success, -1 on failure - * - * Unregisters the interface with dbus - */ -int wpas_dbus_unregister_iface(struct wpa_supplicant *wpa_s) -{ - struct wpas_dbus_priv *ctrl_iface; - DBusConnection *con; - - /* Do nothing if the control interface is not turned on */ - if (wpa_s == NULL || wpa_s->global == NULL) - return 0; - ctrl_iface = wpa_s->global->dbus; - if (ctrl_iface == NULL || wpa_s->dbus_path == NULL) - return 0; - - con = ctrl_iface->con; - if (!dbus_connection_unregister_object_path(con, wpa_s->dbus_path)) - return -1; - - os_free(wpa_s->dbus_path); - wpa_s->dbus_path = NULL; - - return 0; -} - - -/** - * wpa_supplicant_get_iface_by_dbus_path - Get a new network interface - * @global: Pointer to global data from wpa_supplicant_init() - * @path: Pointer to a dbus object path representing an interface - * Returns: Pointer to the interface or %NULL if not found - */ -struct wpa_supplicant * wpa_supplicant_get_iface_by_dbus_path( - struct wpa_global *global, const char *path) -{ - struct wpa_supplicant *wpa_s; - - for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { - if (wpa_s->dbus_path && strcmp(wpa_s->dbus_path, path) == 0) - return wpa_s; - } - return NULL; -} diff --git a/wpa_supplicant/dbus/dbus_old.h b/wpa_supplicant/dbus/dbus_old.h deleted file mode 100644 index 451a9f8..0000000 --- a/wpa_supplicant/dbus/dbus_old.h +++ /dev/null @@ -1,142 +0,0 @@ -/* - * WPA Supplicant / dbus-based control interface - * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc. - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -#ifndef CTRL_IFACE_DBUS_H -#define CTRL_IFACE_DBUS_H - -struct wps_credential; - -#ifdef CONFIG_CTRL_IFACE_DBUS - -#define WPAS_DBUS_OBJECT_PATH_MAX 150 - -#define WPAS_DBUS_SERVICE "fi.epitest.hostap.WPASupplicant" -#define WPAS_DBUS_PATH "/fi/epitest/hostap/WPASupplicant" -#define WPAS_DBUS_INTERFACE "fi.epitest.hostap.WPASupplicant" - -#define WPAS_DBUS_PATH_INTERFACES WPAS_DBUS_PATH "/Interfaces" -#define WPAS_DBUS_IFACE_INTERFACE WPAS_DBUS_INTERFACE ".Interface" - -#define WPAS_DBUS_NETWORKS_PART "Networks" -#define WPAS_DBUS_IFACE_NETWORK WPAS_DBUS_INTERFACE ".Network" - -#define WPAS_DBUS_BSSIDS_PART "BSSIDs" -#define WPAS_DBUS_IFACE_BSSID WPAS_DBUS_INTERFACE ".BSSID" - - -/* Errors */ -#define WPAS_ERROR_INVALID_NETWORK \ - WPAS_DBUS_IFACE_INTERFACE ".InvalidNetwork" -#define WPAS_ERROR_INVALID_BSSID \ - WPAS_DBUS_IFACE_INTERFACE ".InvalidBSSID" - -#define WPAS_ERROR_INVALID_OPTS \ - WPAS_DBUS_INTERFACE ".InvalidOptions" -#define WPAS_ERROR_INVALID_IFACE \ - WPAS_DBUS_INTERFACE ".InvalidInterface" - -#define WPAS_ERROR_ADD_ERROR \ - WPAS_DBUS_INTERFACE ".AddError" -#define WPAS_ERROR_EXISTS_ERROR \ - WPAS_DBUS_INTERFACE ".ExistsError" -#define WPAS_ERROR_REMOVE_ERROR \ - WPAS_DBUS_INTERFACE ".RemoveError" - -#define WPAS_ERROR_SCAN_ERROR \ - WPAS_DBUS_IFACE_INTERFACE ".ScanError" -#define WPAS_ERROR_ADD_NETWORK_ERROR \ - WPAS_DBUS_IFACE_INTERFACE ".AddNetworkError" -#define WPAS_ERROR_INTERNAL_ERROR \ - WPAS_DBUS_IFACE_INTERFACE ".InternalError" -#define WPAS_ERROR_REMOVE_NETWORK_ERROR \ - WPAS_DBUS_IFACE_INTERFACE ".RemoveNetworkError" - -#define WPAS_ERROR_WPS_PBC_ERROR \ - WPAS_DBUS_IFACE_INTERFACE ".WpsPbcError" -#define WPAS_ERROR_WPS_PIN_ERROR \ - WPAS_DBUS_IFACE_INTERFACE ".WpsPinError" -#define WPAS_ERROR_WPS_REG_ERROR \ - WPAS_DBUS_IFACE_INTERFACE ".WpsRegError" - -#define WPAS_DBUS_BSSID_FORMAT "%02x%02x%02x%02x%02x%02x" - -struct wpa_global; -struct wpa_supplicant; - -int wpa_supplicant_dbus_ctrl_iface_init(struct wpas_dbus_priv *iface); -void wpa_supplicant_dbus_notify_scan_results(struct wpa_supplicant *wpa_s); -void wpa_supplicant_dbus_notify_scanning(struct wpa_supplicant *wpa_s); -void wpa_supplicant_dbus_notify_state_change(struct wpa_supplicant *wpa_s, - enum wpa_states new_state, - enum wpa_states old_state); -void wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s, - const struct wps_credential *cred); -void wpa_supplicant_dbus_notify_certification(struct wpa_supplicant *wpa_s, - int depth, const char *subject, - const char *cert_hash, - const struct wpabuf *cert); - -char * wpas_dbus_decompose_object_path(const char *path, char **network, - char **bssid); - -int wpas_dbus_register_iface(struct wpa_supplicant *wpa_s); -int wpas_dbus_unregister_iface(struct wpa_supplicant *wpa_s); - - -/* Methods internal to the dbus control interface */ -struct wpa_supplicant * wpa_supplicant_get_iface_by_dbus_path( - struct wpa_global *global, const char *path); - -#else /* CONFIG_CTRL_IFACE_DBUS */ - -static inline void -wpa_supplicant_dbus_notify_scan_results(struct wpa_supplicant *wpa_s) -{ -} - -static inline void -wpa_supplicant_dbus_notify_scanning(struct wpa_supplicant *wpa_s) -{ -} - -static inline void -wpa_supplicant_dbus_notify_state_change(struct wpa_supplicant *wpa_s, - enum wpa_states new_state, - enum wpa_states old_state) -{ -} - -static inline void -wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s, - const struct wps_credential *cred) -{ -} - -static inline void -wpa_supplicant_dbus_notify_certification(struct wpa_supplicant *wpa_s, - int depth, const char *subject, - const char *cert_hash, - const struct wpabuf *cert) -{ -} - -static inline int -wpas_dbus_register_iface(struct wpa_supplicant *wpa_s) -{ - return 0; -} - -static inline int -wpas_dbus_unregister_iface(struct wpa_supplicant *wpa_s) -{ - return 0; -} - -#endif /* CONFIG_CTRL_IFACE_DBUS */ - -#endif /* CTRL_IFACE_DBUS_H */ diff --git a/wpa_supplicant/dbus/dbus_old_handlers.c b/wpa_supplicant/dbus/dbus_old_handlers.c deleted file mode 100644 index e540832..0000000 --- a/wpa_supplicant/dbus/dbus_old_handlers.c +++ /dev/null @@ -1,1393 +0,0 @@ -/* - * WPA Supplicant / dbus-based control interface - * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc. - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -#include "includes.h" -#include <dbus/dbus.h> - -#include "common.h" -#include "eap_peer/eap_methods.h" -#include "common/ieee802_11_defs.h" -#include "eapol_supp/eapol_supp_sm.h" -#include "rsn_supp/wpa.h" -#include "../config.h" -#include "../wpa_supplicant_i.h" -#include "../driver_i.h" -#include "../notify.h" -#include "../wpas_glue.h" -#include "../bss.h" -#include "../scan.h" -#include "dbus_old.h" -#include "dbus_old_handlers.h" -#include "dbus_dict_helpers.h" - -/** - * wpas_dbus_new_invalid_opts_error - Return a new invalid options error message - * @message: Pointer to incoming dbus message this error refers to - * Returns: a dbus error message - * - * Convenience function to create and return an invalid options error - */ -DBusMessage * wpas_dbus_new_invalid_opts_error(DBusMessage *message, - const char *arg) -{ - DBusMessage *reply; - - reply = dbus_message_new_error( - message, WPAS_ERROR_INVALID_OPTS, - "Did not receive correct message arguments."); - if (arg != NULL) - dbus_message_append_args(reply, DBUS_TYPE_STRING, &arg, - DBUS_TYPE_INVALID); - - return reply; -} - - -/** - * wpas_dbus_new_success_reply - Return a new success reply message - * @message: Pointer to incoming dbus message this reply refers to - * Returns: a dbus message containing a single UINT32 that indicates - * success (ie, a value of 1) - * - * Convenience function to create and return a success reply message - */ -DBusMessage * wpas_dbus_new_success_reply(DBusMessage *message) -{ - DBusMessage *reply; - unsigned int success = 1; - - reply = dbus_message_new_method_return(message); - dbus_message_append_args(reply, DBUS_TYPE_UINT32, &success, - DBUS_TYPE_INVALID); - return reply; -} - - -/** - * wpas_dbus_global_add_interface - Request registration of a network interface - * @message: Pointer to incoming dbus message - * @global: %wpa_supplicant global data structure - * Returns: The object path of the new interface object, - * or a dbus error message with more information - * - * Handler function for "addInterface" method call. Handles requests - * by dbus clients to register a network interface that wpa_supplicant - * will manage. - */ -DBusMessage * wpas_dbus_global_add_interface(DBusMessage *message, - struct wpa_global *global) -{ - char *ifname = NULL; - char *driver = NULL; - char *driver_param = NULL; - char *confname = NULL; - char *bridge_ifname = NULL; - DBusMessage *reply = NULL; - DBusMessageIter iter; - - dbus_message_iter_init(message, &iter); - - /* First argument: interface name (DBUS_TYPE_STRING) - * Required; must be non-zero length - */ - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) - goto error; - dbus_message_iter_get_basic(&iter, &ifname); - if (!os_strlen(ifname)) - goto error; - - /* Second argument: dict of options */ - if (dbus_message_iter_next(&iter)) { - DBusMessageIter iter_dict; - struct wpa_dbus_dict_entry entry; - - if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) - goto error; - while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { - if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) - goto error; - if (!strcmp(entry.key, "driver") && - entry.type == DBUS_TYPE_STRING) { - os_free(driver); - driver = os_strdup(entry.str_value); - wpa_dbus_dict_entry_clear(&entry); - if (driver == NULL) - goto error; - } else if (!strcmp(entry.key, "driver-params") && - entry.type == DBUS_TYPE_STRING) { - os_free(driver_param); - driver_param = os_strdup(entry.str_value); - wpa_dbus_dict_entry_clear(&entry); - if (driver_param == NULL) - goto error; - } else if (!strcmp(entry.key, "config-file") && - entry.type == DBUS_TYPE_STRING) { - os_free(confname); - confname = os_strdup(entry.str_value); - wpa_dbus_dict_entry_clear(&entry); - if (confname == NULL) - goto error; - } else if (!strcmp(entry.key, "bridge-ifname") && - entry.type == DBUS_TYPE_STRING) { - os_free(bridge_ifname); - bridge_ifname = os_strdup(entry.str_value); - wpa_dbus_dict_entry_clear(&entry); - if (bridge_ifname == NULL) - goto error; - } else { - wpa_dbus_dict_entry_clear(&entry); - goto error; - } - } - } - - /* - * Try to get the wpa_supplicant record for this iface, return - * an error if we already control it. - */ - if (wpa_supplicant_get_iface(global, ifname) != NULL) { - reply = dbus_message_new_error( - message, WPAS_ERROR_EXISTS_ERROR, - "wpa_supplicant already controls this interface."); - } else { - struct wpa_supplicant *wpa_s; - struct wpa_interface iface; - - os_memset(&iface, 0, sizeof(iface)); - iface.ifname = ifname; - iface.driver = driver; - iface.driver_param = driver_param; - iface.confname = confname; - iface.bridge_ifname = bridge_ifname; - /* Otherwise, have wpa_supplicant attach to it. */ - wpa_s = wpa_supplicant_add_iface(global, &iface, NULL); - if (wpa_s && wpa_s->dbus_path) { - const char *path = wpa_s->dbus_path; - - reply = dbus_message_new_method_return(message); - dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, - &path, DBUS_TYPE_INVALID); - } else { - reply = dbus_message_new_error( - message, WPAS_ERROR_ADD_ERROR, - "wpa_supplicant couldn't grab this interface."); - } - } - -out: - os_free(driver); - os_free(driver_param); - os_free(confname); - os_free(bridge_ifname); - return reply; - -error: - reply = wpas_dbus_new_invalid_opts_error(message, NULL); - goto out; -} - - -/** - * wpas_dbus_global_remove_interface - Request deregistration of an interface - * @message: Pointer to incoming dbus message - * @global: wpa_supplicant global data structure - * Returns: a dbus message containing a UINT32 indicating success (1) or - * failure (0), or returns a dbus error message with more information - * - * Handler function for "removeInterface" method call. Handles requests - * by dbus clients to deregister a network interface that wpa_supplicant - * currently manages. - */ -DBusMessage * wpas_dbus_global_remove_interface(DBusMessage *message, - struct wpa_global *global) -{ - struct wpa_supplicant *wpa_s; - char *path; - DBusMessage *reply = NULL; - - if (!dbus_message_get_args(message, NULL, - DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_INVALID)) { - reply = wpas_dbus_new_invalid_opts_error(message, NULL); - goto out; - } - - wpa_s = wpa_supplicant_get_iface_by_dbus_path(global, path); - if (wpa_s == NULL) { - reply = wpas_dbus_new_invalid_iface_error(message); - goto out; - } - - if (!wpa_supplicant_remove_iface(global, wpa_s, 0)) { - reply = wpas_dbus_new_success_reply(message); - } else { - reply = dbus_message_new_error( - message, WPAS_ERROR_REMOVE_ERROR, - "wpa_supplicant couldn't remove this interface."); - } - -out: - return reply; -} - - -/** - * wpas_dbus_global_get_interface - Get the object path for an interface name - * @message: Pointer to incoming dbus message - * @global: %wpa_supplicant global data structure - * Returns: The object path of the interface object, - * or a dbus error message with more information - * - * Handler function for "getInterface" method call. Handles requests - * by dbus clients for the object path of an specific network interface. - */ -DBusMessage * wpas_dbus_global_get_interface(DBusMessage *message, - struct wpa_global *global) -{ - DBusMessage *reply = NULL; - const char *ifname; - const char *path; - struct wpa_supplicant *wpa_s; - - if (!dbus_message_get_args(message, NULL, - DBUS_TYPE_STRING, &ifname, - DBUS_TYPE_INVALID)) { - reply = wpas_dbus_new_invalid_opts_error(message, NULL); - goto out; - } - - wpa_s = wpa_supplicant_get_iface(global, ifname); - if (wpa_s == NULL || !wpa_s->dbus_path) { - reply = wpas_dbus_new_invalid_iface_error(message); - goto out; - } - - path = wpa_s->dbus_path; - reply = dbus_message_new_method_return(message); - dbus_message_append_args(reply, - DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_INVALID); - -out: - return reply; -} - - -/** - * wpas_dbus_global_set_debugparams- Set the debug params - * @message: Pointer to incoming dbus message - * @global: %wpa_supplicant global data structure - * Returns: a dbus message containing a UINT32 indicating success (1) or - * failure (0), or returns a dbus error message with more information - * - * Handler function for "setDebugParams" method call. Handles requests - * by dbus clients for the object path of an specific network interface. - */ -DBusMessage * wpas_dbus_global_set_debugparams(DBusMessage *message, - struct wpa_global *global) -{ - DBusMessage *reply = NULL; - int debug_level; - dbus_bool_t debug_timestamp; - dbus_bool_t debug_show_keys; - - if (!dbus_message_get_args(message, NULL, - DBUS_TYPE_INT32, &debug_level, - DBUS_TYPE_BOOLEAN, &debug_timestamp, - DBUS_TYPE_BOOLEAN, &debug_show_keys, - DBUS_TYPE_INVALID)) { - return wpas_dbus_new_invalid_opts_error(message, NULL); - } - - if (wpa_supplicant_set_debug_params(global, debug_level, - debug_timestamp ? 1 : 0, - debug_show_keys ? 1 : 0)) { - return wpas_dbus_new_invalid_opts_error(message, NULL); - } - - reply = wpas_dbus_new_success_reply(message); - - return reply; -} - - -/** - * wpas_dbus_iface_scan - Request a wireless scan on an interface - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * Returns: a dbus message containing a UINT32 indicating success (1) or - * failure (0) - * - * Handler function for "scan" method call of a network device. Requests - * that wpa_supplicant perform a wireless scan as soon as possible - * on a particular wireless interface. - */ -DBusMessage * wpas_dbus_iface_scan(DBusMessage *message, - struct wpa_supplicant *wpa_s) -{ - wpa_s->scan_req = MANUAL_SCAN_REQ; - wpa_supplicant_req_scan(wpa_s, 0, 0); - return wpas_dbus_new_success_reply(message); -} - - -/** - * wpas_dbus_iface_scan_results - Get the results of a recent scan request - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * Returns: a dbus message containing a dbus array of objects paths, or returns - * a dbus error message if not scan results could be found - * - * Handler function for "scanResults" method call of a network device. Returns - * a dbus message containing the object paths of wireless networks found. - */ -DBusMessage * wpas_dbus_iface_scan_results(DBusMessage *message, - struct wpa_supplicant *wpa_s) -{ - DBusMessage *reply; - DBusMessageIter iter; - DBusMessageIter sub_iter; - struct wpa_bss *bss; - - if (!wpa_s->dbus_path) - return dbus_message_new_error(message, - WPAS_ERROR_INTERNAL_ERROR, - "no D-Bus interface available"); - - /* Create and initialize the return message */ - reply = dbus_message_new_method_return(message); - dbus_message_iter_init_append(reply, &iter); - if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, - DBUS_TYPE_OBJECT_PATH_AS_STRING, - &sub_iter)) - goto error; - - /* Loop through scan results and append each result's object path */ - dl_list_for_each(bss, &wpa_s->bss_id, struct wpa_bss, list_id) { - char path_buf[WPAS_DBUS_OBJECT_PATH_MAX]; - char *path = path_buf; - - /* Construct the object path for this network. Note that ':' - * is not a valid character in dbus object paths. - */ - os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX, - "%s/" WPAS_DBUS_BSSIDS_PART "/" - WPAS_DBUS_BSSID_FORMAT, - wpa_s->dbus_path, MAC2STR(bss->bssid)); - if (!dbus_message_iter_append_basic(&sub_iter, - DBUS_TYPE_OBJECT_PATH, - &path)) - goto error; - } - - if (!dbus_message_iter_close_container(&iter, &sub_iter)) - goto error; - - return reply; - -error: - dbus_message_unref(reply); - return dbus_message_new_error(message, WPAS_ERROR_INTERNAL_ERROR, - "an internal error occurred returning scan results"); -} - - -/** - * wpas_dbus_bssid_properties - Return the properties of a scanned network - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * @res: wpa_supplicant scan result for which to get properties - * Returns: a dbus message containing the properties for the requested network - * - * Handler function for "properties" method call of a scanned network. - * Returns a dbus message containing the the properties. - */ -DBusMessage * wpas_dbus_bssid_properties(DBusMessage *message, - struct wpa_supplicant *wpa_s, - struct wpa_bss *bss) -{ - DBusMessage *reply; - DBusMessageIter iter, iter_dict; - const u8 *wpa_ie, *rsn_ie, *wps_ie; - - /* Dump the properties into a dbus message */ - reply = dbus_message_new_method_return(message); - - wpa_ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); - rsn_ie = wpa_bss_get_ie(bss, WLAN_EID_RSN); - wps_ie = wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE); - - dbus_message_iter_init_append(reply, &iter); - if (!wpa_dbus_dict_open_write(&iter, &iter_dict) || - !wpa_dbus_dict_append_byte_array(&iter_dict, "bssid", - (const char *) bss->bssid, - ETH_ALEN) || - !wpa_dbus_dict_append_byte_array(&iter_dict, "ssid", - (const char *) bss->ssid, - bss->ssid_len) || - (wpa_ie && - !wpa_dbus_dict_append_byte_array(&iter_dict, "wpaie", - (const char *) wpa_ie, - wpa_ie[1] + 2)) || - (rsn_ie && - !wpa_dbus_dict_append_byte_array(&iter_dict, "rsnie", - (const char *) rsn_ie, - rsn_ie[1] + 2)) || - (wps_ie && - !wpa_dbus_dict_append_byte_array(&iter_dict, "wpsie", - (const char *) wps_ie, - wps_ie[1] + 2)) || - (bss->freq && - !wpa_dbus_dict_append_int32(&iter_dict, "frequency", bss->freq)) || - !wpa_dbus_dict_append_uint16(&iter_dict, "capabilities", - bss->caps) || - (!(bss->flags & WPA_BSS_QUAL_INVALID) && - !wpa_dbus_dict_append_int32(&iter_dict, "quality", bss->qual)) || - (!(bss->flags & WPA_BSS_NOISE_INVALID) && - !wpa_dbus_dict_append_int32(&iter_dict, "noise", bss->noise)) || - (!(bss->flags & WPA_BSS_LEVEL_INVALID) && - !wpa_dbus_dict_append_int32(&iter_dict, "level", bss->level)) || - !wpa_dbus_dict_append_int32(&iter_dict, "maxrate", - wpa_bss_get_max_rate(bss) * 500000) || - !wpa_dbus_dict_close_write(&iter, &iter_dict)) { - if (reply) - dbus_message_unref(reply); - reply = dbus_message_new_error( - message, WPAS_ERROR_INTERNAL_ERROR, - "an internal error occurred returning BSSID properties."); - } - - return reply; -} - - -/** - * wpas_dbus_iface_capabilities - Return interface capabilities - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * Returns: A dbus message containing a dict of strings - * - * Handler function for "capabilities" method call of an interface. - */ -DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message, - struct wpa_supplicant *wpa_s) -{ - DBusMessage *reply = NULL; - struct wpa_driver_capa capa; - int res; - DBusMessageIter iter, iter_dict; - char **eap_methods; - size_t num_items; - dbus_bool_t strict = FALSE; - DBusMessageIter iter_dict_entry, iter_dict_val, iter_array; - - if (!dbus_message_get_args(message, NULL, - DBUS_TYPE_BOOLEAN, &strict, - DBUS_TYPE_INVALID)) - strict = FALSE; - - reply = dbus_message_new_method_return(message); - - dbus_message_iter_init_append(reply, &iter); - if (!wpa_dbus_dict_open_write(&iter, &iter_dict)) - goto error; - - /* EAP methods */ - eap_methods = eap_get_names_as_string_array(&num_items); - if (eap_methods) { - dbus_bool_t success; - size_t i = 0; - - success = wpa_dbus_dict_append_string_array( - &iter_dict, "eap", (const char **) eap_methods, - num_items); - - /* free returned method array */ - while (eap_methods[i]) - os_free(eap_methods[i++]); - os_free(eap_methods); - - if (!success) - goto error; - } - - res = wpa_drv_get_capa(wpa_s, &capa); - - /***** pairwise cipher */ - if (res < 0) { - if (!strict) { - const char *args[] = {"CCMP", "TKIP", "NONE"}; - - if (!wpa_dbus_dict_append_string_array( - &iter_dict, "pairwise", args, - ARRAY_SIZE(args))) - goto error; - } - } else { - if (!wpa_dbus_dict_begin_string_array(&iter_dict, "pairwise", - &iter_dict_entry, - &iter_dict_val, - &iter_array) || - ((capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) && - !wpa_dbus_dict_string_array_add_element( - &iter_array, "CCMP")) || - ((capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) && - !wpa_dbus_dict_string_array_add_element( - &iter_array, "TKIP")) || - ((capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) && - !wpa_dbus_dict_string_array_add_element( - &iter_array, "NONE")) || - !wpa_dbus_dict_end_string_array(&iter_dict, - &iter_dict_entry, - &iter_dict_val, - &iter_array)) - goto error; - } - - /***** group cipher */ - if (res < 0) { - if (!strict) { - const char *args[] = { - "CCMP", "TKIP", "WEP104", "WEP40" - }; - - if (!wpa_dbus_dict_append_string_array( - &iter_dict, "group", args, - ARRAY_SIZE(args))) - goto error; - } - } else { - if (!wpa_dbus_dict_begin_string_array(&iter_dict, "group", - &iter_dict_entry, - &iter_dict_val, - &iter_array)) - goto error; - - if (((capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) && - !wpa_dbus_dict_string_array_add_element( - &iter_array, "CCMP")) || - ((capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) && - !wpa_dbus_dict_string_array_add_element( - &iter_array, "TKIP")) || - ((capa.enc & WPA_DRIVER_CAPA_ENC_WEP104) && - !wpa_dbus_dict_string_array_add_element( - &iter_array, "WEP104")) || - ((capa.enc & WPA_DRIVER_CAPA_ENC_WEP40) && - !wpa_dbus_dict_string_array_add_element( - &iter_array, "WEP40")) || - !wpa_dbus_dict_end_string_array(&iter_dict, - &iter_dict_entry, - &iter_dict_val, - &iter_array)) - goto error; - } - - /***** key management */ - if (res < 0) { - if (!strict) { - const char *args[] = { - "WPA-PSK", "WPA-EAP", "IEEE8021X", "WPA-NONE", - "NONE" - }; - if (!wpa_dbus_dict_append_string_array( - &iter_dict, "key_mgmt", args, - ARRAY_SIZE(args))) - goto error; - } - } else { - if (!wpa_dbus_dict_begin_string_array(&iter_dict, "key_mgmt", - &iter_dict_entry, - &iter_dict_val, - &iter_array) || - !wpa_dbus_dict_string_array_add_element(&iter_array, - "NONE") || - !wpa_dbus_dict_string_array_add_element(&iter_array, - "IEEE8021X") || - ((capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | - WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) && - !wpa_dbus_dict_string_array_add_element( - &iter_array, "WPA-EAP")) || - ((capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | - WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) && - !wpa_dbus_dict_string_array_add_element( - &iter_array, "WPA-PSK")) || - ((capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) && - !wpa_dbus_dict_string_array_add_element( - &iter_array, "WPA-NONE")) || - !wpa_dbus_dict_end_string_array(&iter_dict, - &iter_dict_entry, - &iter_dict_val, - &iter_array)) - goto error; - } - - /***** WPA protocol */ - if (res < 0) { - if (!strict) { - const char *args[] = { "RSN", "WPA" }; - - if (!wpa_dbus_dict_append_string_array( - &iter_dict, "proto", args, - ARRAY_SIZE(args))) - goto error; - } - } else { - if (!wpa_dbus_dict_begin_string_array(&iter_dict, "proto", - &iter_dict_entry, - &iter_dict_val, - &iter_array) || - ((capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | - WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) && - !wpa_dbus_dict_string_array_add_element( - &iter_array, "RSN")) || - ((capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | - WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) && - !wpa_dbus_dict_string_array_add_element( - &iter_array, "WPA")) || - !wpa_dbus_dict_end_string_array(&iter_dict, - &iter_dict_entry, - &iter_dict_val, - &iter_array)) - goto error; - } - - /***** auth alg */ - if (res < 0) { - if (!strict) { - const char *args[] = { "OPEN", "SHARED", "LEAP" }; - - if (!wpa_dbus_dict_append_string_array( - &iter_dict, "auth_alg", args, - ARRAY_SIZE(args))) - goto error; - } - } else { - if (!wpa_dbus_dict_begin_string_array(&iter_dict, "auth_alg", - &iter_dict_entry, - &iter_dict_val, - &iter_array) || - ((capa.auth & WPA_DRIVER_AUTH_OPEN) && - !wpa_dbus_dict_string_array_add_element( - &iter_array, "OPEN")) || - ((capa.auth & WPA_DRIVER_AUTH_SHARED) && - !wpa_dbus_dict_string_array_add_element( - &iter_array, "SHARED")) || - ((capa.auth & WPA_DRIVER_AUTH_LEAP) && - !wpa_dbus_dict_string_array_add_element( - &iter_array, "LEAP")) || - !wpa_dbus_dict_end_string_array(&iter_dict, - &iter_dict_entry, - &iter_dict_val, - &iter_array)) - goto error; - } - - if (!wpa_dbus_dict_close_write(&iter, &iter_dict)) - goto error; - - return reply; - -error: - if (reply) - dbus_message_unref(reply); - return dbus_message_new_error( - message, WPAS_ERROR_INTERNAL_ERROR, - "an internal error occurred returning interface capabilities."); -} - - -/** - * wpas_dbus_iface_add_network - Add a new configured network - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * Returns: A dbus message containing the object path of the new network - * - * Handler function for "addNetwork" method call of a network interface. - */ -DBusMessage * wpas_dbus_iface_add_network(DBusMessage *message, - struct wpa_supplicant *wpa_s) -{ - DBusMessage *reply = NULL; - struct wpa_ssid *ssid = NULL; - char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *path = path_buf; - - if (wpa_s->dbus_path) - ssid = wpa_supplicant_add_network(wpa_s); - if (ssid == NULL) { - reply = dbus_message_new_error( - message, WPAS_ERROR_ADD_NETWORK_ERROR, - "wpa_supplicant could not add a network on this interface."); - goto out; - } - - /* Construct the object path for this network. */ - os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX, - "%s/" WPAS_DBUS_NETWORKS_PART "/%d", - wpa_s->dbus_path, ssid->id); - - reply = dbus_message_new_method_return(message); - dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, - &path, DBUS_TYPE_INVALID); - -out: - return reply; -} - - -/** - * wpas_dbus_iface_remove_network - Remove a configured network - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * Returns: A dbus message containing a UINT32 indicating success (1) or - * failure (0) - * - * Handler function for "removeNetwork" method call of a network interface. - */ -DBusMessage * wpas_dbus_iface_remove_network(DBusMessage *message, - struct wpa_supplicant *wpa_s) -{ - DBusMessage *reply = NULL; - const char *op; - char *iface = NULL, *net_id = NULL; - int id; - int result; - - if (!dbus_message_get_args(message, NULL, - DBUS_TYPE_OBJECT_PATH, &op, - DBUS_TYPE_INVALID)) { - reply = wpas_dbus_new_invalid_opts_error(message, NULL); - goto out; - } - - /* Extract the network ID */ - iface = wpas_dbus_decompose_object_path(op, &net_id, NULL); - if (iface == NULL || net_id == NULL) { - reply = wpas_dbus_new_invalid_network_error(message); - goto out; - } - - /* Ensure the network is actually a child of this interface */ - if (!wpa_s->dbus_path || os_strcmp(iface, wpa_s->dbus_path) != 0) { - reply = wpas_dbus_new_invalid_network_error(message); - goto out; - } - - id = strtoul(net_id, NULL, 10); - result = wpa_supplicant_remove_network(wpa_s, id); - if (result == -1) { - reply = wpas_dbus_new_invalid_network_error(message); - goto out; - } - if (result == -2) { - reply = dbus_message_new_error( - message, WPAS_ERROR_REMOVE_NETWORK_ERROR, - "error removing the specified on this interface."); - goto out; - } - - reply = wpas_dbus_new_success_reply(message); - -out: - os_free(iface); - os_free(net_id); - return reply; -} - - -static const char * const dont_quote[] = { - "key_mgmt", "proto", "pairwise", "auth_alg", "group", "eap", - "opensc_engine_path", "pkcs11_engine_path", "pkcs11_module_path", - "bssid", "scan_freq", "freq_list", NULL -}; - - -static dbus_bool_t should_quote_opt(const char *key) -{ - int i = 0; - - while (dont_quote[i] != NULL) { - if (os_strcmp(key, dont_quote[i]) == 0) - return FALSE; - i++; - } - return TRUE; -} - - -/** - * wpas_dbus_iface_set_network - Set options for a configured network - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * @ssid: wpa_ssid structure for a configured network - * Returns: a dbus message containing a UINT32 indicating success (1) or - * failure (0) - * - * Handler function for "set" method call of a configured network. - */ -DBusMessage * wpas_dbus_iface_set_network(DBusMessage *message, - struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid) -{ - DBusMessage *reply = NULL; - struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING }; - DBusMessageIter iter, iter_dict; - - dbus_message_iter_init(message, &iter); - - if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) { - reply = wpas_dbus_new_invalid_opts_error(message, NULL); - goto out; - } - - while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { - char *value = NULL; - size_t size = 50; - int ret; - - if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) { - reply = wpas_dbus_new_invalid_opts_error(message, - NULL); - goto out; - } - - /* Type conversions, since wpa_supplicant wants strings */ - if (entry.type == DBUS_TYPE_ARRAY && - entry.array_type == DBUS_TYPE_BYTE) { - if (entry.array_len <= 0) - goto error; - - size = entry.array_len * 2 + 1; - value = os_zalloc(size); - if (value == NULL) - goto error; - ret = wpa_snprintf_hex(value, size, - (u8 *) entry.bytearray_value, - entry.array_len); - if (ret <= 0) - goto error; - } else if (entry.type == DBUS_TYPE_STRING) { - if (should_quote_opt(entry.key)) { - size = os_strlen(entry.str_value); - /* Zero-length option check */ - if (size == 0) - goto error; - size += 3; /* For quotes and terminator */ - value = os_zalloc(size); - if (value == NULL) - goto error; - ret = os_snprintf(value, size, "\"%s\"", - entry.str_value); - if (os_snprintf_error(size, ret)) - goto error; - } else { - value = os_strdup(entry.str_value); - if (value == NULL) - goto error; - } - } else if (entry.type == DBUS_TYPE_UINT32) { - value = os_zalloc(size); - if (value == NULL) - goto error; - ret = os_snprintf(value, size, "%u", - entry.uint32_value); - if (os_snprintf_error(size, ret)) - goto error; - } else if (entry.type == DBUS_TYPE_INT32) { - value = os_zalloc(size); - if (value == NULL) - goto error; - ret = os_snprintf(value, size, "%d", - entry.int32_value); - if (os_snprintf_error(size, ret)) - goto error; - } else - goto error; - - if (wpa_config_set(ssid, entry.key, value, 0) < 0) - goto error; - - if ((os_strcmp(entry.key, "psk") == 0 && - value[0] == '"' && ssid->ssid_len) || - (os_strcmp(entry.key, "ssid") == 0 && ssid->passphrase)) - wpa_config_update_psk(ssid); - else if (os_strcmp(entry.key, "priority") == 0) - wpa_config_update_prio_list(wpa_s->conf); - - os_free(value); - wpa_dbus_dict_entry_clear(&entry); - continue; - - error: - os_free(value); - reply = wpas_dbus_new_invalid_opts_error(message, entry.key); - wpa_dbus_dict_entry_clear(&entry); - break; - } - - if (!reply) - reply = wpas_dbus_new_success_reply(message); - -out: - return reply; -} - - -/** - * wpas_dbus_iface_enable_network - Mark a configured network as enabled - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * @ssid: wpa_ssid structure for a configured network - * Returns: A dbus message containing a UINT32 indicating success (1) or - * failure (0) - * - * Handler function for "enable" method call of a configured network. - */ -DBusMessage * wpas_dbus_iface_enable_network(DBusMessage *message, - struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid) -{ - wpa_supplicant_enable_network(wpa_s, ssid); - return wpas_dbus_new_success_reply(message); -} - - -/** - * wpas_dbus_iface_disable_network - Mark a configured network as disabled - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * @ssid: wpa_ssid structure for a configured network - * Returns: A dbus message containing a UINT32 indicating success (1) or - * failure (0) - * - * Handler function for "disable" method call of a configured network. - */ -DBusMessage * wpas_dbus_iface_disable_network(DBusMessage *message, - struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid) -{ - wpa_supplicant_disable_network(wpa_s, ssid); - return wpas_dbus_new_success_reply(message); -} - - -/** - * wpas_dbus_iface_select_network - Attempt association with a configured network - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * Returns: A dbus message containing a UINT32 indicating success (1) or - * failure (0) - * - * Handler function for "selectNetwork" method call of network interface. - */ -DBusMessage * wpas_dbus_iface_select_network(DBusMessage *message, - struct wpa_supplicant *wpa_s) -{ - DBusMessage *reply = NULL; - const char *op; - struct wpa_ssid *ssid; - char *iface_obj_path = NULL; - char *network = NULL; - - if (os_strlen(dbus_message_get_signature(message)) == 0) { - /* Any network */ - ssid = NULL; - } else { - int nid; - - if (!dbus_message_get_args(message, NULL, - DBUS_TYPE_OBJECT_PATH, &op, - DBUS_TYPE_INVALID)) { - reply = wpas_dbus_new_invalid_opts_error(message, - NULL); - goto out; - } - - /* Extract the network number */ - iface_obj_path = wpas_dbus_decompose_object_path(op, - &network, - NULL); - if (iface_obj_path == NULL) { - reply = wpas_dbus_new_invalid_iface_error(message); - goto out; - } - /* Ensure the object path really points to this interface */ - if (network == NULL || !wpa_s->dbus_path || - os_strcmp(iface_obj_path, wpa_s->dbus_path) != 0) { - reply = wpas_dbus_new_invalid_network_error(message); - goto out; - } - - nid = strtoul(network, NULL, 10); - if (errno == EINVAL) { - reply = wpas_dbus_new_invalid_network_error(message); - goto out; - } - - ssid = wpa_config_get_network(wpa_s->conf, nid); - if (ssid == NULL) { - reply = wpas_dbus_new_invalid_network_error(message); - goto out; - } - } - - /* Finally, associate with the network */ - wpa_supplicant_select_network(wpa_s, ssid); - - reply = wpas_dbus_new_success_reply(message); - -out: - os_free(iface_obj_path); - os_free(network); - return reply; -} - - -/** - * wpas_dbus_iface_disconnect - Terminate the current connection - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * Returns: A dbus message containing a UINT32 indicating success (1) or - * failure (0) - * - * Handler function for "disconnect" method call of network interface. - */ -DBusMessage * wpas_dbus_iface_disconnect(DBusMessage *message, - struct wpa_supplicant *wpa_s) -{ - wpas_request_disconnection(wpa_s); - - return wpas_dbus_new_success_reply(message); -} - - -/** - * wpas_dbus_iface_set_ap_scan - Control roaming mode - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * Returns: A dbus message containing a UINT32 indicating success (1) or - * failure (0) - * - * Handler function for "setAPScan" method call. - */ -DBusMessage * wpas_dbus_iface_set_ap_scan(DBusMessage *message, - struct wpa_supplicant *wpa_s) -{ - DBusMessage *reply = NULL; - dbus_uint32_t ap_scan = 1; - - if (!dbus_message_get_args(message, NULL, DBUS_TYPE_UINT32, &ap_scan, - DBUS_TYPE_INVALID)) { - reply = wpas_dbus_new_invalid_opts_error(message, NULL); - goto out; - } - - if (wpa_supplicant_set_ap_scan(wpa_s, ap_scan)) { - reply = wpas_dbus_new_invalid_opts_error(message, NULL); - goto out; - } - - reply = wpas_dbus_new_success_reply(message); - -out: - return reply; -} - - -/** - * wpas_dbus_iface_set_smartcard_modules - Set smartcard related module paths - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * Returns: A dbus message containing a UINT32 indicating success (1) or - * failure (0) - * - * Handler function for "setSmartcardModules" method call. - */ -DBusMessage * wpas_dbus_iface_set_smartcard_modules( - DBusMessage *message, struct wpa_supplicant *wpa_s) -{ - DBusMessageIter iter, iter_dict; - char *opensc_engine_path = NULL; - char *pkcs11_engine_path = NULL; - char *pkcs11_module_path = NULL; - struct wpa_dbus_dict_entry entry; - - if (!dbus_message_iter_init(message, &iter)) - goto error; - - if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) - goto error; - - while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { - if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) - goto error; - if (!strcmp(entry.key, "opensc_engine_path") && - entry.type == DBUS_TYPE_STRING) { - os_free(opensc_engine_path); - opensc_engine_path = os_strdup(entry.str_value); - wpa_dbus_dict_entry_clear(&entry); - if (opensc_engine_path == NULL) - goto error; - } else if (!strcmp(entry.key, "pkcs11_engine_path") && - entry.type == DBUS_TYPE_STRING) { - os_free(pkcs11_engine_path); - pkcs11_engine_path = os_strdup(entry.str_value); - wpa_dbus_dict_entry_clear(&entry); - if (pkcs11_engine_path == NULL) - goto error; - } else if (!strcmp(entry.key, "pkcs11_module_path") && - entry.type == DBUS_TYPE_STRING) { - os_free(pkcs11_module_path); - pkcs11_module_path = os_strdup(entry.str_value); - wpa_dbus_dict_entry_clear(&entry); - if (pkcs11_module_path == NULL) - goto error; - } else { - wpa_dbus_dict_entry_clear(&entry); - goto error; - } - } - - os_free(wpa_s->conf->opensc_engine_path); - wpa_s->conf->opensc_engine_path = opensc_engine_path; - os_free(wpa_s->conf->pkcs11_engine_path); - wpa_s->conf->pkcs11_engine_path = pkcs11_engine_path; - os_free(wpa_s->conf->pkcs11_module_path); - wpa_s->conf->pkcs11_module_path = pkcs11_module_path; - - wpa_sm_set_eapol(wpa_s->wpa, NULL); - eapol_sm_deinit(wpa_s->eapol); - wpa_s->eapol = NULL; - wpa_supplicant_init_eapol(wpa_s); - wpa_sm_set_eapol(wpa_s->wpa, wpa_s->eapol); - - return wpas_dbus_new_success_reply(message); - -error: - os_free(opensc_engine_path); - os_free(pkcs11_engine_path); - os_free(pkcs11_module_path); - return wpas_dbus_new_invalid_opts_error(message, NULL); -} - - -/** - * wpas_dbus_iface_get_state - Get interface state - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * Returns: A dbus message containing a STRING representing the current - * interface state - * - * Handler function for "state" method call. - */ -DBusMessage * wpas_dbus_iface_get_state(DBusMessage *message, - struct wpa_supplicant *wpa_s) -{ - DBusMessage *reply = NULL; - const char *str_state; - - reply = dbus_message_new_method_return(message); - if (reply != NULL) { - str_state = wpa_supplicant_state_txt(wpa_s->wpa_state); - dbus_message_append_args(reply, DBUS_TYPE_STRING, &str_state, - DBUS_TYPE_INVALID); - } - - return reply; -} - - -/** - * wpas_dbus_iface_get_scanning - Get interface scanning state - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * Returns: A dbus message containing whether the interface is scanning - * - * Handler function for "scanning" method call. - */ -DBusMessage * wpas_dbus_iface_get_scanning(DBusMessage *message, - struct wpa_supplicant *wpa_s) -{ - DBusMessage *reply = NULL; - dbus_bool_t scanning = wpa_s->scanning ? TRUE : FALSE; - - reply = dbus_message_new_method_return(message); - if (reply != NULL) { - dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &scanning, - DBUS_TYPE_INVALID); - } else { - wpa_printf(MSG_ERROR, - "dbus: Not enough memory to return scanning state"); - } - - return reply; -} - - -#ifndef CONFIG_NO_CONFIG_BLOBS - -/** - * wpas_dbus_iface_set_blobs - Store named binary blobs (ie, for certificates) - * @message: Pointer to incoming dbus message - * @wpa_s: %wpa_supplicant data structure - * Returns: A dbus message containing a UINT32 indicating success (1) or - * failure (0) - * - * Asks wpa_supplicant to internally store a one or more binary blobs. - */ -DBusMessage * wpas_dbus_iface_set_blobs(DBusMessage *message, - struct wpa_supplicant *wpa_s) -{ - DBusMessage *reply = NULL; - struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING }; - DBusMessageIter iter, iter_dict; - - dbus_message_iter_init(message, &iter); - - if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) - return wpas_dbus_new_invalid_opts_error(message, NULL); - - while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { - struct wpa_config_blob *blob; - - if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) { - reply = wpas_dbus_new_invalid_opts_error(message, - NULL); - break; - } - - if (entry.type != DBUS_TYPE_ARRAY || - entry.array_type != DBUS_TYPE_BYTE) { - reply = wpas_dbus_new_invalid_opts_error( - message, "Byte array expected."); - break; - } - - if ((entry.array_len <= 0) || (entry.array_len > 65536) || - !strlen(entry.key)) { - reply = wpas_dbus_new_invalid_opts_error( - message, "Invalid array size."); - break; - } - - blob = os_zalloc(sizeof(*blob)); - if (blob == NULL) { - reply = dbus_message_new_error( - message, WPAS_ERROR_ADD_ERROR, - "Not enough memory to add blob."); - break; - } - blob->data = os_zalloc(entry.array_len); - if (blob->data == NULL) { - reply = dbus_message_new_error( - message, WPAS_ERROR_ADD_ERROR, - "Not enough memory to add blob data."); - os_free(blob); - break; - } - - blob->name = os_strdup(entry.key); - blob->len = entry.array_len; - os_memcpy(blob->data, (u8 *) entry.bytearray_value, - entry.array_len); - if (blob->name == NULL) { - wpa_config_free_blob(blob); - reply = dbus_message_new_error( - message, WPAS_ERROR_ADD_ERROR, - "Error adding blob."); - break; - } - - /* Success */ - if (!wpa_config_remove_blob(wpa_s->conf, blob->name)) - wpas_notify_blob_removed(wpa_s, blob->name); - wpa_config_set_blob(wpa_s->conf, blob); - wpas_notify_blob_added(wpa_s, blob->name); - - wpa_dbus_dict_entry_clear(&entry); - } - wpa_dbus_dict_entry_clear(&entry); - - return reply ? reply : wpas_dbus_new_success_reply(message); -} - - -/** - * wpas_dbus_iface_remove_blob - Remove named binary blobs - * @message: Pointer to incoming dbus message - * @wpa_s: %wpa_supplicant data structure - * Returns: A dbus message containing a UINT32 indicating success (1) or - * failure (0) - * - * Asks wpa_supplicant to remove one or more previously stored binary blobs. - */ -DBusMessage * wpas_dbus_iface_remove_blobs(DBusMessage *message, - struct wpa_supplicant *wpa_s) -{ - DBusMessageIter iter, array; - char *err_msg = NULL; - - dbus_message_iter_init(message, &iter); - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || - dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRING) - return wpas_dbus_new_invalid_opts_error(message, NULL); - - dbus_message_iter_recurse(&iter, &array); - while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) { - const char *name; - - dbus_message_iter_get_basic(&array, &name); - if (!os_strlen(name)) - err_msg = "Invalid blob name."; - else if (wpa_config_remove_blob(wpa_s->conf, name) != 0) - err_msg = "Error removing blob."; - else - wpas_notify_blob_removed(wpa_s, name); - dbus_message_iter_next(&array); - } - - if (err_msg) - return dbus_message_new_error(message, WPAS_ERROR_REMOVE_ERROR, - err_msg); - - return wpas_dbus_new_success_reply(message); -} - -#endif /* CONFIG_NO_CONFIG_BLOBS */ - - -/** - * wpas_dbus_iface_flush - Clear BSS of old or all inactive entries - * @message: Pointer to incoming dbus message - * @wpa_s: %wpa_supplicant data structure - * Returns: a dbus message containing a UINT32 indicating success (1) or - * failure (0), or returns a dbus error message with more information - * - * Handler function for "flush" method call. Handles requests for an - * interface with an optional "age" parameter that specifies the minimum - * age of a BSS to be flushed. - */ -DBusMessage * wpas_dbus_iface_flush(DBusMessage *message, - struct wpa_supplicant *wpa_s) -{ - int flush_age = 0; - - if (os_strlen(dbus_message_get_signature(message)) != 0 && - !dbus_message_get_args(message, NULL, - DBUS_TYPE_INT32, &flush_age, - DBUS_TYPE_INVALID)) { - return wpas_dbus_new_invalid_opts_error(message, NULL); - } - - if (flush_age == 0) - wpa_bss_flush(wpa_s); - else - wpa_bss_flush_by_age(wpa_s, flush_age); - - return wpas_dbus_new_success_reply(message); -} diff --git a/wpa_supplicant/dbus/dbus_old_handlers.h b/wpa_supplicant/dbus/dbus_old_handlers.h deleted file mode 100644 index e60ad06..0000000 --- a/wpa_supplicant/dbus/dbus_old_handlers.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * WPA Supplicant / dbus-based control interface - * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc. - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -#ifndef CTRL_IFACE_DBUS_HANDLERS_H -#define CTRL_IFACE_DBUS_HANDLERS_H - -struct wpa_bss; - -DBusMessage * wpas_dbus_new_invalid_iface_error(DBusMessage *message); -DBusMessage * wpas_dbus_new_invalid_network_error(DBusMessage *message); - -DBusMessage * wpas_dbus_global_add_interface(DBusMessage *message, - struct wpa_global *global); - -DBusMessage * wpas_dbus_global_remove_interface(DBusMessage *message, - struct wpa_global *global); - -DBusMessage * wpas_dbus_global_get_interface(DBusMessage *message, - struct wpa_global *global); - -DBusMessage * wpas_dbus_global_set_debugparams(DBusMessage *message, - struct wpa_global *global); - -DBusMessage * wpas_dbus_iface_scan(DBusMessage *message, - struct wpa_supplicant *wpa_s); - -DBusMessage * wpas_dbus_iface_scan_results(DBusMessage *message, - struct wpa_supplicant *wpa_s); - -DBusMessage * wpas_dbus_bssid_properties(DBusMessage *message, - struct wpa_supplicant *wpa_s, - struct wpa_bss *bss); - -DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message, - struct wpa_supplicant *wpa_s); - -DBusMessage * wpas_dbus_iface_add_network(DBusMessage *message, - struct wpa_supplicant *wpa_s); - -DBusMessage * wpas_dbus_iface_remove_network(DBusMessage *message, - struct wpa_supplicant *wpa_s); - -DBusMessage * wpas_dbus_iface_set_network(DBusMessage *message, - struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid); - -DBusMessage * wpas_dbus_iface_enable_network(DBusMessage *message, - struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid); - -DBusMessage * wpas_dbus_iface_disable_network(DBusMessage *message, - struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid); - -DBusMessage * wpas_dbus_iface_select_network(DBusMessage *message, - struct wpa_supplicant *wpa_s); - -DBusMessage * wpas_dbus_iface_disconnect(DBusMessage *message, - struct wpa_supplicant *wpa_s); - -DBusMessage * wpas_dbus_iface_set_ap_scan(DBusMessage *message, - struct wpa_supplicant *wpa_s); - -DBusMessage * wpas_dbus_iface_set_smartcard_modules( - DBusMessage *message, struct wpa_supplicant *wpa_s); - -DBusMessage * wpas_dbus_iface_get_state(DBusMessage *message, - struct wpa_supplicant *wpa_s); - -DBusMessage * wpas_dbus_iface_get_scanning(DBusMessage *message, - struct wpa_supplicant *wpa_s); - -DBusMessage * wpas_dbus_iface_set_blobs(DBusMessage *message, - struct wpa_supplicant *wpa_s); - -DBusMessage * wpas_dbus_iface_remove_blobs(DBusMessage *message, - struct wpa_supplicant *wpa_s); - -DBusMessage * wpas_dbus_iface_wps_pbc(DBusMessage *message, - struct wpa_supplicant *wpa_s); - -DBusMessage * wpas_dbus_iface_wps_pin(DBusMessage *message, - struct wpa_supplicant *wpa_s); - -DBusMessage * wpas_dbus_iface_wps_reg(DBusMessage *message, - struct wpa_supplicant *wpa_s); - -DBusMessage * wpas_dbus_iface_flush(DBusMessage *message, - struct wpa_supplicant *wpa_s); - -DBusMessage * wpas_dbus_new_success_reply(DBusMessage *message); -DBusMessage * wpas_dbus_new_invalid_opts_error(DBusMessage *message, - const char *arg); - -#endif /* CTRL_IFACE_DBUS_HANDLERS_H */ - diff --git a/wpa_supplicant/dbus/dbus_old_handlers_wps.c b/wpa_supplicant/dbus/dbus_old_handlers_wps.c deleted file mode 100644 index 987e12d..0000000 --- a/wpa_supplicant/dbus/dbus_old_handlers_wps.c +++ /dev/null @@ -1,156 +0,0 @@ -/* - * WPA Supplicant / dbus-based control interface (WPS) - * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc. - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -#include "includes.h" -#include <dbus/dbus.h> - -#include "common.h" -#include "../config.h" -#include "../wpa_supplicant_i.h" -#include "../wps_supplicant.h" -#include "dbus_old.h" -#include "dbus_old_handlers.h" - -/** - * wpas_dbus_iface_wps_pbc - Request credentials using WPS PBC method - * @message: Pointer to incoming dbus message - * @wpa_s: %wpa_supplicant data structure - * Returns: A dbus message containing a UINT32 indicating success (1) or - * failure (0) - * - * Handler function for "wpsPbc" method call - */ -DBusMessage * wpas_dbus_iface_wps_pbc(DBusMessage *message, - struct wpa_supplicant *wpa_s) -{ - char *arg_bssid = NULL; - u8 bssid[ETH_ALEN]; - int ret = 0; - - if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg_bssid, - DBUS_TYPE_INVALID)) - return wpas_dbus_new_invalid_opts_error(message, NULL); - - if (os_strcmp(arg_bssid, "any") == 0) - ret = wpas_wps_start_pbc(wpa_s, NULL, 0); - else if (!hwaddr_aton(arg_bssid, bssid)) - ret = wpas_wps_start_pbc(wpa_s, bssid, 0); - else { - return wpas_dbus_new_invalid_opts_error(message, - "Invalid BSSID"); - } - - if (ret < 0) { - return dbus_message_new_error( - message, WPAS_ERROR_WPS_PBC_ERROR, - "Could not start PBC negotiation"); - } - - return wpas_dbus_new_success_reply(message); -} - - -/** - * wpas_dbus_iface_wps_pin - Establish the PIN number of the enrollee - * @message: Pointer to incoming dbus message - * @wpa_s: %wpa_supplicant data structure - * Returns: A dbus message containing a UINT32 indicating success (1) or - * failure (0) - * - * Handler function for "wpsPin" method call - */ -DBusMessage * wpas_dbus_iface_wps_pin(DBusMessage *message, - struct wpa_supplicant *wpa_s) -{ - DBusMessage *reply = NULL; - char *arg_bssid; - char *pin = NULL; - u8 bssid[ETH_ALEN], *_bssid = NULL; - int ret; - char npin[9]; - - if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg_bssid, - DBUS_TYPE_STRING, &pin, DBUS_TYPE_INVALID)) - return wpas_dbus_new_invalid_opts_error(message, NULL); - - if (os_strcmp(arg_bssid, "any") == 0) - _bssid = NULL; - else if (!hwaddr_aton(arg_bssid, bssid)) - _bssid = bssid; - else { - return wpas_dbus_new_invalid_opts_error(message, - "Invalid BSSID"); - } - - if (os_strlen(pin) > 0) - ret = wpas_wps_start_pin(wpa_s, _bssid, pin, 0, - DEV_PW_DEFAULT); - else - ret = wpas_wps_start_pin(wpa_s, _bssid, NULL, 0, - DEV_PW_DEFAULT); - - if (ret < 0) { - return dbus_message_new_error(message, - WPAS_ERROR_WPS_PIN_ERROR, - "Could not init PIN"); - } - - reply = dbus_message_new_method_return(message); - if (reply == NULL) - return NULL; - - if (ret > 0) { - ret = os_snprintf(npin, sizeof(npin), "%08d", ret); - if (os_snprintf_error(sizeof(npin), ret)) - return wpas_dbus_new_invalid_opts_error(message, - "invalid PIN"); - - pin = npin; - } - dbus_message_append_args(reply, DBUS_TYPE_STRING, &pin, - DBUS_TYPE_INVALID); - return reply; -} - - -/** - * wpas_dbus_iface_wps_reg - Request credentials using the PIN of the AP - * @message: Pointer to incoming dbus message - * @wpa_s: %wpa_supplicant data structure - * Returns: A dbus message containing a UINT32 indicating success (1) or - * failure (0) - * - * Handler function for "wpsReg" method call - */ -DBusMessage * wpas_dbus_iface_wps_reg(DBusMessage *message, - struct wpa_supplicant *wpa_s) -{ - char *arg_bssid; - char *pin = NULL; - u8 bssid[ETH_ALEN]; - int ret = 0; - - if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg_bssid, - DBUS_TYPE_STRING, &pin, DBUS_TYPE_INVALID)) - return wpas_dbus_new_invalid_opts_error(message, NULL); - - if (!hwaddr_aton(arg_bssid, bssid)) - ret = wpas_wps_start_reg(wpa_s, bssid, pin, NULL); - else { - return wpas_dbus_new_invalid_opts_error(message, - "Invalid BSSID"); - } - - if (ret < 0) { - return dbus_message_new_error(message, - WPAS_ERROR_WPS_REG_ERROR, - "Could not request credentials"); - } - - return wpas_dbus_new_success_reply(message); -} diff --git a/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service.in b/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service.in deleted file mode 100644 index a75918f..0000000 --- a/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service.in +++ /dev/null @@ -1,5 +0,0 @@ -[D-BUS Service] -Name=fi.epitest.hostap.WPASupplicant -Exec=@BINDIR@/wpa_supplicant -u -User=root -SystemdService=wpa_supplicant.service diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig index b51675b..88cd790 100644 --- a/wpa_supplicant/defconfig +++ b/wpa_supplicant/defconfig @@ -109,10 +109,7 @@ CONFIG_EAP_PEAP=y CONFIG_EAP_TTLS=y # EAP-FAST -# Note: If OpenSSL is used as the TLS library, OpenSSL 1.0 or newer is needed -# for EAP-FAST support. Older OpenSSL releases would need to be patched, e.g., -# with openssl-0.9.8x-tls-extensions.patch, to add the needed functions. -#CONFIG_EAP_FAST=y +CONFIG_EAP_FAST=y # EAP-GTC CONFIG_EAP_GTC=y @@ -127,10 +124,10 @@ CONFIG_EAP_OTP=y #CONFIG_EAP_PSK=y # EAP-pwd (secure authentication using only a password) -#CONFIG_EAP_PWD=y +CONFIG_EAP_PWD=y # EAP-PAX -#CONFIG_EAP_PAX=y +CONFIG_EAP_PAX=y # LEAP CONFIG_EAP_LEAP=y @@ -146,18 +143,18 @@ CONFIG_EAP_LEAP=y #CONFIG_USIM_SIMULATOR=y # EAP-SAKE -#CONFIG_EAP_SAKE=y +CONFIG_EAP_SAKE=y # EAP-GPSK -#CONFIG_EAP_GPSK=y +CONFIG_EAP_GPSK=y # Include support for optional SHA256 cipher suite in EAP-GPSK -#CONFIG_EAP_GPSK_SHA256=y +CONFIG_EAP_GPSK_SHA256=y # EAP-TNC and related Trusted Network Connect support (experimental) -#CONFIG_EAP_TNC=y +CONFIG_EAP_TNC=y # Wi-Fi Protected Setup (WPS) -#CONFIG_WPS=y +CONFIG_WPS=y # Enable WPS external registrar functionality #CONFIG_WPS_ER=y # Disable credentials for an open network by default when acting as a WPS @@ -167,7 +164,7 @@ CONFIG_EAP_LEAP=y #CONFIG_WPS_NFC=y # EAP-IKEv2 -#CONFIG_EAP_IKEV2=y +CONFIG_EAP_IKEV2=y # EAP-EKE #CONFIG_EAP_EKE=y @@ -235,6 +232,9 @@ CONFIG_CTRL_IFACE=y # wpa_passphrase). This saves about 0.5 kB in code size. #CONFIG_NO_WPA_PASSPHRASE=y +# Simultaneous Authentication of Equals (SAE), WPA3-Personal +CONFIG_SAE=y + # Disable scan result processing (ap_mode=1) to save code size by about 1 kB. # This can be used if ap_scan=1 mode is never enabled. #CONFIG_NO_SCAN_PROCESSING=y @@ -299,7 +299,7 @@ CONFIG_BACKEND=file # IEEE 802.11w (management frame protection), also known as PMF # Driver support is also needed for IEEE 802.11w. -#CONFIG_IEEE80211W=y +CONFIG_IEEE80211W=y # Support Operating Channel Validation #CONFIG_OCV=y @@ -352,16 +352,12 @@ CONFIG_BACKEND=file #CONFIG_NDIS_EVENTS_INTEGRATED=y #PLATFORMSDKLIB="/opt/Program Files/Microsoft Platform SDK/Lib" -# Add support for old DBus control interface -# (fi.epitest.hostap.WPASupplicant) -#CONFIG_CTRL_IFACE_DBUS=y - # Add support for new DBus control interface # (fi.w1.hostap.wpa_supplicant1) -#CONFIG_CTRL_IFACE_DBUS_NEW=y +CONFIG_CTRL_IFACE_DBUS_NEW=y # Add introspection support for new DBus control interface -#CONFIG_CTRL_IFACE_DBUS_INTRO=y +CONFIG_CTRL_IFACE_DBUS_INTRO=y # Add support for loading EAP methods dynamically as shared libraries. # When this option is enabled, each EAP method can be either included @@ -385,13 +381,13 @@ CONFIG_BACKEND=file #CONFIG_DYNAMIC_EAP_METHODS=y # IEEE Std 802.11r-2008 (Fast BSS Transition) for station mode -#CONFIG_IEEE80211R=y +CONFIG_IEEE80211R=y # Add support for writing debug log to a file (/tmp/wpa_supplicant-log-#.txt) -#CONFIG_DEBUG_FILE=y +CONFIG_DEBUG_FILE=y # Send debug messages to syslog instead of stdout -#CONFIG_DEBUG_SYSLOG=y +CONFIG_DEBUG_SYSLOG=y # Set syslog facility for debug messages #CONFIG_DEBUG_SYSLOG_FACILITY=LOG_DAEMON @@ -467,11 +463,11 @@ CONFIG_BACKEND=file #CONFIG_GETRANDOM=y # IEEE 802.11n (High Throughput) support (mainly for AP mode) -#CONFIG_IEEE80211N=y +CONFIG_IEEE80211N=y # IEEE 802.11ac (Very High Throughput) support (mainly for AP mode) # (depends on CONFIG_IEEE80211N) -#CONFIG_IEEE80211AC=y +CONFIG_IEEE80211AC=y # Wireless Network Management (IEEE Std 802.11v-2011) # Note: This is experimental and not complete implementation. @@ -481,10 +477,10 @@ CONFIG_BACKEND=file # This can be used to enable functionality to improve interworking with # external networks (GAS/ANQP to learn more about the networks and network # selection based on available credentials). -#CONFIG_INTERWORKING=y +CONFIG_INTERWORKING=y # Hotspot 2.0 -#CONFIG_HS20=y +CONFIG_HS20=y # Enable interface matching in wpa_supplicant #CONFIG_MATCH_IFACE=y @@ -497,20 +493,20 @@ CONFIG_BACKEND=file # should be noted that this is mainly aimed at simple cases like # WPA2-Personal while more complex configurations like WPA2-Enterprise with an # external RADIUS server can be supported with hostapd. -#CONFIG_AP=y +CONFIG_AP=y # P2P (Wi-Fi Direct) # This can be used to enable P2P support in wpa_supplicant. See README-P2P for # more information on P2P operations. -#CONFIG_P2P=y +CONFIG_P2P=y # Enable TDLS support #CONFIG_TDLS=y -# Wi-Fi Direct -# This can be used to enable Wi-Fi Direct extensions for P2P using an external +# Wi-Fi Display +# This can be used to enable Wi-Fi Display extensions for P2P using an external # program to control the additional information exchanges in the messages. -#CONFIG_WIFI_DISPLAY=y +CONFIG_WIFI_DISPLAY=y # Autoscan # This can be used to enable automatic scan support in wpa_supplicant. @@ -576,7 +572,7 @@ CONFIG_BACKEND=file # Support RSN on IBSS networks # This is needed to be able to use mode=1 network profile with proto=RSN and # key_mgmt=WPA-PSK (i.e., full key management instead of WPA-None). -#CONFIG_IBSS_RSN=y +CONFIG_IBSS_RSN=y # External PMKSA cache control # This can be used to enable control interface commands that allow the current @@ -591,7 +587,7 @@ CONFIG_BACKEND=file # operations for roaming within an ESS (same SSID). See the bgscan parameter in # the wpa_supplicant.conf file for more details. # Periodic background scans based on signal strength -#CONFIG_BGSCAN_SIMPLE=y +CONFIG_BGSCAN_SIMPLE=y # Learn channels used by the network and try to avoid bgscans on other # channels (experimental) #CONFIG_BGSCAN_LEARN=y @@ -599,3 +595,8 @@ CONFIG_BACKEND=file # Opportunistic Wireless Encryption (OWE) # Experimental implementation of draft-harkins-owe-07.txt #CONFIG_OWE=y + +# Device Provisioning Protocol (DPP) +# This requires CONFIG_IEEE80211W=y to be enabled, too. (see +# wpa_supplicant/README-DPP for details) +CONFIG_DPP=y diff --git a/wpa_supplicant/doc/docbook/wpa_supplicant.sgml b/wpa_supplicant/doc/docbook/wpa_supplicant.sgml index ebf102e..aaff150 100644 --- a/wpa_supplicant/doc/docbook/wpa_supplicant.sgml +++ b/wpa_supplicant/doc/docbook/wpa_supplicant.sgml @@ -471,7 +471,7 @@ <para>Enable DBus control interface. If enabled, interface definitions may be omitted. (This is only available if <command>wpa_supplicant</command> was built with - the <literal>CONFIG_DBUS</literal> option.)</para> + the <literal>CONFIG_CTRL_IFACE_DBUS_NEW</literal> option.)</para> </listitem> </varlistentry> diff --git a/wpa_supplicant/dpp_supplicant.c b/wpa_supplicant/dpp_supplicant.c index 7bc4661..e003a85 100644 --- a/wpa_supplicant/dpp_supplicant.c +++ b/wpa_supplicant/dpp_supplicant.c @@ -1,7 +1,7 @@ /* * wpa_supplicant - DPP * Copyright (c) 2017, Qualcomm Atheros, Inc. - * Copyright (c) 2018, The Linux Foundation + * Copyright (c) 2018-2019, The Linux Foundation * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -52,34 +52,6 @@ static const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; static const u8 TRANSACTION_ID = 1; -static struct dpp_configurator * -dpp_configurator_get_id(struct wpa_supplicant *wpa_s, unsigned int id) -{ - struct dpp_configurator *conf; - - dl_list_for_each(conf, &wpa_s->dpp_configurator, - struct dpp_configurator, list) { - if (conf->id == id) - return conf; - } - return NULL; -} - - -static unsigned int wpas_dpp_next_id(struct wpa_supplicant *wpa_s) -{ - struct dpp_bootstrap_info *bi; - unsigned int max_id = 0; - - dl_list_for_each(bi, &wpa_s->dpp_bootstrap, struct dpp_bootstrap_info, - list) { - if (bi->id > max_id) - max_id = bi->id; - } - return max_id + 1; -} - - /** * wpas_dpp_qr_code - Parse and add DPP bootstrapping info from a QR Code * @wpa_s: Pointer to wpa_supplicant data @@ -91,13 +63,10 @@ int wpas_dpp_qr_code(struct wpa_supplicant *wpa_s, const char *cmd) struct dpp_bootstrap_info *bi; struct dpp_authentication *auth = wpa_s->dpp_auth; - bi = dpp_parse_qr_code(cmd); + bi = dpp_add_qr_code(wpa_s->dpp, cmd); if (!bi) return -1; - bi->id = wpas_dpp_next_id(wpa_s); - dl_list_add(&wpa_s->dpp_bootstrap, &bi->list); - if (auth && auth->response_pending && dpp_notify_new_qr_code(auth, bi) == 1) { wpa_printf(MSG_DEBUG, @@ -118,195 +87,6 @@ int wpas_dpp_qr_code(struct wpa_supplicant *wpa_s, const char *cmd) } -static char * get_param(const char *cmd, const char *param) -{ - const char *pos, *end; - char *val; - size_t len; - - pos = os_strstr(cmd, param); - if (!pos) - return NULL; - - pos += os_strlen(param); - end = os_strchr(pos, ' '); - if (end) - len = end - pos; - else - len = os_strlen(pos); - val = os_malloc(len + 1); - if (!val) - return NULL; - os_memcpy(val, pos, len); - val[len] = '\0'; - return val; -} - - -int wpas_dpp_bootstrap_gen(struct wpa_supplicant *wpa_s, const char *cmd) -{ - char *chan = NULL, *mac = NULL, *info = NULL, *pk = NULL, *curve = NULL; - char *key = NULL; - u8 *privkey = NULL; - size_t privkey_len = 0; - size_t len; - int ret = -1; - struct dpp_bootstrap_info *bi; - - bi = os_zalloc(sizeof(*bi)); - if (!bi) - goto fail; - - if (os_strstr(cmd, "type=qrcode")) - bi->type = DPP_BOOTSTRAP_QR_CODE; - else if (os_strstr(cmd, "type=pkex")) - bi->type = DPP_BOOTSTRAP_PKEX; - else - goto fail; - - chan = get_param(cmd, " chan="); - mac = get_param(cmd, " mac="); - info = get_param(cmd, " info="); - curve = get_param(cmd, " curve="); - key = get_param(cmd, " key="); - - if (key) { - privkey_len = os_strlen(key) / 2; - privkey = os_malloc(privkey_len); - if (!privkey || - hexstr2bin(key, privkey, privkey_len) < 0) - goto fail; - } - - pk = dpp_keygen(bi, curve, privkey, privkey_len); - if (!pk) - goto fail; - - len = 4; /* "DPP:" */ - if (chan) { - if (dpp_parse_uri_chan_list(bi, chan) < 0) - goto fail; - len += 3 + os_strlen(chan); /* C:...; */ - } - if (mac) { - if (dpp_parse_uri_mac(bi, mac) < 0) - goto fail; - len += 3 + os_strlen(mac); /* M:...; */ - } - if (info) { - if (dpp_parse_uri_info(bi, info) < 0) - goto fail; - len += 3 + os_strlen(info); /* I:...; */ - } - len += 4 + os_strlen(pk); - bi->uri = os_malloc(len + 1); - if (!bi->uri) - goto fail; - os_snprintf(bi->uri, len + 1, "DPP:%s%s%s%s%s%s%s%s%sK:%s;;", - chan ? "C:" : "", chan ? chan : "", chan ? ";" : "", - mac ? "M:" : "", mac ? mac : "", mac ? ";" : "", - info ? "I:" : "", info ? info : "", info ? ";" : "", - pk); - bi->id = wpas_dpp_next_id(wpa_s); - dl_list_add(&wpa_s->dpp_bootstrap, &bi->list); - ret = bi->id; - bi = NULL; -fail: - os_free(curve); - os_free(pk); - os_free(chan); - os_free(mac); - os_free(info); - str_clear_free(key); - bin_clear_free(privkey, privkey_len); - dpp_bootstrap_info_free(bi); - return ret; -} - - -static struct dpp_bootstrap_info * -dpp_bootstrap_get_id(struct wpa_supplicant *wpa_s, unsigned int id) -{ - struct dpp_bootstrap_info *bi; - - dl_list_for_each(bi, &wpa_s->dpp_bootstrap, struct dpp_bootstrap_info, - list) { - if (bi->id == id) - return bi; - } - return NULL; -} - - -static int dpp_bootstrap_del(struct wpa_supplicant *wpa_s, unsigned int id) -{ - struct dpp_bootstrap_info *bi, *tmp; - int found = 0; - - dl_list_for_each_safe(bi, tmp, &wpa_s->dpp_bootstrap, - struct dpp_bootstrap_info, list) { - if (id && bi->id != id) - continue; - found = 1; - dl_list_del(&bi->list); - dpp_bootstrap_info_free(bi); - } - - if (id == 0) - return 0; /* flush succeeds regardless of entries found */ - return found ? 0 : -1; -} - - -int wpas_dpp_bootstrap_remove(struct wpa_supplicant *wpa_s, const char *id) -{ - unsigned int id_val; - - if (os_strcmp(id, "*") == 0) { - id_val = 0; - } else { - id_val = atoi(id); - if (id_val == 0) - return -1; - } - - return dpp_bootstrap_del(wpa_s, id_val); -} - - -const char * wpas_dpp_bootstrap_get_uri(struct wpa_supplicant *wpa_s, - unsigned int id) -{ - struct dpp_bootstrap_info *bi; - - bi = dpp_bootstrap_get_id(wpa_s, id); - if (!bi) - return NULL; - return bi->uri; -} - - -int wpas_dpp_bootstrap_info(struct wpa_supplicant *wpa_s, int id, - char *reply, int reply_size) -{ - struct dpp_bootstrap_info *bi; - - bi = dpp_bootstrap_get_id(wpa_s, id); - if (!bi) - return -1; - return os_snprintf(reply, reply_size, "type=%s\n" - "mac_addr=" MACSTR "\n" - "info=%s\n" - "num_freq=%u\n" - "curve=%s\n", - dpp_bootstrap_type_txt(bi->type), - MAC2STR(bi->mac_addr), - bi->info ? bi->info : "", - bi->num_freq, - bi->curve->name); -} - - static void wpas_dpp_auth_resp_retry_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; @@ -364,6 +144,18 @@ static void wpas_dpp_auth_resp_retry(struct wpa_supplicant *wpa_s) } +static void wpas_dpp_try_to_connect(struct wpa_supplicant *wpa_s) +{ + wpa_printf(MSG_DEBUG, "DPP: Trying to connect to the new network"); + wpa_s->disconnected = 0; + wpa_s->reassociate = 1; + wpa_s->scan_runs = 0; + wpa_s->normal_scans = 0; + wpa_supplicant_cancel_sched_scan(wpa_s); + wpa_supplicant_req_scan(wpa_s, 0, 0); +} + + static void wpas_dpp_tx_status(struct wpa_supplicant *wpa_s, unsigned int freq, const u8 *dst, const u8 *src, const u8 *bssid, @@ -387,6 +179,17 @@ static void wpas_dpp_tx_status(struct wpa_supplicant *wpa_s, return; } +#ifdef CONFIG_DPP2 + if (auth->connect_on_tx_status) { + wpa_printf(MSG_DEBUG, + "DPP: Try to connect after completed configuration result"); + wpas_dpp_try_to_connect(wpa_s); + dpp_auth_deinit(wpa_s->dpp_auth); + wpa_s->dpp_auth = NULL; + return; + } +#endif /* CONFIG_DPP2 */ + if (wpa_s->dpp_auth->remove_on_tx_status) { wpa_printf(MSG_DEBUG, "DPP: Terminate authentication exchange due to an earlier error"); @@ -527,176 +330,6 @@ static void wpas_dpp_set_testing_options(struct wpa_supplicant *wpa_s, } -static int wpas_dpp_set_configurator(struct wpa_supplicant *wpa_s, - struct dpp_authentication *auth, - const char *cmd) -{ - const char *pos, *end; - struct dpp_configuration *conf_sta = NULL, *conf_ap = NULL; - struct dpp_configurator *conf = NULL; - u8 ssid[32] = { "test" }; - size_t ssid_len = 4; - char pass[64] = { }; - size_t pass_len = 0; - u8 psk[PMK_LEN]; - int psk_set = 0; - char *group_id = NULL; - - if (!cmd) - return 0; - - wpa_printf(MSG_DEBUG, "DPP: Set configurator parameters: %s", cmd); - pos = os_strstr(cmd, " ssid="); - if (pos) { - pos += 6; - end = os_strchr(pos, ' '); - ssid_len = end ? (size_t) (end - pos) : os_strlen(pos); - ssid_len /= 2; - if (ssid_len > sizeof(ssid) || - hexstr2bin(pos, ssid, ssid_len) < 0) - goto fail; - } - - pos = os_strstr(cmd, " pass="); - if (pos) { - pos += 6; - end = os_strchr(pos, ' '); - pass_len = end ? (size_t) (end - pos) : os_strlen(pos); - pass_len /= 2; - if (pass_len > sizeof(pass) - 1 || pass_len < 8 || - hexstr2bin(pos, (u8 *) pass, pass_len) < 0) - goto fail; - } - - pos = os_strstr(cmd, " psk="); - if (pos) { - pos += 5; - if (hexstr2bin(pos, psk, PMK_LEN) < 0) - goto fail; - psk_set = 1; - } - - pos = os_strstr(cmd, " group_id="); - if (pos) { - size_t group_id_len; - - pos += 10; - end = os_strchr(pos, ' '); - group_id_len = end ? (size_t) (end - pos) : os_strlen(pos); - group_id = os_malloc(group_id_len + 1); - if (!group_id) - goto fail; - os_memcpy(group_id, pos, group_id_len); - group_id[group_id_len] = '\0'; - } - - if (os_strstr(cmd, " conf=sta-")) { - conf_sta = os_zalloc(sizeof(struct dpp_configuration)); - if (!conf_sta) - goto fail; - os_memcpy(conf_sta->ssid, ssid, ssid_len); - conf_sta->ssid_len = ssid_len; - if (os_strstr(cmd, " conf=sta-psk") || - os_strstr(cmd, " conf=sta-sae") || - os_strstr(cmd, " conf=sta-psk-sae")) { - if (os_strstr(cmd, " conf=sta-psk-sae")) - conf_sta->akm = DPP_AKM_PSK_SAE; - else if (os_strstr(cmd, " conf=sta-sae")) - conf_sta->akm = DPP_AKM_SAE; - else - conf_sta->akm = DPP_AKM_PSK; - if (psk_set) { - os_memcpy(conf_sta->psk, psk, PMK_LEN); - } else if (pass_len > 0) { - conf_sta->passphrase = os_strdup(pass); - if (!conf_sta->passphrase) - goto fail; - } else { - goto fail; - } - } else if (os_strstr(cmd, " conf=sta-dpp")) { - conf_sta->akm = DPP_AKM_DPP; - } else { - goto fail; - } - if (os_strstr(cmd, " group_id=")) { - conf_sta->group_id = group_id; - group_id = NULL; - } - } - - if (os_strstr(cmd, " conf=ap-")) { - conf_ap = os_zalloc(sizeof(struct dpp_configuration)); - if (!conf_ap) - goto fail; - os_memcpy(conf_ap->ssid, ssid, ssid_len); - conf_ap->ssid_len = ssid_len; - if (os_strstr(cmd, " conf=ap-psk") || - os_strstr(cmd, " conf=ap-sae") || - os_strstr(cmd, " conf=ap-psk-sae")) { - if (os_strstr(cmd, " conf=ap-psk-sae")) - conf_ap->akm = DPP_AKM_PSK_SAE; - else if (os_strstr(cmd, " conf=ap-sae")) - conf_ap->akm = DPP_AKM_SAE; - else - conf_ap->akm = DPP_AKM_PSK; - if (psk_set) { - os_memcpy(conf_ap->psk, psk, PMK_LEN); - } else { - conf_ap->passphrase = os_strdup(pass); - if (!conf_ap->passphrase) - goto fail; - } - } else if (os_strstr(cmd, " conf=ap-dpp")) { - conf_ap->akm = DPP_AKM_DPP; - } else { - goto fail; - } - if (os_strstr(cmd, " group_id=")) { - conf_ap->group_id = group_id; - group_id = NULL; - } - } - - pos = os_strstr(cmd, " expiry="); - if (pos) { - long int val; - - pos += 8; - val = strtol(pos, NULL, 0); - if (val <= 0) - goto fail; - if (conf_sta) - conf_sta->netaccesskey_expiry = val; - if (conf_ap) - conf_ap->netaccesskey_expiry = val; - } - - pos = os_strstr(cmd, " configurator="); - if (pos) { - pos += 14; - conf = dpp_configurator_get_id(wpa_s, atoi(pos)); - if (!conf) { - wpa_printf(MSG_INFO, - "DPP: Could not find the specified configurator"); - goto fail; - } - } - auth->conf_sta = conf_sta; - auth->conf_ap = conf_ap; - auth->conf = conf; - os_free(group_id); - return 0; - -fail: - wpa_msg(wpa_s, MSG_INFO, "DPP: Failed to set configurator parameters"); - dpp_configuration_free(conf_sta); - dpp_configuration_free(conf_ap); - os_free(group_id); - return -1; -} - - static void wpas_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; @@ -809,7 +442,7 @@ int wpas_dpp_auth_init(struct wpa_supplicant *wpa_s, const char *cmd) if (!pos) return -1; pos += 6; - peer_bi = dpp_bootstrap_get_id(wpa_s, atoi(pos)); + peer_bi = dpp_bootstrap_get_id(wpa_s->dpp, atoi(pos)); if (!peer_bi) { wpa_printf(MSG_INFO, "DPP: Could not find bootstrapping info for the identified peer"); @@ -819,7 +452,7 @@ int wpas_dpp_auth_init(struct wpa_supplicant *wpa_s, const char *cmd) pos = os_strstr(cmd, " own="); if (pos) { pos += 5; - own_bi = dpp_bootstrap_get_id(wpa_s, atoi(pos)); + own_bi = dpp_bootstrap_get_id(wpa_s->dpp, atoi(pos)); if (!own_bi) { wpa_printf(MSG_INFO, "DPP: Could not find bootstrapping info for the identified local entry"); @@ -872,7 +505,7 @@ int wpas_dpp_auth_init(struct wpa_supplicant *wpa_s, const char *cmd) if (!wpa_s->dpp_auth) goto fail; wpas_dpp_set_testing_options(wpa_s, wpa_s->dpp_auth); - if (wpas_dpp_set_configurator(wpa_s, wpa_s->dpp_auth, cmd) < 0) { + if (dpp_set_configurator(wpa_s->dpp, wpa_s, wpa_s->dpp_auth, cmd) < 0) { dpp_auth_deinit(wpa_s->dpp_auth); wpa_s->dpp_auth = NULL; goto fail; @@ -942,6 +575,7 @@ static void dpp_start_listen_cb(struct wpa_radio_work *work, int deinit) wpa_printf(MSG_DEBUG, "DPP: Failed to request the driver to remain on channel (%u MHz) for listen", lwork->freq); + wpa_s->dpp_listen_freq = 0; wpas_dpp_listen_work_done(wpa_s); wpa_s->dpp_pending_listen_freq = 0; return; @@ -1055,7 +689,10 @@ static void wpas_dpp_rx_auth_req(struct wpa_supplicant *wpa_s, const u8 *src, { const u8 *r_bootstrap, *i_bootstrap; u16 r_bootstrap_len, i_bootstrap_len; - struct dpp_bootstrap_info *bi, *own_bi = NULL, *peer_bi = NULL; + struct dpp_bootstrap_info *own_bi = NULL, *peer_bi = NULL; + + if (!wpa_s->dpp) + return; wpa_printf(MSG_DEBUG, "DPP: Authentication Request from " MACSTR, MAC2STR(src)); @@ -1082,28 +719,8 @@ static void wpas_dpp_rx_auth_req(struct wpa_supplicant *wpa_s, const u8 *src, /* Try to find own and peer bootstrapping key matches based on the * received hash values */ - dl_list_for_each(bi, &wpa_s->dpp_bootstrap, struct dpp_bootstrap_info, - list) { - if (!own_bi && bi->own && - os_memcmp(bi->pubkey_hash, r_bootstrap, - SHA256_MAC_LEN) == 0) { - wpa_printf(MSG_DEBUG, - "DPP: Found matching own bootstrapping information"); - own_bi = bi; - } - - if (!peer_bi && !bi->own && - os_memcmp(bi->pubkey_hash, i_bootstrap, - SHA256_MAC_LEN) == 0) { - wpa_printf(MSG_DEBUG, - "DPP: Found matching peer bootstrapping information"); - peer_bi = bi; - } - - if (own_bi && peer_bi) - break; - } - + dpp_bootstrap_find_pair(wpa_s->dpp, i_bootstrap, r_bootstrap, + &own_bi, &peer_bi); if (!own_bi) { wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_FAIL "No matching own bootstrapping key found - ignore message"); @@ -1126,8 +743,8 @@ static void wpas_dpp_rx_auth_req(struct wpa_supplicant *wpa_s, const u8 *src, return; } wpas_dpp_set_testing_options(wpa_s, wpa_s->dpp_auth); - if (wpas_dpp_set_configurator(wpa_s, wpa_s->dpp_auth, - wpa_s->dpp_configurator_params) < 0) { + if (dpp_set_configurator(wpa_s->dpp, wpa_s, wpa_s->dpp_auth, + wpa_s->dpp_configurator_params) < 0) { dpp_auth_deinit(wpa_s->dpp_auth); wpa_s->dpp_auth = NULL; return; @@ -1164,6 +781,27 @@ static struct wpa_ssid * wpas_dpp_add_network(struct wpa_supplicant *wpa_s, { struct wpa_ssid *ssid; +#ifdef CONFIG_DPP2 + if (auth->akm == DPP_AKM_SAE) { +#ifdef CONFIG_SAE + struct wpa_driver_capa capa; + int res; + + res = wpa_drv_get_capa(wpa_s, &capa); + if (res == 0 && + !(capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SAE) && + !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE)) { + wpa_printf(MSG_DEBUG, + "DPP: SAE not supported by the driver"); + return NULL; + } +#else /* CONFIG_SAE */ + wpa_printf(MSG_DEBUG, "DPP: SAE not supported in the build"); + return NULL; +#endif /* CONFIG_SAE */ + } +#endif /* CONFIG_DPP2 */ + ssid = wpa_config_add_network(wpa_s->conf); if (!ssid) return NULL; @@ -1206,12 +844,14 @@ static struct wpa_ssid * wpas_dpp_add_network(struct wpa_supplicant *wpa_s, ssid->dpp_netaccesskey_expiry = auth->net_access_key_expiry; } - if (!auth->connector) { - ssid->key_mgmt = 0; - if (auth->akm == DPP_AKM_PSK || auth->akm == DPP_AKM_PSK_SAE) + if (!auth->connector || dpp_akm_psk(auth->akm) || + dpp_akm_sae(auth->akm)) { + if (!auth->connector) + ssid->key_mgmt = 0; + if (dpp_akm_psk(auth->akm)) ssid->key_mgmt |= WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_PSK_SHA256 | WPA_KEY_MGMT_FT_PSK; - if (auth->akm == DPP_AKM_SAE || auth->akm == DPP_AKM_PSK_SAE) + if (dpp_akm_sae(auth->akm)) ssid->key_mgmt |= WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE; ssid->ieee80211w = MGMT_FRAME_PROTECTION_OPTIONAL; @@ -1235,35 +875,47 @@ fail: } -static void wpas_dpp_process_config(struct wpa_supplicant *wpa_s, - struct dpp_authentication *auth) +static int wpas_dpp_process_config(struct wpa_supplicant *wpa_s, + struct dpp_authentication *auth) { struct wpa_ssid *ssid; if (wpa_s->conf->dpp_config_processing < 1) - return; + return 0; ssid = wpas_dpp_add_network(wpa_s, auth); if (!ssid) - return; + return -1; wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_NETWORK_ID "%d", ssid->id); + if (wpa_s->conf->dpp_config_processing == 2) + ssid->disabled = 0; + +#ifndef CONFIG_NO_CONFIG_WRITE + if (wpa_s->conf->update_config && + wpa_config_write(wpa_s->confname, wpa_s->conf)) + wpa_printf(MSG_DEBUG, "DPP: Failed to update configuration"); +#endif /* CONFIG_NO_CONFIG_WRITE */ + if (wpa_s->conf->dpp_config_processing < 2) - return; + return 0; - wpa_printf(MSG_DEBUG, "DPP: Trying to connect to the new network"); - ssid->disabled = 0; - wpa_s->disconnected = 0; - wpa_s->reassociate = 1; - wpa_s->scan_runs = 0; - wpa_s->normal_scans = 0; - wpa_supplicant_cancel_sched_scan(wpa_s); - wpa_supplicant_req_scan(wpa_s, 0, 0); +#ifdef CONFIG_DPP2 + if (auth->peer_version >= 2) { + wpa_printf(MSG_DEBUG, + "DPP: Postpone connection attempt to wait for completion of DPP Configuration Result"); + auth->connect_on_tx_status = 1; + return 0; + } +#endif /* CONFIG_DPP2 */ + + wpas_dpp_try_to_connect(wpa_s); + return 0; } -static void wpas_dpp_handle_config_obj(struct wpa_supplicant *wpa_s, - struct dpp_authentication *auth) +static int wpas_dpp_handle_config_obj(struct wpa_supplicant *wpa_s, + struct dpp_authentication *auth) { wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_RECEIVED); if (auth->ssid_len) @@ -1315,7 +967,7 @@ static void wpas_dpp_handle_config_obj(struct wpa_supplicant *wpa_s, } } - wpas_dpp_process_config(wpa_s, auth); + return wpas_dpp_process_config(wpa_s, auth); } @@ -1327,6 +979,8 @@ static void wpas_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token, struct wpa_supplicant *wpa_s = ctx; const u8 *pos; struct dpp_authentication *auth = wpa_s->dpp_auth; + int res; + enum dpp_status_error status = DPP_STATUS_CONFIG_REJECTED; wpa_s->dpp_gas_dialog_token = -1; @@ -1364,13 +1018,46 @@ static void wpas_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token, goto fail; } - wpas_dpp_handle_config_obj(wpa_s, auth); - dpp_auth_deinit(wpa_s->dpp_auth); - wpa_s->dpp_auth = NULL; - return; + res = wpas_dpp_handle_config_obj(wpa_s, auth); + if (res < 0) + goto fail; + 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: - wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_FAILED); + if (status != DPP_STATUS_OK) + wpa_msg(wpa_s, 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(wpa_s, MSG_INFO, + DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d", + MAC2STR(addr), auth->curr_freq, + DPP_PA_CONFIGURATION_RESULT); + offchannel_send_action(wpa_s, auth->curr_freq, + addr, wpa_s->own_addr, broadcast, + wpabuf_head(msg), + wpabuf_len(msg), + 500, wpas_dpp_tx_status, 0); + wpabuf_free(msg); + + /* This exchange will be terminated in the TX status handler */ + return; + } +fail2: +#endif /* CONFIG_DPP2 */ dpp_auth_deinit(wpa_s->dpp_auth); wpa_s->dpp_auth = NULL; } @@ -1379,7 +1066,7 @@ fail: static void wpas_dpp_start_gas_client(struct wpa_supplicant *wpa_s) { struct dpp_authentication *auth = wpa_s->dpp_auth; - struct wpabuf *buf, *conf_req; + struct wpabuf *buf; char json[100]; int res; @@ -1400,34 +1087,13 @@ static void wpas_dpp_start_gas_client(struct wpa_supplicant *wpa_s) offchannel_send_action_done(wpa_s); wpas_dpp_listen_stop(wpa_s); - conf_req = dpp_build_conf_req(auth, json); - if (!conf_req) { + buf = dpp_build_conf_req(auth, json); + if (!buf) { wpa_printf(MSG_DEBUG, "DPP: No configuration request data available"); return; } - buf = gas_build_initial_req(0, 10 + 2 + wpabuf_len(conf_req)); - if (!buf) { - wpabuf_free(conf_req); - return; - } - - /* Advertisement Protocol IE */ - wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO); - wpabuf_put_u8(buf, 8); /* Length */ - wpabuf_put_u8(buf, 0x7f); - wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); - wpabuf_put_u8(buf, 5); - wpabuf_put_be24(buf, OUI_WFA); - wpabuf_put_u8(buf, DPP_OUI_TYPE); - wpabuf_put_u8(buf, 0x01); - - /* GAS Query */ - wpabuf_put_le16(buf, wpabuf_len(conf_req)); - wpabuf_put_buf(buf, conf_req); - wpabuf_free(conf_req); - wpa_printf(MSG_DEBUG, "DPP: GAS request to " MACSTR " (freq %u MHz)", MAC2STR(auth->peer_mac_addr), auth->curr_freq); @@ -1553,6 +1219,62 @@ static void wpas_dpp_rx_auth_conf(struct wpa_supplicant *wpa_s, const u8 *src, } +#ifdef CONFIG_DPP2 + +static void wpas_dpp_config_result_wait_timeout(void *eloop_ctx, + void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct dpp_authentication *auth = wpa_s->dpp_auth; + + if (!auth || !auth->waiting_conf_result) + return; + + wpa_printf(MSG_DEBUG, + "DPP: Timeout while waiting for Configuration Result"); + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_FAILED); + dpp_auth_deinit(auth); + wpa_s->dpp_auth = NULL; +} + + +static void wpas_dpp_rx_conf_result(struct wpa_supplicant *wpa_s, const u8 *src, + const u8 *hdr, const u8 *buf, size_t len) +{ + struct dpp_authentication *auth = wpa_s->dpp_auth; + 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); + + offchannel_send_action_done(wpa_s); + wpas_dpp_listen_stop(wpa_s); + if (status == DPP_STATUS_OK) + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_SENT); + else + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_FAILED); + dpp_auth_deinit(auth); + wpa_s->dpp_auth = NULL; + eloop_cancel_timeout(wpas_dpp_config_result_wait_timeout, wpa_s, NULL); +} + +#endif /* CONFIG_DPP2 */ + + static void wpas_dpp_rx_peer_disc_resp(struct wpa_supplicant *wpa_s, const u8 *src, const u8 *buf, size_t len) @@ -1913,27 +1635,12 @@ static struct dpp_bootstrap_info * wpas_dpp_pkex_finish(struct wpa_supplicant *wpa_s, const u8 *peer, unsigned int freq) { - struct dpp_pkex *pkex = wpa_s->dpp_pkex; struct dpp_bootstrap_info *bi; - bi = os_zalloc(sizeof(*bi)); + bi = dpp_pkex_finish(wpa_s->dpp, wpa_s->dpp_pkex, peer, freq); if (!bi) return NULL; - bi->id = wpas_dpp_next_id(wpa_s); - bi->type = DPP_BOOTSTRAP_PKEX; - os_memcpy(bi->mac_addr, peer, ETH_ALEN); - bi->num_freq = 1; - bi->freq[0] = freq; - bi->curve = pkex->own_bi->curve; - bi->pubkey = pkex->peer_bootstrap_key; - pkex->peer_bootstrap_key = NULL; - dpp_pkex_free(pkex); wpa_s->dpp_pkex = NULL; - if (dpp_bootstrap_key_hash(bi) < 0) { - dpp_bootstrap_info_free(bi); - return NULL; - } - dl_list_add(&wpa_s->dpp_bootstrap, &bi->list); return bi; } @@ -2096,6 +1803,11 @@ void wpas_dpp_rx_action(struct wpa_supplicant *wpa_s, const u8 *src, wpas_dpp_rx_pkex_commit_reveal_resp(wpa_s, src, hdr, buf, len, freq); break; +#ifdef CONFIG_DPP2 + case DPP_PA_CONFIGURATION_RESULT: + wpas_dpp_rx_conf_result(wpa_s, src, hdr, buf, len); + break; +#endif /* CONFIG_DPP2 */ default: wpa_printf(MSG_DEBUG, "DPP: Ignored unsupported frame subtype %d", type); @@ -2165,6 +1877,21 @@ wpas_dpp_gas_status_handler(void *ctx, struct wpabuf *resp, int ok) ok); eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL); eloop_cancel_timeout(wpas_dpp_auth_resp_retry_timeout, wpa_s, 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; + auth->conf_resp = NULL; + wpabuf_free(resp); + eloop_cancel_timeout(wpas_dpp_config_result_wait_timeout, + wpa_s, NULL); + eloop_register_timeout(2, 0, + wpas_dpp_config_result_wait_timeout, + wpa_s, NULL); + return; + } +#endif /* CONFIG_DPP2 */ offchannel_send_action_done(wpa_s); wpas_dpp_listen_stop(wpa_s); if (ok) @@ -2177,93 +1904,6 @@ wpas_dpp_gas_status_handler(void *ctx, struct wpabuf *resp, int ok) } -static unsigned int wpas_dpp_next_configurator_id(struct wpa_supplicant *wpa_s) -{ - struct dpp_configurator *conf; - unsigned int max_id = 0; - - dl_list_for_each(conf, &wpa_s->dpp_configurator, - struct dpp_configurator, list) { - if (conf->id > max_id) - max_id = conf->id; - } - return max_id + 1; -} - - -int wpas_dpp_configurator_add(struct wpa_supplicant *wpa_s, const char *cmd) -{ - char *curve = NULL; - char *key = NULL; - u8 *privkey = NULL; - size_t privkey_len = 0; - int ret = -1; - struct dpp_configurator *conf = NULL; - - curve = get_param(cmd, " curve="); - key = get_param(cmd, " key="); - - if (key) { - privkey_len = os_strlen(key) / 2; - privkey = os_malloc(privkey_len); - if (!privkey || - hexstr2bin(key, privkey, privkey_len) < 0) - goto fail; - } - - conf = dpp_keygen_configurator(curve, privkey, privkey_len); - if (!conf) - goto fail; - - conf->id = wpas_dpp_next_configurator_id(wpa_s); - dl_list_add(&wpa_s->dpp_configurator, &conf->list); - ret = conf->id; - conf = NULL; -fail: - os_free(curve); - str_clear_free(key); - bin_clear_free(privkey, privkey_len); - dpp_configurator_free(conf); - return ret; -} - - -static int dpp_configurator_del(struct wpa_supplicant *wpa_s, unsigned int id) -{ - struct dpp_configurator *conf, *tmp; - int found = 0; - - dl_list_for_each_safe(conf, tmp, &wpa_s->dpp_configurator, - struct dpp_configurator, list) { - if (id && conf->id != id) - continue; - found = 1; - dl_list_del(&conf->list); - dpp_configurator_free(conf); - } - - if (id == 0) - return 0; /* flush succeeds regardless of entries found */ - return found ? 0 : -1; -} - - -int wpas_dpp_configurator_remove(struct wpa_supplicant *wpa_s, const char *id) -{ - unsigned int id_val; - - if (os_strcmp(id, "*") == 0) { - id_val = 0; - } else { - id_val = atoi(id); - if (id_val == 0) - return -1; - } - - return dpp_configurator_del(wpa_s, id_val); -} - - int wpas_dpp_configurator_sign(struct wpa_supplicant *wpa_s, const char *cmd) { struct dpp_authentication *auth; @@ -2276,11 +1916,9 @@ int wpas_dpp_configurator_sign(struct wpa_supplicant *wpa_s, const char *cmd) curve = get_param(cmd, " curve="); wpas_dpp_set_testing_options(wpa_s, auth); - if (wpas_dpp_set_configurator(wpa_s, auth, cmd) == 0 && - dpp_configurator_own_config(auth, curve, 0) == 0) { - wpas_dpp_handle_config_obj(wpa_s, auth); - ret = 0; - } + if (dpp_set_configurator(wpa_s->dpp, wpa_s, auth, cmd) == 0 && + dpp_configurator_own_config(auth, curve, 0) == 0) + ret = wpas_dpp_handle_config_obj(wpa_s, auth); dpp_auth_deinit(auth); os_free(curve); @@ -2289,19 +1927,6 @@ int wpas_dpp_configurator_sign(struct wpa_supplicant *wpa_s, const char *cmd) } -int wpas_dpp_configurator_get_key(struct wpa_supplicant *wpa_s, unsigned int id, - char *buf, size_t buflen) -{ - struct dpp_configurator *conf; - - conf = dpp_configurator_get_id(wpa_s, id); - if (!conf) - return -1; - - return dpp_configurator_get_key(conf, buf, buflen); -} - - static void wpas_dpp_tx_introduction_status(struct wpa_supplicant *wpa_s, unsigned int freq, const u8 *dst, @@ -2329,9 +1954,15 @@ int wpas_dpp_check_connect(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct os_time now; struct wpabuf *msg; unsigned int wait_time; + const u8 *rsn; + struct wpa_ie_data ied; if (!(ssid->key_mgmt & WPA_KEY_MGMT_DPP) || !bss) return 0; /* Not using DPP AKM - continue */ + rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN); + if (rsn && wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied) == 0 && + !(ied.key_mgmt & WPA_KEY_MGMT_DPP)) + return 0; /* AP does not support DPP AKM - continue */ if (wpa_sm_pmksa_exists(wpa_s->wpa, bss->bssid, ssid)) return 0; /* PMKSA exists for DPP AKM - continue */ @@ -2444,7 +2075,7 @@ int wpas_dpp_pkex_add(struct wpa_supplicant *wpa_s, const char *cmd) if (!pos) return -1; pos += 5; - own_bi = dpp_bootstrap_get_id(wpa_s, atoi(pos)); + own_bi = dpp_bootstrap_get_id(wpa_s->dpp, atoi(pos)); if (!own_bi) { wpa_printf(MSG_DEBUG, "DPP: Identified bootstrap info not found"); @@ -2577,10 +2208,8 @@ int wpas_dpp_init(struct wpa_supplicant *wpa_s) sizeof(adv_proto_id), wpas_dpp_gas_req_handler, wpas_dpp_gas_status_handler, wpa_s) < 0) return -1; - dl_list_init(&wpa_s->dpp_bootstrap); - dl_list_init(&wpa_s->dpp_configurator); - wpa_s->dpp_init_done = 1; - return 0; + wpa_s->dpp = dpp_global_init(); + return wpa_s->dpp ? 0 : -1; } @@ -2595,16 +2224,20 @@ void wpas_dpp_deinit(struct wpa_supplicant *wpa_s) wpa_s->dpp_groups_override = NULL; wpa_s->dpp_ignore_netaccesskey_mismatch = 0; #endif /* CONFIG_TESTING_OPTIONS */ - if (!wpa_s->dpp_init_done) + if (!wpa_s->dpp) return; + dpp_global_clear(wpa_s->dpp); eloop_cancel_timeout(wpas_dpp_pkex_retry_timeout, wpa_s, NULL); eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL); eloop_cancel_timeout(wpas_dpp_init_timeout, wpa_s, NULL); eloop_cancel_timeout(wpas_dpp_auth_resp_retry_timeout, wpa_s, NULL); +#ifdef CONFIG_DPP2 + eloop_cancel_timeout(wpas_dpp_config_result_wait_timeout, wpa_s, NULL); + dpp_pfs_free(wpa_s->dpp_pfs); + wpa_s->dpp_pfs = NULL; +#endif /* CONFIG_DPP2 */ offchannel_send_action_done(wpa_s); wpas_dpp_listen_stop(wpa_s); - dpp_bootstrap_del(wpa_s, 0); - dpp_configurator_del(wpa_s, 0); wpas_dpp_stop(wpa_s); wpas_dpp_pkex_remove(wpa_s, "*"); os_memset(wpa_s->dpp_intro_bssid, 0, ETH_ALEN); diff --git a/wpa_supplicant/dpp_supplicant.h b/wpa_supplicant/dpp_supplicant.h index 5a4f06e..ecb7a7d 100644 --- a/wpa_supplicant/dpp_supplicant.h +++ b/wpa_supplicant/dpp_supplicant.h @@ -10,12 +10,6 @@ #define DPP_SUPPLICANT_H int wpas_dpp_qr_code(struct wpa_supplicant *wpa_s, const char *cmd); -int wpas_dpp_bootstrap_gen(struct wpa_supplicant *wpa_s, const char *cmd); -int wpas_dpp_bootstrap_remove(struct wpa_supplicant *wpa_s, const char *id); -const char * wpas_dpp_bootstrap_get_uri(struct wpa_supplicant *wpa_s, - unsigned int id); -int wpas_dpp_bootstrap_info(struct wpa_supplicant *wpa_s, int id, - char *reply, int reply_size); int wpas_dpp_auth_init(struct wpa_supplicant *wpa_s, const char *cmd); int wpas_dpp_listen(struct wpa_supplicant *wpa_s, const char *cmd); void wpas_dpp_listen_stop(struct wpa_supplicant *wpa_s); @@ -23,11 +17,7 @@ void wpas_dpp_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, unsigned int freq); void wpas_dpp_rx_action(struct wpa_supplicant *wpa_s, const u8 *src, const u8 *buf, size_t len, unsigned int freq); -int wpas_dpp_configurator_add(struct wpa_supplicant *wpa_s, const char *cmd); -int wpas_dpp_configurator_remove(struct wpa_supplicant *wpa_s, const char *id); int wpas_dpp_configurator_sign(struct wpa_supplicant *wpa_s, const char *cmd); -int wpas_dpp_configurator_get_key(struct wpa_supplicant *wpa_s, unsigned int id, - char *buf, size_t buflen); int wpas_dpp_pkex_add(struct wpa_supplicant *wpa_s, const char *cmd); int wpas_dpp_pkex_remove(struct wpa_supplicant *wpa_s, const char *id); void wpas_dpp_stop(struct wpa_supplicant *wpa_s); diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index 638a9ab..f6ec111 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - Driver event processing - * Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -29,6 +29,7 @@ #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "common/gas_server.h" +#include "common/dpp.h" #include "crypto/random.h" #include "blacklist.h" #include "wpas_glue.h" @@ -559,6 +560,10 @@ static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s, " skip RSN IE - parse failed"); break; } + if (!ie.has_pairwise) + ie.pairwise_cipher = wpa_default_rsn_cipher(bss->freq); + if (!ie.has_group) + ie.group_cipher = wpa_default_rsn_cipher(bss->freq); if (wep_ok && (ie.group_cipher & (WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104))) @@ -1899,7 +1904,7 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, if (sme_proc_obss_scan(wpa_s) > 0) goto scan_work_done; - if (own_request && + if (own_request && data && wpas_beacon_rep_scan_process(wpa_s, scan_res, &data->scan_info) > 0) goto scan_work_done; @@ -2305,6 +2310,13 @@ static void multi_ap_process_assoc_resp(struct wpa_supplicant *wpa_s, } if (!(map_sub_elem[2] & MULTI_AP_BACKHAUL_BSS)) { + if ((map_sub_elem[2] & MULTI_AP_FRONTHAUL_BSS) && + wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_WPS) { + wpa_printf(MSG_INFO, + "WPS active, accepting fronthaul-only BSS"); + /* Don't set 4addr mode in this case, so just return */ + return; + } wpa_printf(MSG_INFO, "AP doesn't support backhaul BSS"); goto fail; } @@ -2409,6 +2421,26 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, wpa_dbg(wpa_s, MSG_DEBUG, "freq=%u MHz", data->assoc_info.freq); + wpa_s->connection_set = 0; + if (data->assoc_info.req_ies && data->assoc_info.resp_ies) { + struct ieee802_11_elems req_elems, resp_elems; + + if (ieee802_11_parse_elems(data->assoc_info.req_ies, + data->assoc_info.req_ies_len, + &req_elems, 0) != ParseFailed && + ieee802_11_parse_elems(data->assoc_info.resp_ies, + data->assoc_info.resp_ies_len, + &resp_elems, 0) != ParseFailed) { + wpa_s->connection_set = 1; + wpa_s->connection_ht = req_elems.ht_capabilities && + resp_elems.ht_capabilities; + wpa_s->connection_vht = req_elems.vht_capabilities && + resp_elems.vht_capabilities; + wpa_s->connection_he = req_elems.he_capabilities && + resp_elems.he_capabilities; + } + } + p = data->assoc_info.req_ies; l = data->assoc_info.req_ies_len; @@ -2467,6 +2499,28 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_OWE */ +#ifdef CONFIG_DPP2 + wpa_sm_set_dpp_z(wpa_s->wpa, NULL); + if (wpa_s->key_mgmt == WPA_KEY_MGMT_DPP && wpa_s->dpp_pfs) { + struct ieee802_11_elems elems; + + if (ieee802_11_parse_elems(data->assoc_info.resp_ies, + data->assoc_info.resp_ies_len, + &elems, 0) == ParseFailed || + !elems.owe_dh) + goto no_pfs; + if (dpp_pfs_process(wpa_s->dpp_pfs, elems.owe_dh, + elems.owe_dh_len) < 0) { + wpa_supplicant_deauthenticate(wpa_s, + WLAN_REASON_UNSPECIFIED); + return -1; + } + + wpa_sm_set_dpp_z(wpa_s->wpa, wpa_s->dpp_pfs->secret); + } +no_pfs: +#endif /* CONFIG_DPP2 */ + #ifdef CONFIG_IEEE80211R #ifdef CONFIG_SME if (wpa_s->sme.auth_alg == WPA_AUTH_ALG_FT) { @@ -2805,8 +2859,17 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, } wpa_supplicant_cancel_scan(wpa_s); - if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK) && - wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) { + if (ft_completed) { + /* + * FT protocol completed - make sure EAPOL state machine ends + * up in authenticated. + */ + wpa_supplicant_cancel_auth_timeout(wpa_s); + wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); + eapol_sm_notify_portValid(wpa_s->eapol, TRUE); + eapol_sm_notify_eap_success(wpa_s->eapol, TRUE); + } else if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK) && + wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) { /* * We are done; the driver will take care of RSN 4-way * handshake. @@ -2823,15 +2886,6 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, * waiting for WPA supplicant. */ eapol_sm_notify_portValid(wpa_s->eapol, TRUE); - } else if (ft_completed) { - /* - * FT protocol completed - make sure EAPOL state machine ends - * up in authenticated. - */ - wpa_supplicant_cancel_auth_timeout(wpa_s); - wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); - eapol_sm_notify_portValid(wpa_s->eapol, TRUE); - eapol_sm_notify_eap_success(wpa_s->eapol, TRUE); } wpa_s->last_eapol_matches_bssid = 0; @@ -4809,7 +4863,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, break; case EVENT_WPS_BUTTON_PUSHED: #ifdef CONFIG_WPS - wpas_wps_start_pbc(wpa_s, NULL, 0); + wpas_wps_start_pbc(wpa_s, NULL, 0, 0); #endif /* CONFIG_WPS */ break; case EVENT_AVOID_FREQUENCIES: diff --git a/wpa_supplicant/examples/wpas-test.py b/wpa_supplicant/examples/wpas-test.py deleted file mode 100755 index bdd16a8..0000000 --- a/wpa_supplicant/examples/wpas-test.py +++ /dev/null @@ -1,91 +0,0 @@ -#!/usr/bin/python - -import dbus -import sys, os -import time - -WPAS_DBUS_SERVICE = "fi.epitest.hostap.WPASupplicant" -WPAS_DBUS_INTERFACE = "fi.epitest.hostap.WPASupplicant" -WPAS_DBUS_OPATH = "/fi/epitest/hostap/WPASupplicant" - -WPAS_DBUS_INTERFACES_INTERFACE = "fi.epitest.hostap.WPASupplicant.Interface" -WPAS_DBUS_INTERFACES_OPATH = "/fi/epitest/hostap/WPASupplicant/Interfaces" -WPAS_DBUS_BSSID_INTERFACE = "fi.epitest.hostap.WPASupplicant.BSSID" - -def byte_array_to_string(s): - import urllib - r = "" - for c in s: - if c >= 32 and c < 127: - r += "%c" % c - else: - r += urllib.quote(chr(c)) - return r - -def main(): - if len(sys.argv) != 2: - print("Usage: wpas-test.py <interface>") - os._exit(1) - - ifname = sys.argv[1] - - bus = dbus.SystemBus() - wpas_obj = bus.get_object(WPAS_DBUS_SERVICE, WPAS_DBUS_OPATH) - wpas = dbus.Interface(wpas_obj, WPAS_DBUS_INTERFACE) - - # See if wpa_supplicant already knows about this interface - path = None - try: - path = wpas.getInterface(ifname) - except dbus.dbus_bindings.DBusException as exc: - if str(exc) != "wpa_supplicant knows nothing about this interface.": - raise exc - try: - path = wpas.addInterface(ifname, {'driver': dbus.Variant('wext')}) - except dbus.dbus_bindings.DBusException as exc: - if str(exc) != "wpa_supplicant already controls this interface.": - raise exc - - if_obj = bus.get_object(WPAS_DBUS_SERVICE, path) - iface = dbus.Interface(if_obj, WPAS_DBUS_INTERFACES_INTERFACE) - iface.scan() - # Should really wait for the "scanResults" signal instead of sleeping - time.sleep(5) - res = iface.scanResults() - - print("Scanned wireless networks:") - for opath in res: - net_obj = bus.get_object(WPAS_DBUS_SERVICE, opath) - net = dbus.Interface(net_obj, WPAS_DBUS_BSSID_INTERFACE) - props = net.properties() - - # Convert the byte-array for SSID and BSSID to printable strings - bssid = "" - for item in props["bssid"]: - bssid = bssid + ":%02x" % item - bssid = bssid[1:] - ssid = byte_array_to_string(props["ssid"]) - wpa = "no" - if props.has_key("wpaie"): - wpa = "yes" - wpa2 = "no" - if props.has_key("rsnie"): - wpa2 = "yes" - freq = 0 - if props.has_key("frequency"): - freq = props["frequency"] - caps = props["capabilities"] - qual = props["quality"] - level = props["level"] - noise = props["noise"] - maxrate = props["maxrate"] / 1000000 - - print(" %s :: ssid='%s' wpa=%s wpa2=%s quality=%d%% rate=%d freq=%d" % (bssid, ssid, wpa, wpa2, qual, maxrate, freq)) - - wpas.removeInterface(dbus.ObjectPath(path)) - # Should fail here with unknown interface error - iface.scan() - -if __name__ == "__main__": - main() - diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c index 00919d1..e96ea65 100644 --- a/wpa_supplicant/ibss_rsn.c +++ b/wpa_supplicant/ibss_rsn.c @@ -260,12 +260,14 @@ static void auth_logger(void *ctx, const u8 *addr, logger_level level, static const u8 * auth_get_psk(void *ctx, const u8 *addr, const u8 *p2p_dev_addr, const u8 *prev_psk, - size_t *psk_len) + size_t *psk_len, int *vlan_id) { struct ibss_rsn *ibss_rsn = ctx; if (psk_len) *psk_len = PMK_LEN; + if (vlan_id) + *vlan_id = 0; wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)", __func__, MAC2STR(addr), prev_psk); if (prev_psk) @@ -457,7 +459,7 @@ static int ibss_rsn_auth_init(struct ibss_rsn *ibss_rsn, } /* TODO: get peer RSN IE with Probe Request */ - if (wpa_validate_wpa_ie(ibss_rsn->auth_group, peer->auth, + if (wpa_validate_wpa_ie(ibss_rsn->auth_group, peer->auth, 0, (u8 *) "\x30\x14\x01\x00" "\x00\x0f\xac\x04" "\x01\x00\x00\x0f\xac\x04" diff --git a/wpa_supplicant/main.c b/wpa_supplicant/main.c index e08c2fd..51a8a02 100644 --- a/wpa_supplicant/main.c +++ b/wpa_supplicant/main.c @@ -28,9 +28,9 @@ static void usage(void) "s" #endif /* CONFIG_DEBUG_SYSLOG */ "t" -#ifdef CONFIG_DBUS +#ifdef CONFIG_CTRL_IFACE_DBUS_NEW "u" -#endif /* CONFIG_DBUS */ +#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ "vW] [-P<pid file>] " "[-g<global ctrl>] \\\n" " [-G<group>] \\\n" @@ -98,9 +98,9 @@ static void usage(void) " -T = record to Linux tracing in addition to logging\n" " (records all messages regardless of debug verbosity)\n" #endif /* CONFIG_DEBUG_LINUX_TRACING */ -#ifdef CONFIG_DBUS +#ifdef CONFIG_CTRL_IFACE_DBUS_NEW " -u = enable DBus control interface\n" -#endif /* CONFIG_DBUS */ +#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ " -v = show version\n" " -W = wait for a control interface monitor before starting\n"); @@ -295,11 +295,11 @@ int main(int argc, char *argv[]) case 't': params.wpa_debug_timestamp++; break; -#ifdef CONFIG_DBUS +#ifdef CONFIG_CTRL_IFACE_DBUS_NEW case 'u': params.dbus_ctrl_interface = 1; break; -#endif /* CONFIG_DBUS */ +#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ case 'v': printf("%s\n", wpa_supplicant_version); exitcode = 0; diff --git a/wpa_supplicant/mbo.c b/wpa_supplicant/mbo.c index bd5020a..43b1fa7 100644 --- a/wpa_supplicant/mbo.c +++ b/wpa_supplicant/mbo.c @@ -98,6 +98,13 @@ static void wpas_mbo_non_pref_chan_attr_body(struct wpa_supplicant *wpa_s, } +static void wpas_mbo_non_pref_chan_attr_hdr(struct wpabuf *mbo, size_t size) +{ + wpabuf_put_u8(mbo, MBO_ATTR_ID_NON_PREF_CHAN_REPORT); + wpabuf_put_u8(mbo, size); /* Length */ +} + + static void wpas_mbo_non_pref_chan_attr(struct wpa_supplicant *wpa_s, struct wpabuf *mbo, u8 start, u8 end) { @@ -106,9 +113,7 @@ static void wpas_mbo_non_pref_chan_attr(struct wpa_supplicant *wpa_s, if (size + 2 > wpabuf_tailroom(mbo)) return; - wpabuf_put_u8(mbo, MBO_ATTR_ID_NON_PREF_CHAN_REPORT); - wpabuf_put_u8(mbo, size); /* Length */ - + wpas_mbo_non_pref_chan_attr_hdr(mbo, size); wpas_mbo_non_pref_chan_attr_body(wpa_s, mbo, start, end); } @@ -145,6 +150,8 @@ static void wpas_mbo_non_pref_chan_attrs(struct wpa_supplicant *wpa_s, if (!wpa_s->non_pref_chan || !wpa_s->non_pref_chan_num) { if (subelement) wpas_mbo_non_pref_chan_subelem_hdr(mbo, 4); + else + wpas_mbo_non_pref_chan_attr_hdr(mbo, 0); return; } start_pref = &wpa_s->non_pref_chan[0]; @@ -268,6 +275,7 @@ static void wpas_mbo_non_pref_chan_changed(struct wpa_supplicant *wpa_s) wpas_mbo_non_pref_chan_attrs(wpa_s, buf, 1); wpas_mbo_send_wnm_notification(wpa_s, wpabuf_head_u8(buf), wpabuf_len(buf)); + wpas_update_mbo_connect_params(wpa_s); wpabuf_free(buf); } @@ -293,10 +301,10 @@ static int wpa_non_pref_chan_cmp(const void *_a, const void *_b) const struct wpa_mbo_non_pref_channel *a = _a, *b = _b; if (a->oper_class != b->oper_class) - return a->oper_class - b->oper_class; + return (int) a->oper_class - (int) b->oper_class; if (a->reason != b->reason) - return a->reason - b->reason; - return a->preference - b->preference; + return (int) a->reason - (int) b->reason; + return (int) a->preference - (int) b->preference; } @@ -558,6 +566,7 @@ void wpas_mbo_update_cell_capa(struct wpa_supplicant *wpa_s, u8 mbo_cell_capa) wpas_mbo_send_wnm_notification(wpa_s, cell_capa, 7); wpa_supplicant_set_default_scan_ies(wpa_s); + wpas_update_mbo_connect_params(wpa_s); } diff --git a/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c index e9457f0..9260021 100644 --- a/wpa_supplicant/mesh.c +++ b/wpa_supplicant/mesh.c @@ -197,7 +197,7 @@ static int wpas_mesh_complete(struct wpa_supplicant *wpa_s) struct wpa_ssid *ssid = wpa_s->current_ssid; int ret; - if (!params || !ssid) { + if (!params || !ssid || !ifmsh) { wpa_printf(MSG_ERROR, "mesh: %s called without active mesh", __func__); return -1; @@ -217,13 +217,11 @@ static int wpas_mesh_complete(struct wpa_supplicant *wpa_s) wpa_s->mgmt_group_cipher = wpa_s->mesh_rsn->mgmt_group_cipher; } - if (ifmsh) { - params->ies = ifmsh->mconf->rsn_ie; - params->ie_len = ifmsh->mconf->rsn_ie_len; - params->basic_rates = ifmsh->basic_rates; - params->conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_HT_OP_MODE; - params->conf.ht_opmode = ifmsh->bss[0]->iface->ht_op_mode; - } + params->ies = ifmsh->mconf->rsn_ie; + params->ie_len = ifmsh->mconf->rsn_ie_len; + params->basic_rates = ifmsh->basic_rates; + params->conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_HT_OP_MODE; + params->conf.ht_opmode = ifmsh->bss[0]->iface->ht_op_mode; wpa_msg(wpa_s, MSG_INFO, "joining mesh %s", wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); diff --git a/wpa_supplicant/mesh_mpm.c b/wpa_supplicant/mesh_mpm.c index 4485939..9d6ab8d 100644 --- a/wpa_supplicant/mesh_mpm.c +++ b/wpa_supplicant/mesh_mpm.c @@ -189,7 +189,7 @@ static void mesh_mpm_init_link(struct wpa_supplicant *wpa_s, do { if (os_get_random((u8 *) &llid, sizeof(llid)) < 0) - continue; + llid = 0; /* continue */ } while (!llid || llid_in_use(wpa_s, llid)); sta->my_lid = llid; diff --git a/wpa_supplicant/mesh_rsn.c b/wpa_supplicant/mesh_rsn.c index 04ac747..4b8d6c4 100644 --- a/wpa_supplicant/mesh_rsn.c +++ b/wpa_supplicant/mesh_rsn.c @@ -76,7 +76,7 @@ static void auth_logger(void *ctx, const u8 *addr, logger_level level, static const u8 *auth_get_psk(void *ctx, const u8 *addr, const u8 *p2p_dev_addr, const u8 *prev_psk, - size_t *psk_len) + size_t *psk_len, int *vlan_id) { struct mesh_rsn *mesh_rsn = ctx; struct hostapd_data *hapd = mesh_rsn->wpa_s->ifmsh->bss[0]; @@ -84,6 +84,8 @@ static const u8 *auth_get_psk(void *ctx, const u8 *addr, if (psk_len) *psk_len = PMK_LEN; + if (vlan_id) + *vlan_id = 0; wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)", __func__, MAC2STR(addr), prev_psk); @@ -641,7 +643,7 @@ int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta, size_t crypt_len; const u8 *aad[] = { sta->addr, wpa_s->own_addr, cat }; const size_t aad_len[] = { ETH_ALEN, ETH_ALEN, - (elems->mic - 2) - cat }; + elems->mic ? (elems->mic - 2) - cat : 0 }; size_t key_len; if (!sta->sae) { @@ -655,7 +657,9 @@ int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta, mesh_rsn_auth_sae_sta(wpa_s, sta); } - if (chosen_pmk && os_memcmp(chosen_pmk, sta->sae->pmkid, PMKID_LEN)) { + if (chosen_pmk && + (!sta->sae || + os_memcmp(chosen_pmk, sta->sae->pmkid, PMKID_LEN) != 0)) { wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: Invalid PMKID (Chosen PMK did not match calculated PMKID)"); return -1; diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c index e5e45de..bedb74b 100644 --- a/wpa_supplicant/notify.c +++ b/wpa_supplicant/notify.c @@ -15,7 +15,6 @@ #include "wps_supplicant.h" #include "binder/binder.h" #include "dbus/dbus_common.h" -#include "dbus/dbus_old.h" #include "dbus/dbus_new.h" #include "rsn_supp/wpa.h" #include "fst/fst.h" @@ -27,13 +26,13 @@ int wpas_notify_supplicant_initialized(struct wpa_global *global) { -#ifdef CONFIG_DBUS +#ifdef CONFIG_CTRL_IFACE_DBUS_NEW if (global->params.dbus_ctrl_interface) { global->dbus = wpas_dbus_init(global); if (global->dbus == NULL) return -1; } -#endif /* CONFIG_DBUS */ +#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ #ifdef CONFIG_BINDER global->binder = wpas_binder_init(global); @@ -47,10 +46,10 @@ int wpas_notify_supplicant_initialized(struct wpa_global *global) void wpas_notify_supplicant_deinitialized(struct wpa_global *global) { -#ifdef CONFIG_DBUS +#ifdef CONFIG_CTRL_IFACE_DBUS_NEW if (global->dbus) wpas_dbus_deinit(global->dbus); -#endif /* CONFIG_DBUS */ +#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ #ifdef CONFIG_BINDER if (global->binder) @@ -64,9 +63,6 @@ int wpas_notify_iface_added(struct wpa_supplicant *wpa_s) if (wpa_s->p2p_mgmt) return 0; - if (wpas_dbus_register_iface(wpa_s)) - return -1; - if (wpas_dbus_register_interface(wpa_s)) return -1; @@ -79,9 +75,6 @@ void wpas_notify_iface_removed(struct wpa_supplicant *wpa_s) if (wpa_s->p2p_mgmt) return; - /* unregister interface in old DBus ctrl iface */ - wpas_dbus_unregister_iface(wpa_s); - /* unregister interface in new DBus ctrl iface */ wpas_dbus_unregister_interface(wpa_s); } @@ -94,10 +87,6 @@ void wpas_notify_state_changed(struct wpa_supplicant *wpa_s, if (wpa_s->p2p_mgmt) return; - /* notify the old DBus API */ - wpa_supplicant_dbus_notify_state_change(wpa_s, new_state, - old_state); - /* notify the new DBus API */ wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_STATE); @@ -267,9 +256,6 @@ void wpas_notify_scanning(struct wpa_supplicant *wpa_s) if (wpa_s->p2p_mgmt) return; - /* notify the old DBus API */ - wpa_supplicant_dbus_notify_scanning(wpa_s); - /* notify the new DBus API */ wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_SCANNING); } @@ -289,9 +275,6 @@ void wpas_notify_scan_results(struct wpa_supplicant *wpa_s) if (wpa_s->p2p_mgmt) return; - /* notify the old DBus API */ - wpa_supplicant_dbus_notify_scan_results(wpa_s); - wpas_wps_notify_scan_results(wpa_s); } @@ -303,8 +286,6 @@ void wpas_notify_wps_credential(struct wpa_supplicant *wpa_s, return; #ifdef CONFIG_WPS - /* notify the old DBus API */ - wpa_supplicant_dbus_notify_wps_cred(wpa_s, cred); /* notify the new DBus API */ wpas_dbus_signal_wps_cred(wpa_s, cred); #endif /* CONFIG_WPS */ @@ -838,9 +819,6 @@ void wpas_notify_certification(struct wpa_supplicant *wpa_s, int depth, "depth=%d %s", depth, altsubject[i]); } - /* notify the old DBus API */ - wpa_supplicant_dbus_notify_certification(wpa_s, depth, subject, - cert_hash, cert); /* notify the new DBus API */ wpas_dbus_signal_certification(wpa_s, depth, subject, altsubject, num_altsubject, cert_hash, cert); diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c index 848299d..e7c1f5d 100644 --- a/wpa_supplicant/p2p_supplicant.c +++ b/wpa_supplicant/p2p_supplicant.c @@ -980,6 +980,7 @@ static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, os_free(wpa_s->p2p_group_common_freqs); wpa_s->p2p_group_common_freqs = NULL; wpa_s->p2p_group_common_freqs_num = 0; + wpa_s->p2p_go_do_acs = 0; wpa_s->waiting_presence_resp = 0; @@ -1584,20 +1585,27 @@ static int wpas_send_action_work(struct wpa_supplicant *wpa_s, static int wpas_send_action(void *ctx, unsigned int freq, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *buf, - size_t len, unsigned int wait_time) + size_t len, unsigned int wait_time, int *scheduled) { struct wpa_supplicant *wpa_s = ctx; int listen_freq = -1, send_freq = -1; + if (scheduled) + *scheduled = 0; if (wpa_s->p2p_listen_work) listen_freq = wpa_s->p2p_listen_work->freq; if (wpa_s->p2p_send_action_work) send_freq = wpa_s->p2p_send_action_work->freq; if (listen_freq != (int) freq && send_freq != (int) freq) { - wpa_printf(MSG_DEBUG, "P2P: Schedule new radio work for Action frame TX (listen_freq=%d send_freq=%d)", - listen_freq, send_freq); - return wpas_send_action_work(wpa_s, freq, dst, src, bssid, buf, - len, wait_time); + int res; + + wpa_printf(MSG_DEBUG, "P2P: Schedule new radio work for Action frame TX (listen_freq=%d send_freq=%d freq=%u)", + listen_freq, send_freq, freq); + res = wpas_send_action_work(wpa_s, freq, dst, src, bssid, buf, + len, wait_time); + if (res == 0 && scheduled) + *scheduled = 1; + return res; } wpa_printf(MSG_DEBUG, "P2P: Use ongoing radio work for Action frame TX"); @@ -1649,7 +1657,7 @@ static void wpas_start_wps_enrollee(struct wpa_supplicant *wpa_s, wpa_supplicant_ap_deinit(wpa_s); wpas_copy_go_neg_results(wpa_s, res); if (res->wps_method == WPS_PBC) { - wpas_wps_start_pbc(wpa_s, res->peer_interface_addr, 1); + wpas_wps_start_pbc(wpa_s, res->peer_interface_addr, 1, 0); #ifdef CONFIG_WPS_NFC } else if (res->wps_method == WPS_NFC) { wpas_wps_start_nfc(wpa_s, res->peer_device_addr, @@ -4180,6 +4188,9 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, return; } + wpa_s->global->pending_p2ps_group = 0; + wpa_s->global->pending_p2ps_group_freq = 0; + if (conncap == P2PS_SETUP_GROUP_OWNER) { /* * We need to copy the interface name. Simply saving a @@ -4190,8 +4201,10 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, go_ifname[0] = '\0'; if (!go_wpa_s) { - wpa_s->global->pending_p2ps_group = 1; - wpa_s->global->pending_p2ps_group_freq = freq; + if (!response_done) { + wpa_s->global->pending_p2ps_group = 1; + wpa_s->global->pending_p2ps_group_freq = freq; + } if (!wpas_p2p_create_iface(wpa_s)) os_memcpy(go_ifname, wpa_s->ifname, @@ -4498,7 +4511,10 @@ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s) * channel. */ if (p2p_config_get_random_social(&p2p, &p2p.reg_class, - &p2p.channel) != 0) { + &p2p.channel, + &global->p2p_go_avoid_freq, + &global->p2p_disallow_freq) != + 0) { wpa_printf(MSG_INFO, "P2P: No social channels supported by the driver - do not enable P2P"); return 0; @@ -4523,10 +4539,14 @@ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s) * other preference is indicated. */ if (p2p_config_get_random_social(&p2p, &p2p.op_reg_class, - &p2p.op_channel) != 0) { - wpa_printf(MSG_ERROR, + &p2p.op_channel, NULL, + NULL) != 0) { + wpa_printf(MSG_INFO, "P2P: Failed to select random social channel as operation channel"); - return -1; + p2p.op_reg_class = 0; + p2p.op_channel = 0; + /* This will be overridden during group setup in + * p2p_prepare_channel(), so allow setup to continue. */ } p2p.cfg_op_channel = 0; wpa_printf(MSG_DEBUG, "P2P: Random operating channel: " @@ -7200,7 +7220,7 @@ void wpas_p2p_completed(struct wpa_supplicant *wpa_s) u8 go_dev_addr[ETH_ALEN]; int persistent; int freq; - u8 ip[3 * 4]; + u8 ip[3 * 4], *ip_ptr = NULL; char ip_addr[100]; if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION) { @@ -7247,6 +7267,7 @@ void wpas_p2p_completed(struct wpa_supplicant *wpa_s) ip[8], ip[9], ip[10], ip[11]); if (os_snprintf_error(sizeof(ip_addr), res)) ip_addr[0] = '\0'; + ip_ptr = ip; } wpas_p2p_group_started(wpa_s, 0, ssid, freq, @@ -7259,7 +7280,7 @@ void wpas_p2p_completed(struct wpa_supplicant *wpa_s) wpas_p2p_store_persistent_group(wpa_s->p2pdev, ssid, go_dev_addr); - wpas_notify_p2p_group_started(wpa_s, ssid, persistent, 1, ip); + wpas_notify_p2p_group_started(wpa_s, ssid, persistent, 1, ip_ptr); } diff --git a/wpa_supplicant/rrm.c b/wpa_supplicant/rrm.c index ab5e6db..cb3c6c9 100644 --- a/wpa_supplicant/rrm.c +++ b/wpa_supplicant/rrm.c @@ -397,7 +397,10 @@ static int wpas_rrm_beacon_rep_update_last_frame(u8 *pos, size_t len) struct rrm_measurement_report_element *msr_rep; u8 *end = pos + len; u8 *msr_rep_end; + struct rrm_measurement_beacon_report *rep = NULL; + u8 *subelem; + /* Find the last beacon report element */ while (end - pos >= (int) sizeof(*msr_rep)) { msr_rep = (struct rrm_measurement_report_element *) pos; msr_rep_end = pos + msr_rep->len + 2; @@ -410,30 +413,27 @@ static int wpas_rrm_beacon_rep_update_last_frame(u8 *pos, size_t len) return -1; } - if (msr_rep->type == MEASURE_TYPE_BEACON) { - struct rrm_measurement_beacon_report *rep; - u8 *subelem; - + if (msr_rep->type == MEASURE_TYPE_BEACON) rep = (struct rrm_measurement_beacon_report *) msr_rep->variable; - subelem = rep->variable; - while (subelem + 2 < msr_rep_end && - subelem[0] != - WLAN_BEACON_REPORT_SUBELEM_LAST_INDICATION) - subelem += 2 + subelem[1]; - - if (subelem + 2 < msr_rep_end && - subelem[0] == - WLAN_BEACON_REPORT_SUBELEM_LAST_INDICATION && - subelem[1] == 1 && - subelem + - BEACON_REPORT_LAST_INDICATION_SUBELEM_LEN <= end) - subelem[2] = 1; - } pos += pos[1] + 2; } + if (!rep) + return 0; + + subelem = rep->variable; + while (subelem + 2 < msr_rep_end && + subelem[0] != WLAN_BEACON_REPORT_SUBELEM_LAST_INDICATION) + subelem += 2 + subelem[1]; + + if (subelem + 2 < msr_rep_end && + subelem[0] == WLAN_BEACON_REPORT_SUBELEM_LAST_INDICATION && + subelem[1] == 1 && + subelem + BEACON_REPORT_LAST_INDICATION_SUBELEM_LEN <= end) + subelem[2] = 1; + return 0; } diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c index 727c49a..7abb028 100644 --- a/wpa_supplicant/scan.c +++ b/wpa_supplicant/scan.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - Scanning - * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -1993,7 +1993,8 @@ static int wpa_scan_result_compar(const void *a, const void *b) /* if SNR is close, decide by max rate or frequency band */ if (snr_a && snr_b && abs(snr_b - snr_a) < 7) { if (wa->est_throughput != wb->est_throughput) - return wb->est_throughput - wa->est_throughput; + return (int) wb->est_throughput - + (int) wa->est_throughput; } if ((snr_a && snr_b && abs(snr_b - snr_a) < 5) || (wa->qual && wb->qual && abs(wb->qual - wa->qual) < 10)) { diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index ba22a93..17a984d 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -16,6 +16,7 @@ #include "eapol_supp/eapol_supp_sm.h" #include "common/wpa_common.h" #include "common/sae.h" +#include "common/dpp.h" #include "rsn_supp/wpa.h" #include "rsn_supp/pmksa_cache.h" #include "config.h" @@ -57,7 +58,7 @@ static int index_within_array(const int *array, int idx) static int sme_set_sae_group(struct wpa_supplicant *wpa_s) { int *groups = wpa_s->conf->sae_groups; - int default_groups[] = { 19, 20, 21, 25, 26, 0 }; + int default_groups[] = { 19, 20, 21, 0 }; if (!groups || groups[0] <= 0) groups = default_groups; @@ -84,7 +85,8 @@ static int sme_set_sae_group(struct wpa_supplicant *wpa_s) static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, - const u8 *bssid, int external) + const u8 *bssid, int external, + int reuse) { struct wpabuf *buf; size_t len; @@ -96,8 +98,10 @@ static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s, buf = wpabuf_alloc(4 + wpabuf_len(wpa_s->sae_commit_override)); if (!buf) return NULL; - wpabuf_put_le16(buf, 1); /* Transaction seq# */ - wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); + if (!external) { + wpabuf_put_le16(buf, 1); /* Transaction seq# */ + wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); + } wpabuf_put_buf(buf, wpa_s->sae_commit_override); return buf; } @@ -111,6 +115,12 @@ static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s, return NULL; } + if (reuse && wpa_s->sme.sae.tmp && + os_memcmp(bssid, wpa_s->sme.sae.tmp->bssid, ETH_ALEN) == 0) { + wpa_printf(MSG_DEBUG, + "SAE: Reuse previously generated PWE on a retry with the same AP"); + goto reuse_data; + } if (sme_set_sae_group(wpa_s) < 0) { wpa_printf(MSG_DEBUG, "SAE: Failed to select group"); return NULL; @@ -123,7 +133,10 @@ static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s, wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE"); return NULL; } + if (wpa_s->sme.sae.tmp) + os_memcpy(wpa_s->sme.sae.tmp->bssid, bssid, ETH_ALEN); +reuse_data: len = wpa_s->sme.sae_token ? wpabuf_len(wpa_s->sme.sae_token) : 0; if (ssid->sae_password_id) len += 4 + os_strlen(ssid->sae_password_id); @@ -303,6 +316,12 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, if (!rsn) { wpa_dbg(wpa_s, MSG_DEBUG, "SAE enabled, but target BSS does not advertise RSN"); +#ifdef CONFIG_DPP + } else if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied) == 0 && + (ssid->key_mgmt & WPA_KEY_MGMT_DPP) && + (ied.key_mgmt & WPA_KEY_MGMT_DPP)) { + wpa_dbg(wpa_s, MSG_DEBUG, "Prefer DPP over SAE when both are enabled"); +#endif /* CONFIG_DPP */ } else if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied) == 0 && wpa_key_mgmt_sae(ied.key_mgmt)) { wpa_dbg(wpa_s, MSG_DEBUG, "Using SAE auth_alg"); @@ -435,13 +454,14 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, if (ie && ie[1] >= MOBILITY_DOMAIN_ID_LEN) md = ie + 2; wpa_sm_set_ft_params(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0); + if (md && (!wpa_key_mgmt_ft(ssid->key_mgmt) || + !wpa_key_mgmt_ft(wpa_s->key_mgmt))) + md = NULL; if (md) { /* Prepare for the next transition */ wpa_ft_prepare_auth_request(wpa_s->wpa, ie); } - if (md && !wpa_key_mgmt_ft(ssid->key_mgmt)) - md = NULL; if (md) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: FT mobility domain %02x%02x", md[0], md[1]); @@ -620,7 +640,10 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, #ifdef CONFIG_SAE if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE && pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, ssid, 0, - NULL, WPA_KEY_MGMT_SAE) == 0) { + NULL, + wpa_s->key_mgmt == WPA_KEY_MGMT_FT_SAE ? + WPA_KEY_MGMT_FT_SAE : + WPA_KEY_MGMT_SAE) == 0) { wpa_dbg(wpa_s, MSG_DEBUG, "PMKSA cache entry found - try to use PMKSA caching instead of new SAE authentication"); wpa_sm_set_pmk_from_pmksa(wpa_s->wpa); @@ -631,7 +654,8 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE) { if (start) resp = sme_auth_build_sae_commit(wpa_s, ssid, - bss->bssid, 0); + bss->bssid, 0, + start == 2); else resp = sme_auth_build_sae_confirm(wpa_s, 0); if (resp == NULL) { @@ -914,7 +938,7 @@ static void sme_external_auth_send_sae_commit(struct wpa_supplicant *wpa_s, { struct wpabuf *resp, *buf; - resp = sme_auth_build_sae_commit(wpa_s, ssid, bssid, 1); + resp = sme_auth_build_sae_commit(wpa_s, ssid, bssid, 1, 0); if (!resp) return; @@ -941,10 +965,9 @@ static void sme_send_external_auth_status(struct wpa_supplicant *wpa_s, os_memset(¶ms, 0, sizeof(params)); params.status = status; - os_memcpy(params.ssid, wpa_s->sme.ext_auth.ssid, - wpa_s->sme.ext_auth.ssid_len); + params.ssid = wpa_s->sme.ext_auth.ssid; params.ssid_len = wpa_s->sme.ext_auth.ssid_len; - os_memcpy(params.bssid, wpa_s->sme.ext_auth.bssid, ETH_ALEN); + params.bssid = wpa_s->sme.ext_auth.bssid; wpa_drv_send_external_auth_status(wpa_s, ¶ms); } @@ -954,14 +977,14 @@ static void sme_handle_external_auth_start(struct wpa_supplicant *wpa_s, { struct wpa_ssid *ssid; size_t ssid_str_len = data->external_auth.ssid_len; - u8 *ssid_str = data->external_auth.ssid; + const u8 *ssid_str = data->external_auth.ssid; /* Get the SSID conf from the ssid string obtained */ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { if (!wpas_network_disabled(wpa_s, ssid) && ssid_str_len == ssid->ssid_len && os_memcmp(ssid_str, ssid->ssid, ssid_str_len) == 0 && - (ssid->key_mgmt & WPA_KEY_MGMT_SAE)) + (ssid->key_mgmt & (WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE))) break; } if (ssid) @@ -1037,7 +1060,7 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction, status_code == WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ && wpa_s->sme.sae.state == SAE_COMMITTED && (external || wpa_s->current_bss) && wpa_s->current_ssid) { - int default_groups[] = { 19, 20, 21, 25, 26, 0 }; + int default_groups[] = { 19, 20, 21, 0 }; u16 group; groups = wpa_s->conf->sae_groups; @@ -1065,7 +1088,7 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction, len - sizeof(le16)); if (!external) sme_send_authentication(wpa_s, wpa_s->current_bss, - wpa_s->current_ssid, 1); + wpa_s->current_ssid, 2); else sme_external_auth_send_sae_commit( wpa_s, wpa_s->sme.ext_auth.bssid, @@ -1554,6 +1577,36 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode, } #endif /* CONFIG_OWE */ +#ifdef CONFIG_DPP2 + if (wpa_s->key_mgmt == WPA_KEY_MGMT_DPP && wpa_s->current_ssid && + wpa_s->current_ssid->dpp_netaccesskey) { + struct wpa_ssid *ssid = wpa_s->current_ssid; + + dpp_pfs_free(wpa_s->dpp_pfs); + wpa_s->dpp_pfs = dpp_pfs_init(ssid->dpp_netaccesskey, + ssid->dpp_netaccesskey_len); + if (!wpa_s->dpp_pfs) { + wpa_printf(MSG_DEBUG, "DPP: Could not initialize PFS"); + /* Try to continue without PFS */ + goto pfs_fail; + } + if (wpa_s->sme.assoc_req_ie_len + + wpabuf_len(wpa_s->dpp_pfs->ie) > + sizeof(wpa_s->sme.assoc_req_ie)) { + wpa_printf(MSG_ERROR, + "DPP: Not enough buffer room for own Association Request frame elements"); + dpp_pfs_free(wpa_s->dpp_pfs); + wpa_s->dpp_pfs = NULL; + goto pfs_fail; + } + os_memcpy(wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len, + wpabuf_head(wpa_s->dpp_pfs->ie), + wpabuf_len(wpa_s->dpp_pfs->ie)); + wpa_s->sme.assoc_req_ie_len += wpabuf_len(wpa_s->dpp_pfs->ie); + } +pfs_fail: +#endif /* CONFIG_DPP2 */ + if (wpa_s->current_ssid && wpa_s->current_ssid->multi_ap_backhaul_sta) { size_t multi_ap_ie_len; @@ -1932,17 +1985,14 @@ void sme_clear_on_disassoc(struct wpa_supplicant *wpa_s) if (wpa_s->sme.ft_ies || wpa_s->sme.ft_used) sme_update_ft_ies(wpa_s, NULL, NULL, 0); #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + sme_stop_sa_query(wpa_s); +#endif /* CONFIG_IEEE80211W */ } void sme_deinit(struct wpa_supplicant *wpa_s) { - os_free(wpa_s->sme.ft_ies); - wpa_s->sme.ft_ies = NULL; - wpa_s->sme.ft_ies_len = 0; -#ifdef CONFIG_IEEE80211W - sme_stop_sa_query(wpa_s); -#endif /* CONFIG_IEEE80211W */ sme_clear_on_disassoc(wpa_s); eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL); @@ -2347,6 +2397,8 @@ static void sme_start_sa_query(struct wpa_supplicant *wpa_s) static void sme_stop_sa_query(struct wpa_supplicant *wpa_s) { + if (wpa_s->sme.sa_query_trans_id) + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Stop SA Query"); eloop_cancel_timeout(sme_sa_query_timer, wpa_s, NULL); os_free(wpa_s->sme.sa_query_trans_id); wpa_s->sme.sa_query_trans_id = NULL; diff --git a/wpa_supplicant/systemd/wpa_supplicant.service.in b/wpa_supplicant/systemd/wpa_supplicant.service.in index bc5d49a..75a37a8 100644 --- a/wpa_supplicant/systemd/wpa_supplicant.service.in +++ b/wpa_supplicant/systemd/wpa_supplicant.service.in @@ -5,9 +5,9 @@ Wants=network.target [Service] Type=dbus -BusName=@DBUS_INTERFACE@ +BusName=fi.w1.wpa_supplicant1 ExecStart=@BINDIR@/wpa_supplicant -u [Install] WantedBy=multi-user.target -Alias=dbus-@DBUS_INTERFACE@.service +Alias=dbus-fi.w1.wpa_supplicant1.service diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c index 9881021..695fcbe 100644 --- a/wpa_supplicant/wpa_cli.c +++ b/wpa_supplicant/wpa_cli.c @@ -1411,9 +1411,11 @@ static const char *network_fields[] = { "eap", "identity", "anonymous_identity", "password", "ca_cert", "ca_path", "client_cert", "private_key", "private_key_passwd", "dh_file", "subject_match", "altsubject_match", + "check_cert_subject", "domain_suffix_match", "domain_match", "ca_cert2", "ca_path2", "client_cert2", "private_key2", "private_key2_passwd", "dh_file2", "subject_match2", "altsubject_match2", + "check_cert_subject2", "domain_suffix_match2", "domain_match2", "phase1", "phase2", "pcsc", "pin", "engine_id", "key_id", "cert_id", "ca_cert_id", "pin2", "engine2_id", "key2_id", "cert2_id", "ca_cert2_id", diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index e0ee553..96a3691 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -39,6 +39,7 @@ #include "common/ieee802_11_defs.h" #include "common/hw_features_common.h" #include "common/gas_server.h" +#include "common/dpp.h" #include "p2p/p2p.h" #include "fst/fst.h" #include "blacklist.h" @@ -672,6 +673,8 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) #ifdef CONFIG_DPP wpas_dpp_deinit(wpa_s); + dpp_global_deinit(wpa_s->dpp); + wpa_s->dpp = NULL; #endif /* CONFIG_DPP */ } @@ -958,7 +961,7 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, wpa_supplicant_stop_bgscan(wpa_s); #endif /* CONFIG_BGSCAN */ - if (state == WPA_AUTHENTICATING) + if (state > WPA_SCANNING) wpa_supplicant_stop_autoscan(wpa_s); if (state == WPA_DISCONNECTED || state == WPA_INACTIVE) @@ -1189,6 +1192,18 @@ static int wpa_supplicant_suites_from_ai(struct wpa_supplicant *wpa_s, } +static int matching_ciphers(struct wpa_ssid *ssid, struct wpa_ie_data *ie, + int freq) +{ + if (!ie->has_group) + ie->group_cipher = wpa_default_rsn_cipher(freq); + if (!ie->has_pairwise) + ie->pairwise_cipher = wpa_default_rsn_cipher(freq); + return (ie->group_cipher & ssid->group_cipher) && + (ie->pairwise_cipher & ssid->pairwise_cipher); +} + + /** * wpa_supplicant_set_suites - Set authentication and encryption parameters * @wpa_s: Pointer to wpa_supplicant data @@ -1220,8 +1235,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, if (bss_rsn && (ssid->proto & WPA_PROTO_RSN) && wpa_parse_wpa_ie(bss_rsn, 2 + bss_rsn[1], &ie) == 0 && - (ie.group_cipher & ssid->group_cipher) && - (ie.pairwise_cipher & ssid->pairwise_cipher) && + matching_ciphers(ssid, &ie, bss->freq) && (ie.key_mgmt & ssid->key_mgmt)) { wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using IEEE 802.11i/D9.0"); proto = WPA_PROTO_RSN; @@ -1361,6 +1375,9 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, wpa_s->pairwise_cipher = WPA_CIPHER_NONE; #else /* CONFIG_NO_WPA */ sel = ie.group_cipher & ssid->group_cipher; + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: AP group 0x%x network profile group 0x%x; available group 0x%x", + ie.group_cipher, ssid->group_cipher, sel); wpa_s->group_cipher = wpa_pick_group_cipher(sel); if (wpa_s->group_cipher < 0) { wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select group " @@ -1371,6 +1388,9 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, wpa_cipher_txt(wpa_s->group_cipher)); sel = ie.pairwise_cipher & ssid->pairwise_cipher; + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: AP pairwise 0x%x network profile pairwise 0x%x; available pairwise 0x%x", + ie.pairwise_cipher, ssid->pairwise_cipher, sel); wpa_s->pairwise_cipher = wpa_pick_pairwise_cipher(sel, 1); if (wpa_s->pairwise_cipher < 0) { wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select pairwise " @@ -1382,11 +1402,29 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, #endif /* CONFIG_NO_WPA */ sel = ie.key_mgmt & ssid->key_mgmt; + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: AP key_mgmt 0x%x network profile key_mgmt 0x%x; available key_mgmt 0x%x", + ie.key_mgmt, ssid->key_mgmt, sel); #ifdef CONFIG_SAE if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE)) sel &= ~(WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE); #endif /* CONFIG_SAE */ if (0) { +#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_SHA384 + } else if (sel & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) { + wpa_s->key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X_SHA384; + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: using KEY_MGMT FT/802.1X-SHA384"); + if (pmksa_cache_get_current(wpa_s->wpa)) { + /* PMKSA caching with FT is not fully functional, so + * disable the case for now. */ + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: Disable PMKSA caching for FT/802.1X connection"); + pmksa_cache_clear_current(wpa_s->wpa); + } +#endif /* CONFIG_SHA384 */ +#endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_SUITEB192 } else if (sel & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) { wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B_192; @@ -1416,19 +1454,6 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FILS-SHA256"); #endif /* CONFIG_FILS */ #ifdef CONFIG_IEEE80211R -#ifdef CONFIG_SHA384 - } else if (sel & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) { - wpa_s->key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X_SHA384; - wpa_dbg(wpa_s, MSG_DEBUG, - "WPA: using KEY_MGMT FT/802.1X-SHA384"); - if (pmksa_cache_get_current(wpa_s->wpa)) { - /* PMKSA caching with FT is not fully functional, so - * disable the case for now. */ - wpa_dbg(wpa_s, MSG_DEBUG, - "WPA: Disable PMKSA caching for FT/802.1X connection"); - pmksa_cache_clear_current(wpa_s->wpa); - } -#endif /* CONFIG_SHA384 */ } else if (sel & WPA_KEY_MGMT_FT_IEEE8021X) { wpa_s->key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/802.1X"); @@ -1439,18 +1464,25 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, "WPA: Disable PMKSA caching for FT/802.1X connection"); pmksa_cache_clear_current(wpa_s->wpa); } - } else if (sel & WPA_KEY_MGMT_FT_PSK) { - wpa_s->key_mgmt = WPA_KEY_MGMT_FT_PSK; - wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/PSK"); #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_DPP + } else if (sel & WPA_KEY_MGMT_DPP) { + wpa_s->key_mgmt = WPA_KEY_MGMT_DPP; + wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT DPP"); +#endif /* CONFIG_DPP */ #ifdef CONFIG_SAE - } else if (sel & WPA_KEY_MGMT_SAE) { - wpa_s->key_mgmt = WPA_KEY_MGMT_SAE; - wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT SAE"); } else if (sel & WPA_KEY_MGMT_FT_SAE) { wpa_s->key_mgmt = WPA_KEY_MGMT_FT_SAE; wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT FT/SAE"); + } else if (sel & WPA_KEY_MGMT_SAE) { + wpa_s->key_mgmt = WPA_KEY_MGMT_SAE; + wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT SAE"); #endif /* CONFIG_SAE */ +#ifdef CONFIG_IEEE80211R + } else if (sel & WPA_KEY_MGMT_FT_PSK) { + wpa_s->key_mgmt = WPA_KEY_MGMT_FT_PSK; + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/PSK"); +#endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211W } else if (sel & WPA_KEY_MGMT_IEEE8021X_SHA256) { wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256; @@ -1480,11 +1512,6 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, wpa_s->key_mgmt = WPA_KEY_MGMT_OWE; wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT OWE"); #endif /* CONFIG_OWE */ -#ifdef CONFIG_DPP - } else if (sel & WPA_KEY_MGMT_DPP) { - wpa_s->key_mgmt = WPA_KEY_MGMT_DPP; - wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT DPP"); -#endif /* CONFIG_DPP */ } else { wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select " "authenticated key management type"); @@ -1503,6 +1530,9 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, if (wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION || !(ie.capabilities & WPA_CAPABILITY_MFPC)) sel = 0; + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: AP mgmt_group_cipher 0x%x network profile mgmt_group_cipher 0x%x; available mgmt_group_cipher 0x%x", + ie.mgmt_group_cipher, ssid->group_mgmt_cipher, sel); if (sel & WPA_CIPHER_AES_128_CMAC) { wpa_s->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher " @@ -1537,7 +1567,13 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, return -1; } - if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) { + if (0) { +#ifdef CONFIG_DPP + } else if (wpa_s->key_mgmt == WPA_KEY_MGMT_DPP) { + /* Use PMK from DPP network introduction (PMKSA entry) */ + wpa_sm_set_pmk_from_pmksa(wpa_s->wpa); +#endif /* CONFIG_DPP */ + } else if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) { int psk_set = 0; int sae_only; @@ -2485,6 +2521,9 @@ static u8 * wpas_populate_assoc_ies( #ifdef CONFIG_MBO const u8 *mbo_ie; #endif +#ifdef CONFIG_SAE + int sae_pmksa_cached = 0; +#endif /* CONFIG_SAE */ #ifdef CONFIG_FILS const u8 *realm, *username, *rrk; size_t realm_len, username_len, rrk_len; @@ -2522,8 +2561,12 @@ static u8 * wpas_populate_assoc_ies( #endif /* CONFIG_FILS */ if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, ssid, try_opportunistic, - cache_id, 0) == 0) + cache_id, 0) == 0) { eapol_sm_notify_pmkid_attempt(wpa_s->eapol); +#ifdef CONFIG_SAE + sae_pmksa_cached = 1; +#endif /* CONFIG_SAE */ + } wpa_ie_len = max_wpa_ie_len; if (wpa_supplicant_set_suites(wpa_s, bss, ssid, wpa_ie, &wpa_ie_len)) { @@ -2636,6 +2679,14 @@ static u8 * wpas_populate_assoc_ies( "Overriding auth_alg selection: 0x%x", algs); } +#ifdef CONFIG_SAE + if (sae_pmksa_cached && algs == WPA_AUTH_ALG_SAE) { + wpa_dbg(wpa_s, MSG_DEBUG, + "SAE: Use WPA_AUTH_ALG_OPEN for PMKSA caching attempt"); + algs = WPA_AUTH_ALG_OPEN; + } +#endif /* CONFIG_SAE */ + #ifdef CONFIG_P2P if (wpa_s->global->p2p) { u8 *pos; @@ -2810,11 +2861,33 @@ static u8 * wpas_populate_assoc_ies( os_memcpy(wpa_ie + wpa_ie_len, wpabuf_head(owe_ie), wpabuf_len(owe_ie)); wpa_ie_len += wpabuf_len(owe_ie); - wpabuf_free(owe_ie); } + wpabuf_free(owe_ie); } #endif /* CONFIG_OWE */ +#ifdef CONFIG_DPP2 + if (wpa_sm_get_key_mgmt(wpa_s->wpa) == WPA_KEY_MGMT_DPP && + ssid->dpp_netaccesskey) { + dpp_pfs_free(wpa_s->dpp_pfs); + wpa_s->dpp_pfs = dpp_pfs_init(ssid->dpp_netaccesskey, + ssid->dpp_netaccesskey_len); + if (!wpa_s->dpp_pfs) { + wpa_printf(MSG_DEBUG, "DPP: Could not initialize PFS"); + /* Try to continue without PFS */ + goto pfs_fail; + } + if (wpabuf_len(wpa_s->dpp_pfs->ie) <= + max_wpa_ie_len - wpa_ie_len) { + os_memcpy(wpa_ie + wpa_ie_len, + wpabuf_head(wpa_s->dpp_pfs->ie), + wpabuf_len(wpa_s->dpp_pfs->ie)); + wpa_ie_len += wpabuf_len(wpa_s->dpp_pfs->ie); + } + } +pfs_fail: +#endif /* CONFIG_DPP2 */ + #ifdef CONFIG_IEEE80211R /* * Add MDIE under these conditions: the network profile allows FT, @@ -2901,6 +2974,34 @@ static void wpas_update_fils_connect_params(struct wpa_supplicant *wpa_s) #endif /* CONFIG_FILS && IEEE8021X_EAPOL */ +#ifdef CONFIG_MBO +void wpas_update_mbo_connect_params(struct wpa_supplicant *wpa_s) +{ + struct wpa_driver_associate_params params; + u8 *wpa_ie; + + /* + * Update MBO connect params only in case of change of MBO attributes + * when connected, if the AP support MBO. + */ + + if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid || + !wpa_s->current_bss || + !wpa_bss_get_vendor_ie(wpa_s->current_bss, MBO_IE_VENDOR_TYPE)) + return; + + os_memset(¶ms, 0, sizeof(params)); + wpa_ie = wpas_populate_assoc_ies(wpa_s, wpa_s->current_bss, + wpa_s->current_ssid, ¶ms, NULL); + if (!wpa_ie) + return; + + wpa_drv_update_connect_params(wpa_s, ¶ms, WPA_DRV_UPDATE_ASSOC_IES); + os_free(wpa_ie); +} +#endif /* CONFIG_MBO */ + + static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) { struct wpa_connect_work *cwork = work->ctx; @@ -3942,7 +4043,9 @@ struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s) while (entry) { if (!wpas_network_disabled(wpa_s, entry) && ((ssid_len == entry->ssid_len && - os_memcmp(ssid, entry->ssid, ssid_len) == 0) || wired) && + (!entry->ssid || + os_memcmp(ssid, entry->ssid, ssid_len) == 0)) || + wired) && (!entry->bssid_set || os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0)) return entry; @@ -4428,11 +4531,11 @@ static int wpa_disable_max_amsdu(struct wpa_supplicant *wpa_s, { le16 msk; - wpa_msg(wpa_s, MSG_DEBUG, "set_disable_max_amsdu: %d", disabled); - if (disabled == -1) return 0; + wpa_msg(wpa_s, MSG_DEBUG, "set_disable_max_amsdu: %d", disabled); + msk = host_to_le16(HT_CAP_INFO_MAX_AMSDU_SIZE); htcaps_mask->ht_capabilities_info |= msk; if (disabled) @@ -4449,11 +4552,11 @@ static int wpa_set_ampdu_factor(struct wpa_supplicant *wpa_s, struct ieee80211_ht_capabilities *htcaps_mask, int factor) { - wpa_msg(wpa_s, MSG_DEBUG, "set_ampdu_factor: %d", factor); - if (factor == -1) return 0; + wpa_msg(wpa_s, MSG_DEBUG, "set_ampdu_factor: %d", factor); + if (factor < 0 || factor > 3) { wpa_msg(wpa_s, MSG_ERROR, "ampdu_factor: %d out of range. " "Must be 0-3 or -1", factor); @@ -4473,11 +4576,11 @@ static int wpa_set_ampdu_density(struct wpa_supplicant *wpa_s, struct ieee80211_ht_capabilities *htcaps_mask, int density) { - wpa_msg(wpa_s, MSG_DEBUG, "set_ampdu_density: %d", density); - if (density == -1) return 0; + wpa_msg(wpa_s, MSG_DEBUG, "set_ampdu_density: %d", density); + if (density < 0 || density > 7) { wpa_msg(wpa_s, MSG_ERROR, "ampdu_density: %d out of range. Must be 0-7 or -1.", @@ -4498,7 +4601,8 @@ static int wpa_set_disable_ht40(struct wpa_supplicant *wpa_s, struct ieee80211_ht_capabilities *htcaps_mask, int disabled) { - wpa_msg(wpa_s, MSG_DEBUG, "set_disable_ht40: %d", disabled); + if (disabled) + wpa_msg(wpa_s, MSG_DEBUG, "set_disable_ht40: %d", disabled); set_disable_ht40(htcaps, disabled); set_disable_ht40(htcaps_mask, 0); @@ -4516,7 +4620,8 @@ static int wpa_set_disable_sgi(struct wpa_supplicant *wpa_s, le16 msk = host_to_le16(HT_CAP_INFO_SHORT_GI20MHZ | HT_CAP_INFO_SHORT_GI40MHZ); - wpa_msg(wpa_s, MSG_DEBUG, "set_disable_sgi: %d", disabled); + if (disabled) + wpa_msg(wpa_s, MSG_DEBUG, "set_disable_sgi: %d", disabled); if (disabled) htcaps->ht_capabilities_info &= ~msk; @@ -4537,7 +4642,8 @@ static int wpa_set_disable_ldpc(struct wpa_supplicant *wpa_s, /* Masking these out disables LDPC */ le16 msk = host_to_le16(HT_CAP_INFO_LDPC_CODING_CAP); - wpa_msg(wpa_s, MSG_DEBUG, "set_disable_ldpc: %d", disabled); + if (disabled) + wpa_msg(wpa_s, MSG_DEBUG, "set_disable_ldpc: %d", disabled); if (disabled) htcaps->ht_capabilities_info &= ~msk; @@ -4557,11 +4663,11 @@ static int wpa_set_tx_stbc(struct wpa_supplicant *wpa_s, { le16 msk = host_to_le16(HT_CAP_INFO_TX_STBC); - wpa_msg(wpa_s, MSG_DEBUG, "set_tx_stbc: %d", tx_stbc); - if (tx_stbc == -1) return 0; + wpa_msg(wpa_s, MSG_DEBUG, "set_tx_stbc: %d", tx_stbc); + if (tx_stbc < 0 || tx_stbc > 1) { wpa_msg(wpa_s, MSG_ERROR, "tx_stbc: %d out of range. Must be 0-1 or -1", tx_stbc); @@ -4583,11 +4689,11 @@ static int wpa_set_rx_stbc(struct wpa_supplicant *wpa_s, { le16 msk = host_to_le16(HT_CAP_INFO_RX_STBC_MASK); - wpa_msg(wpa_s, MSG_DEBUG, "set_rx_stbc: %d", rx_stbc); - if (rx_stbc == -1) return 0; + wpa_msg(wpa_s, MSG_DEBUG, "set_rx_stbc: %d", rx_stbc); + if (rx_stbc < 0 || rx_stbc > 3) { wpa_msg(wpa_s, MSG_ERROR, "rx_stbc: %d out of range. Must be 0-3 or -1", rx_stbc); @@ -6674,6 +6780,9 @@ void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid) * TODO: if more than one possible AP is available in scan results, * could try the other ones before requesting a new scan. */ + + /* speed up the connection attempt with normal scan */ + wpa_s->normal_scans = 0; wpa_supplicant_req_scan(wpa_s, timeout / 1000, 1000 * (timeout % 1000)); } @@ -7059,6 +7168,8 @@ void wpas_request_disconnection(struct wpa_supplicant *wpa_s) wpa_supplicant_cancel_scan(wpa_s); wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL); + radio_remove_works(wpa_s, "connect", 0); + radio_remove_works(wpa_s, "sme-connect", 0); } diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf index 1bd43b2..a9205f0 100644 --- a/wpa_supplicant/wpa_supplicant.conf +++ b/wpa_supplicant/wpa_supplicant.conf @@ -282,6 +282,14 @@ fast_reauth=1 # to external program(s) #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 +# station gets configured in WPA3-Personal transition mode (supports both +# WPA2-Personal (PSK) and WPA3-Personal (SAE) APs). +#wps_cred_add_sae=0 + # Vendor attribute in WPS M1, e.g., Windows 7 Vertical Pairing # The vendor attribute contents to be added in M1 (hex string) #wps_vendor_ext_m1=000137100100020001 @@ -310,6 +318,15 @@ fast_reauth=1 # of APs when using ap_scan=1 mode. #bss_max_count=200 +# BSS expiration age in seconds. A BSS will be removed from the local cache +# if it is not in use and has not been seen for this time. Default is 180. +#bss_expiration_age=180 + +# BSS expiration after number of scans. A BSS will be removed from the local +# cache if it is not seen in this number of scans. +# Default is 2. +#bss_expiration_scan_count=2 + # Automatic scan # This is an optional set of parameters for automatic scanning # within an interface in following format: @@ -377,11 +394,16 @@ fast_reauth=1 # Enabled SAE finite cyclic groups in preference order # By default (if this parameter is not set), the mandatory group 19 (ECC group -# defined over a 256-bit prime order field) is preferred, but other groups are -# also enabled. If this parameter is set, the groups will be tried in the -# indicated order. The group values are listed in the IANA registry: +# defined over a 256-bit prime order field, NIST P-256) is preferred and groups +# 20 (NIST P-384) and 21 (NIST P-521) are also enabled. If this parameter is +# set, the groups will be tried in the indicated order. +# The group values are listed in the IANA registry: # http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xml#ipsec-registry-9 -#sae_groups=21 20 19 26 25 +# 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 # Default value for DTIM period (if not overridden in network block) #dtim_period=2 @@ -1167,6 +1189,12 @@ fast_reauth=1 # certificate may include additional sub-level labels in addition to the # required labels. # +# More than one match string can be provided by using semicolons to +# separate the strings (e.g., example.org;example.com). When multiple +# strings are specified, a match with any one of the values is considered +# a sufficient match for the certificate, i.e., the conditions are ORed +# together. +# # For example, domain_suffix_match=example.com would match # test.example.com but would not match test-example.com. # domain_match: Constraint for server domain name @@ -1179,6 +1207,12 @@ fast_reauth=1 # no subdomains or wildcard matches are allowed. Case-insensitive # comparison is used, so "Example.com" matches "example.com", but would # not match "test.Example.com". +# +# More than one match string can be provided by using semicolons to +# separate the strings (e.g., example.org;example.com). When multiple +# strings are specified, a match with any one of the values is considered +# a sufficient match for the certificate, i.e., the conditions are ORed +# together. # phase1: Phase1 (outer authentication, i.e., TLS tunnel) parameters # (string with field-value pairs, e.g., "peapver=0" or # "peapver=1 peaplabel=1") diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index ee581e7..16e4db6 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -504,9 +504,6 @@ struct wpa_supplicant { #ifdef CONFIG_MATCH_IFACE int matched; #endif /* CONFIG_MATCH_IFACE */ -#ifdef CONFIG_CTRL_IFACE_DBUS - char *dbus_path; -#endif /* CONFIG_CTRL_IFACE_DBUS */ #ifdef CONFIG_CTRL_IFACE_DBUS_NEW char *dbus_new_path; char *dbus_groupobj_path; @@ -751,6 +748,10 @@ struct wpa_supplicant { unsigned int wnmsleep_used:1; unsigned int owe_transition_select:1; unsigned int owe_transition_search:1; + unsigned int connection_set:1; + unsigned int connection_ht:1; + unsigned int connection_vht:1; + unsigned int connection_he:1; struct os_reltime last_mac_addr_change; int last_mac_addr_style; @@ -1206,9 +1207,7 @@ struct wpa_supplicant { int last_auth_timeout_sec; #ifdef CONFIG_DPP - struct dl_list dpp_bootstrap; /* struct dpp_bootstrap_info */ - struct dl_list dpp_configurator; /* struct dpp_configurator */ - int dpp_init_done; + struct dpp_global *dpp; struct dpp_authentication *dpp_auth; struct wpa_radio_work *dpp_listen_work; unsigned int dpp_pending_listen_freq; @@ -1235,6 +1234,9 @@ struct wpa_supplicant { unsigned int dpp_resp_wait_time; unsigned int dpp_resp_max_tries; unsigned int dpp_resp_retry_time; +#ifdef CONFIG_DPP2 + struct dpp_pfs *dpp_pfs; +#endif /* CONFIG_DPP2 */ #ifdef CONFIG_TESTING_OPTIONS char *dpp_config_obj_override; char *dpp_discovery_override; @@ -1400,6 +1402,7 @@ struct wpabuf * mbo_build_anqp_buf(struct wpa_supplicant *wpa_s, void mbo_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, const u8 *sa, const u8 *data, size_t slen); +void wpas_update_mbo_connect_params(struct wpa_supplicant *wpa_s); /* op_classes.c */ enum chan_allowed { diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c index 1a2677b..0579274 100644 --- a/wpa_supplicant/wps_supplicant.c +++ b/wpa_supplicant/wps_supplicant.c @@ -530,11 +530,18 @@ static int wpa_supplicant_wps_cred(void *ctx, case WPS_AUTH_WPA2PSK: ssid->auth_alg = WPA_AUTH_ALG_OPEN; ssid->key_mgmt = WPA_KEY_MGMT_PSK; + if (wpa_s->conf->wps_cred_add_sae && + cred->key_len != 2 * PMK_LEN) { + ssid->key_mgmt |= WPA_KEY_MGMT_SAE; +#ifdef CONFIG_IEEE80211W + ssid->ieee80211w = MGMT_FRAME_PROTECTION_OPTIONAL; +#endif /* CONFIG_IEEE80211W */ + } ssid->proto = WPA_PROTO_RSN; break; } - if (ssid->key_mgmt == WPA_KEY_MGMT_PSK) { + if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) { if (cred->key_len == 2 * PMK_LEN) { if (hexstr2bin((const char *) cred->key, ssid->psk, PMK_LEN)) { @@ -1137,9 +1144,10 @@ static void wpas_wps_reassoc(struct wpa_supplicant *wpa_s, int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid, - int p2p_group) + int p2p_group, int multi_ap_backhaul_sta) { struct wpa_ssid *ssid; + char phase1[32]; #ifdef CONFIG_AP if (wpa_s->ap_iface) { @@ -1177,10 +1185,14 @@ int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid, } } #endif /* CONFIG_P2P */ - if (wpa_config_set(ssid, "phase1", "\"pbc=1\"", 0) < 0) + os_snprintf(phase1, sizeof(phase1), "pbc=1%s", + multi_ap_backhaul_sta ? " multi_ap=1" : ""); + if (wpa_config_set_quoted(ssid, "phase1", phase1) < 0) return -1; if (wpa_s->wps_fragment_size) ssid->eap.fragment_size = wpa_s->wps_fragment_size; + if (multi_ap_backhaul_sta) + ssid->multi_ap_backhaul_sta = 1; wpa_supplicant_wps_event(wpa_s, WPS_EV_PBC_ACTIVE, NULL); eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout, wpa_s, NULL); diff --git a/wpa_supplicant/wps_supplicant.h b/wpa_supplicant/wps_supplicant.h index c8fe47e..0fbc851 100644 --- a/wpa_supplicant/wps_supplicant.h +++ b/wpa_supplicant/wps_supplicant.h @@ -30,7 +30,7 @@ void wpas_wps_deinit(struct wpa_supplicant *wpa_s); int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s); enum wps_request_type wpas_wps_get_req_type(struct wpa_ssid *ssid); int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid, - int p2p_group); + int p2p_group, int multi_ap_backhaul_sta); int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, const char *pin, int p2p_group, u16 dev_pw_id); void wpas_wps_pbc_overlap(struct wpa_supplicant *wpa_s); |