diff --git a/contrib/wpa/CONTRIBUTIONS b/contrib/wpa/CONTRIBUTIONS index ca09bae6e15d..76600bc87280 100644 --- a/contrib/wpa/CONTRIBUTIONS +++ b/contrib/wpa/CONTRIBUTIONS @@ -29,6 +29,34 @@ using your real name. Pseudonyms or anonymous contributions cannot unfortunately be accepted. +The preferred method of submitting the contribution to the project is by +email to the hostap mailing list: +hostap@lists.infradead.org +Note that the list may require subscription before accepting message +without moderation. You can subscribe to the list at this address: +http://lists.infradead.org/mailman/listinfo/hostap + +The message should contain an inlined patch against the current +development branch (i.e., the master branch of +git://w1.fi/hostap.git). Please make sure the software you use for +sending the patch does not corrupt whitespace. If that cannot be fixed +for some reason, it is better to include an attached version of the +patch file than just send a whitespace damaged version in the message +body. + +The patches should be separate logical changes rather than doing +everything in a single patch. In other words, please keep cleanup, new +features, and bug fixes all in their own patches. Each patch needs a +commit log that describes the changes (what the changes fix, what +functionality is added, why the changes are useful, etc.). + +Please try to follow the coding style used in the project. + +In general, the best way of generating a suitable formatted patch file +is by committing the changes to a cloned git repository and using git +format-patch. The patch can then be sent, e.g., with git send-email. + + History of license and contributions terms ------------------------------------------ @@ -112,7 +140,7 @@ The license terms used for hostap.git files Modified BSD license (no advertisement clause): -Copyright (c) 2002-2015, Jouni Malinen and contributors +Copyright (c) 2002-2016, Jouni Malinen and contributors All Rights Reserved. Redistribution and use in source and binary forms, with or without diff --git a/contrib/wpa/COPYING b/contrib/wpa/COPYING index 5962e2fca0bc..7efce0dee1a7 100644 --- a/contrib/wpa/COPYING +++ b/contrib/wpa/COPYING @@ -1,7 +1,7 @@ wpa_supplicant and hostapd -------------------------- -Copyright (c) 2002-2015, Jouni Malinen and contributors +Copyright (c) 2002-2016, Jouni Malinen and contributors All Rights Reserved. diff --git a/contrib/wpa/README b/contrib/wpa/README index 07d1d25db777..9685f586beb7 100644 --- a/contrib/wpa/README +++ b/contrib/wpa/README @@ -1,7 +1,7 @@ wpa_supplicant and hostapd -------------------------- -Copyright (c) 2002-2015, Jouni Malinen and contributors +Copyright (c) 2002-2016, Jouni Malinen and contributors All Rights Reserved. These programs are licensed under the BSD license (the one with diff --git a/contrib/wpa/hostapd/ChangeLog b/contrib/wpa/hostapd/ChangeLog index af54e1e5b4e4..d2b669b58654 100644 --- a/contrib/wpa/hostapd/ChangeLog +++ b/contrib/wpa/hostapd/ChangeLog @@ -1,5 +1,78 @@ ChangeLog for hostapd +2016-10-02 - v2.6 + * fixed EAP-pwd last fragment validation + [http://w1.fi/security/2015-7/] (CVE-2015-5314) + * fixed WPS configuration update vulnerability with malformed passphrase + [http://w1.fi/security/2016-1/] (CVE-2016-4476) + * extended channel switch support for VHT bandwidth changes + * added support for configuring new ANQP-elements with + anqp_elem=: + * fixed Suite B 192-bit AKM to use proper PMK length + (note: this makes old releases incompatible with the fixed behavior) + * added no_probe_resp_if_max_sta=1 parameter to disable Probe Response + frame sending for not-associated STAs if max_num_sta limit has been + reached + * added option (-S as command line argument) to request all interfaces + to be started at the same time + * modified rts_threshold and fragm_threshold configuration parameters + to allow -1 to be used to disable RTS/fragmentation + * EAP-pwd: added support for Brainpool Elliptic Curves + (with OpenSSL 1.0.2 and newer) + * fixed EAPOL reauthentication after FT protocol run + * fixed FTIE generation for 4-way handshake after FT protocol run + * fixed and improved various FST operations + * TLS server + - support SHA384 and SHA512 hashes + - support TLS v1.2 signature algorithm with SHA384 and SHA512 + - support PKCS #5 v2.0 PBES2 + - support PKCS #5 with PKCS #12 style key decryption + - minimal support for PKCS #12 + - support OCSP stapling (including ocsp_multi) + * added support for OpenSSL 1.1 API changes + - drop support for OpenSSL 0.9.8 + - drop support for OpenSSL 1.0.0 + * EAP-PEAP: support fast-connect crypto binding + * RADIUS + - fix Called-Station-Id to not escape SSID + - add Event-Timestamp to all Accounting-Request packets + - add Acct-Session-Id to Accounting-On/Off + - add Acct-Multi-Session-Id ton Access-Request packets + - add Service-Type (= Frames) + - allow server to provide PSK instead of passphrase for WPA-PSK + Tunnel_password case + - update full message for interim accounting updates + - add Acct-Delay-Time into Accounting messages + - add require_message_authenticator configuration option to require + CoA/Disconnect-Request packets to be authenticated + * started to postpone WNM-Notification frame sending by 100 ms so that + the STA has some more time to configure the key before this frame is + received after the 4-way handshake + * VHT: added interoperability workaround for 80+80 and 160 MHz channels + * extended VLAN support (per-STA vif, etc.) + * fixed PMKID derivation with SAE + * nl80211 + - added support for full station state operations + - fix IEEE 802.1X/WEP EAP reauthentication and rekeying to use + unencrypted EAPOL frames + * added initial MBO support; number of extensions to WNM BSS Transition + Management + * added initial functionality for location related operations + * added assocresp_elements parameter to allow vendor specific elements + to be added into (Re)Association Response frames + * improved Public Action frame addressing + - use Address 3 = wildcard BSSID in GAS response if a query from an + unassociated STA used that address + - fix TX status processing for Address 3 = wildcard BSSID + - add gas_address3 configuration parameter to control Address 3 + behavior + * added command line parameter -i to override interface parameter in + hostapd.conf + * added command completion support to hostapd_cli + * added passive client taxonomy determination (CONFIG_TAXONOMY=y + compile option and "SIGNATURE " control interface command) + * number of small fixes + 2015-09-27 - v2.5 * fixed WPS UPnP vulnerability with HTTP chunked transfer encoding [http://w1.fi/security/2015-2/] (CVE-2015-4141) diff --git a/contrib/wpa/hostapd/README b/contrib/wpa/hostapd/README index 366b1998f484..5d5fd365bb62 100644 --- a/contrib/wpa/hostapd/README +++ b/contrib/wpa/hostapd/README @@ -2,7 +2,7 @@ hostapd - user space IEEE 802.11 AP and IEEE 802.1X/WPA/WPA2/EAP Authenticator and RADIUS authentication server ================================================================ -Copyright (c) 2002-2015, Jouni Malinen and contributors +Copyright (c) 2002-2016, Jouni Malinen and contributors All Rights Reserved. This program is licensed under the BSD license (the one with diff --git a/contrib/wpa/hostapd/config_file.c b/contrib/wpa/hostapd/config_file.c index 82ac61d7729a..5079f69e3bc5 100644 --- a/contrib/wpa/hostapd/config_file.c +++ b/contrib/wpa/hostapd/config_file.c @@ -97,6 +97,8 @@ static int hostapd_config_read_vlan_file(struct hostapd_bss_config *bss, } vlan->vlan_id = vlan_id; + vlan->vlan_desc.untagged = vlan_id; + vlan->vlan_desc.notempty = !!vlan_id; os_strlcpy(vlan->ifname, pos, sizeof(vlan->ifname)); vlan->next = bss->vlan; bss->vlan = vlan; @@ -197,7 +199,10 @@ static int hostapd_config_read_maclist(const char *fname, *acl = newacl; os_memcpy((*acl)[*num].addr, addr, ETH_ALEN); - (*acl)[*num].vlan_id = vlan_id; + os_memset(&(*acl)[*num].vlan_id, 0, + sizeof((*acl)[*num].vlan_id)); + (*acl)[*num].vlan_id.untagged = vlan_id; + (*acl)[*num].vlan_id.notempty = !!vlan_id; (*num)++; } @@ -631,8 +636,7 @@ hostapd_parse_radius_attr(const char *value) } -static int hostapd_parse_das_client(struct hostapd_bss_config *bss, - const char *val) +static int hostapd_parse_das_client(struct hostapd_bss_config *bss, char *val) { char *secret; @@ -640,7 +644,7 @@ static int hostapd_parse_das_client(struct hostapd_bss_config *bss, if (secret == NULL) return -1; - secret++; + *secret++ = '\0'; if (hostapd_parse_ip_addr(val, &bss->radius_das_client_addr)) return -1; @@ -1519,6 +1523,54 @@ fail: } +static int parse_anqp_elem(struct hostapd_bss_config *bss, char *buf, int line) +{ + char *delim; + u16 infoid; + size_t len; + struct wpabuf *payload; + struct anqp_element *elem; + + delim = os_strchr(buf, ':'); + if (!delim) + return -1; + delim++; + infoid = atoi(buf); + len = os_strlen(delim); + if (len & 1) + return -1; + len /= 2; + payload = wpabuf_alloc(len); + if (!payload) + return -1; + if (hexstr2bin(delim, wpabuf_put(payload, len), len) < 0) { + wpabuf_free(payload); + return -1; + } + + dl_list_for_each(elem, &bss->anqp_elem, struct anqp_element, list) { + if (elem->infoid == infoid) { + /* Update existing entry */ + wpabuf_free(elem->payload); + elem->payload = payload; + return 0; + } + } + + /* Add a new entry */ + elem = os_zalloc(sizeof(*elem)); + if (!elem) { + wpabuf_free(payload); + return -1; + } + elem->infoid = infoid; + elem->payload = payload; + dl_list_add(&bss->anqp_elem, &elem->list); + + return 0; +} + + static int parse_qos_map_set(struct hostapd_bss_config *bss, char *buf, int line) { @@ -1867,31 +1919,6 @@ static int hs20_parse_osu_service_desc(struct hostapd_bss_config *bss, #endif /* CONFIG_HS20 */ -#ifdef CONFIG_WPS_NFC -static struct wpabuf * hostapd_parse_bin(const char *buf) -{ - size_t len; - struct wpabuf *ret; - - len = os_strlen(buf); - if (len & 0x01) - return NULL; - len /= 2; - - ret = wpabuf_alloc(len); - if (ret == NULL) - return NULL; - - if (hexstr2bin(buf, wpabuf_put(ret, len), len)) { - wpabuf_free(ret); - return NULL; - } - - return ret; -} -#endif /* CONFIG_WPS_NFC */ - - #ifdef CONFIG_ACS static int hostapd_config_parse_acs_chan_bias(struct hostapd_config *conf, char *pos) @@ -1934,6 +1961,31 @@ fail: #endif /* CONFIG_ACS */ +static int parse_wpabuf_hex(int line, const char *name, struct wpabuf **buf, + const char *val) +{ + struct wpabuf *elems; + + if (val[0] == '\0') { + wpabuf_free(*buf); + *buf = NULL; + return 0; + } + + elems = wpabuf_parse_bin(val); + if (!elems) { + wpa_printf(MSG_ERROR, "Line %d: Invalid %s '%s'", + line, name, val); + return -1; + } + + wpabuf_free(*buf); + *buf = elems; + + return 0; +} + + static int hostapd_config_fill(struct hostapd_config *conf, struct hostapd_bss_config *bss, const char *buf, char *pos, int line) @@ -2084,6 +2136,9 @@ static int hostapd_config_fill(struct hostapd_config *conf, } else if (os_strcmp(buf, "ocsp_stapling_response") == 0) { os_free(bss->ocsp_stapling_response); bss->ocsp_stapling_response = os_strdup(pos); + } else if (os_strcmp(buf, "ocsp_stapling_response_multi") == 0) { + os_free(bss->ocsp_stapling_response_multi); + bss->ocsp_stapling_response_multi = os_strdup(pos); } else if (os_strcmp(buf, "dh_file") == 0) { os_free(bss->dh_file); bss->dh_file = os_strdup(pos); @@ -2139,6 +2194,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, } else if (os_strcmp(buf, "eap_sim_db") == 0) { os_free(bss->eap_sim_db); bss->eap_sim_db = os_strdup(pos); + } else if (os_strcmp(buf, "eap_sim_db_timeout") == 0) { + bss->eap_sim_db_timeout = atoi(pos); } else if (os_strcmp(buf, "eap_sim_aka_result_ind") == 0) { bss->eap_sim_aka_result_ind = atoi(pos); #endif /* EAP_SERVER_SIM */ @@ -2353,6 +2410,9 @@ static int hostapd_config_fill(struct hostapd_config *conf, bss->radius_das_time_window = atoi(pos); } else if (os_strcmp(buf, "radius_das_require_event_timestamp") == 0) { bss->radius_das_require_event_timestamp = atoi(pos); + } else if (os_strcmp(buf, "radius_das_require_message_authenticator") == + 0) { + bss->radius_das_require_message_authenticator = atoi(pos); #endif /* CONFIG_NO_RADIUS */ } else if (os_strcmp(buf, "auth_algs") == 0) { bss->auth_algs = atoi(pos); @@ -2644,7 +2704,7 @@ static int hostapd_config_fill(struct hostapd_config *conf, } } else if (os_strcmp(buf, "rts_threshold") == 0) { conf->rts_threshold = atoi(pos); - if (conf->rts_threshold < 0 || conf->rts_threshold > 2347) { + if (conf->rts_threshold < -1 || conf->rts_threshold > 65535) { wpa_printf(MSG_ERROR, "Line %d: invalid rts_threshold %d", line, conf->rts_threshold); @@ -2652,8 +2712,10 @@ static int hostapd_config_fill(struct hostapd_config *conf, } } else if (os_strcmp(buf, "fragm_threshold") == 0) { conf->fragm_threshold = atoi(pos); - if (conf->fragm_threshold < 256 || - conf->fragm_threshold > 2346) { + if (conf->fragm_threshold == -1) { + /* allow a value of -1 */ + } else if (conf->fragm_threshold < 256 || + conf->fragm_threshold > 2346) { wpa_printf(MSG_ERROR, "Line %d: invalid fragm_threshold %d", line, conf->fragm_threshold); @@ -2686,6 +2748,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, conf->preamble = LONG_PREAMBLE; } else if (os_strcmp(buf, "ignore_broadcast_ssid") == 0) { bss->ignore_broadcast_ssid = atoi(pos); + } else if (os_strcmp(buf, "no_probe_resp_if_max_sta") == 0) { + bss->no_probe_resp_if_max_sta = atoi(pos); } else if (os_strcmp(buf, "wep_default_key") == 0) { bss->ssid.wep.idx = atoi(pos); if (bss->ssid.wep.idx > 3) { @@ -2707,6 +2771,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, #ifndef CONFIG_NO_VLAN } else if (os_strcmp(buf, "dynamic_vlan") == 0) { bss->ssid.dynamic_vlan = atoi(pos); + } else if (os_strcmp(buf, "per_sta_vif") == 0) { + bss->ssid.per_sta_vif = atoi(pos); } else if (os_strcmp(buf, "vlan_file") == 0) { if (hostapd_config_read_vlan_file(bss, pos)) { wpa_printf(MSG_ERROR, "Line %d: failed to read VLAN file '%s'", @@ -2762,6 +2828,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, line); return 1; } + } else if (os_strcmp(buf, "use_driver_iface_addr") == 0) { + conf->use_driver_iface_addr = atoi(pos); #ifdef CONFIG_IEEE80211W } else if (os_strcmp(buf, "ieee80211w") == 0) { bss->ieee80211w = atoi(pos); @@ -2827,6 +2895,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, conf->vht_oper_centr_freq_seg1_idx = atoi(pos); } else if (os_strcmp(buf, "vendor_vht") == 0) { bss->vendor_vht = atoi(pos); + } else if (os_strcmp(buf, "use_sta_nsts") == 0) { + bss->use_sta_nsts = atoi(pos); #endif /* CONFIG_IEEE80211AC */ } else if (os_strcmp(buf, "max_listen_interval") == 0) { bss->max_listen_interval = atoi(pos); @@ -2965,15 +3035,15 @@ static int hostapd_config_fill(struct hostapd_config *conf, bss->wps_nfc_pw_from_config = 1; } else if (os_strcmp(buf, "wps_nfc_dh_pubkey") == 0) { wpabuf_free(bss->wps_nfc_dh_pubkey); - bss->wps_nfc_dh_pubkey = hostapd_parse_bin(pos); + bss->wps_nfc_dh_pubkey = wpabuf_parse_bin(pos); bss->wps_nfc_pw_from_config = 1; } else if (os_strcmp(buf, "wps_nfc_dh_privkey") == 0) { wpabuf_free(bss->wps_nfc_dh_privkey); - bss->wps_nfc_dh_privkey = hostapd_parse_bin(pos); + bss->wps_nfc_dh_privkey = wpabuf_parse_bin(pos); bss->wps_nfc_pw_from_config = 1; } else if (os_strcmp(buf, "wps_nfc_dev_pw") == 0) { wpabuf_free(bss->wps_nfc_dev_pw); - bss->wps_nfc_dev_pw = hostapd_parse_bin(pos); + bss->wps_nfc_dev_pw = wpabuf_parse_bin(pos); bss->wps_nfc_pw_from_config = 1; #endif /* CONFIG_WPS_NFC */ #endif /* CONFIG_WPS */ @@ -3136,6 +3206,9 @@ static int hostapd_config_fill(struct hostapd_config *conf, } else if (os_strcmp(buf, "nai_realm") == 0) { if (parse_nai_realm(bss, pos, line) < 0) return 1; + } else if (os_strcmp(buf, "anqp_elem") == 0) { + if (parse_anqp_elem(bss, pos, line) < 0) + return 1; } else if (os_strcmp(buf, "gas_frag_limit") == 0) { bss->gas_frag_limit = atoi(pos); } else if (os_strcmp(buf, "gas_comeback_delay") == 0) { @@ -3149,13 +3222,15 @@ static int hostapd_config_fill(struct hostapd_config *conf, os_free(bss->dump_msk_file); bss->dump_msk_file = os_strdup(pos); #endif /* CONFIG_RADIUS_TEST */ +#ifdef CONFIG_PROXYARP + } else if (os_strcmp(buf, "proxy_arp") == 0) { + bss->proxy_arp = atoi(pos); +#endif /* CONFIG_PROXYARP */ #ifdef CONFIG_HS20 } else if (os_strcmp(buf, "hs20") == 0) { bss->hs20 = atoi(pos); } else if (os_strcmp(buf, "disable_dgaf") == 0) { bss->disable_dgaf = atoi(pos); - } else if (os_strcmp(buf, "proxy_arp") == 0) { - bss->proxy_arp = atoi(pos); } else if (os_strcmp(buf, "na_mcast_to_ucast") == 0) { bss->na_mcast_to_ucast = atoi(pos); } else if (os_strcmp(buf, "osen") == 0) { @@ -3231,6 +3306,10 @@ static int hostapd_config_fill(struct hostapd_config *conf, } else if (os_strcmp(buf, "subscr_remediation_method") == 0) { bss->subscr_remediation_method = atoi(pos); #endif /* CONFIG_HS20 */ +#ifdef CONFIG_MBO + } else if (os_strcmp(buf, "mbo") == 0) { + bss->mbo_enabled = atoi(pos); +#endif /* CONFIG_MBO */ #ifdef CONFIG_TESTING_OPTIONS #define PARSE_TEST_PROBABILITY(_val) \ } else if (os_strcmp(buf, #_val) == 0) { \ @@ -3249,6 +3328,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, PARSE_TEST_PROBABILITY(ignore_assoc_probability) PARSE_TEST_PROBABILITY(ignore_reassoc_probability) PARSE_TEST_PROBABILITY(corrupt_gtk_rekey_mic_probability) + } else if (os_strcmp(buf, "ecsa_ie_only") == 0) { + conf->ecsa_ie_only = atoi(pos); } else if (os_strcmp(buf, "bss_load_test") == 0) { WPA_PUT_LE16(bss->bss_load_test, atoi(pos)); pos = os_strchr(pos, ':'); @@ -3269,7 +3350,15 @@ static int hostapd_config_fill(struct hostapd_config *conf, WPA_PUT_LE16(&bss->bss_load_test[3], atoi(pos)); bss->bss_load_test_set = 1; } else if (os_strcmp(buf, "radio_measurements") == 0) { - bss->radio_measurements = atoi(pos); + /* + * DEPRECATED: This parameter will be removed in the future. + * Use rrm_neighbor_report instead. + */ + int val = atoi(pos); + + if (val & BIT(0)) + bss->radio_measurements[0] |= + WLAN_RRM_CAPS_NEIGHBOR_REPORT; } else if (os_strcmp(buf, "own_ie_override") == 0) { struct wpabuf *tmp; size_t len = os_strlen(pos) / 2; @@ -3290,35 +3379,11 @@ static int hostapd_config_fill(struct hostapd_config *conf, bss->own_ie_override = tmp; #endif /* CONFIG_TESTING_OPTIONS */ } else if (os_strcmp(buf, "vendor_elements") == 0) { - struct wpabuf *elems; - size_t len = os_strlen(pos); - if (len & 0x01) { - wpa_printf(MSG_ERROR, - "Line %d: Invalid vendor_elements '%s'", - line, pos); + if (parse_wpabuf_hex(line, buf, &bss->vendor_elements, pos)) return 1; - } - len /= 2; - if (len == 0) { - wpabuf_free(bss->vendor_elements); - bss->vendor_elements = NULL; - return 0; - } - - elems = wpabuf_alloc(len); - if (elems == NULL) + } else if (os_strcmp(buf, "assocresp_elements") == 0) { + if (parse_wpabuf_hex(line, buf, &bss->assocresp_elements, pos)) return 1; - - if (hexstr2bin(pos, wpabuf_put(elems, len), len)) { - wpabuf_free(elems); - wpa_printf(MSG_ERROR, - "Line %d: Invalid vendor_elements '%s'", - line, pos); - return 1; - } - - wpabuf_free(bss->vendor_elements); - bss->vendor_elements = elems; } else if (os_strcmp(buf, "sae_anti_clogging_threshold") == 0) { bss->sae_anti_clogging_threshold = atoi(pos); } else if (os_strcmp(buf, "sae_groups") == 0) { @@ -3391,7 +3456,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, return -1; } val = strtol(pos, &endp, 0); - if (*endp || val < 1 || val > FST_MAX_LLT_MS) { + if (*endp || val < 1 || + (unsigned long int) val > FST_MAX_LLT_MS) { wpa_printf(MSG_ERROR, "Line %d: Invalid fst_llt %ld (%s) (expected 1..%u)", line, val, pos, FST_MAX_LLT_MS); @@ -3409,6 +3475,22 @@ static int hostapd_config_fill(struct hostapd_config *conf, } else if (os_strcmp(buf, "no_auth_if_seen_on") == 0) { os_free(bss->no_auth_if_seen_on); bss->no_auth_if_seen_on = os_strdup(pos); + } else if (os_strcmp(buf, "lci") == 0) { + wpabuf_free(conf->lci); + conf->lci = wpabuf_parse_bin(pos); + } else if (os_strcmp(buf, "civic") == 0) { + wpabuf_free(conf->civic); + conf->civic = wpabuf_parse_bin(pos); + } else if (os_strcmp(buf, "rrm_neighbor_report") == 0) { + if (atoi(pos)) + bss->radio_measurements[0] |= + WLAN_RRM_CAPS_NEIGHBOR_REPORT; + } else if (os_strcmp(buf, "gas_address3") == 0) { + bss->gas_address3 = atoi(pos); + } else if (os_strcmp(buf, "ftm_responder") == 0) { + bss->ftm_responder = atoi(pos); + } else if (os_strcmp(buf, "ftm_initiator") == 0) { + bss->ftm_initiator = atoi(pos); } else { wpa_printf(MSG_ERROR, "Line %d: unknown configuration item '%s'", @@ -3429,7 +3511,7 @@ struct hostapd_config * hostapd_config_read(const char *fname) { struct hostapd_config *conf; FILE *f; - char buf[512], *pos; + char buf[4096], *pos; int line = 0; int errors = 0; size_t i; diff --git a/contrib/wpa/hostapd/ctrl_iface.c b/contrib/wpa/hostapd/ctrl_iface.c index cb6fb1757708..d7db4a7c3c48 100644 --- a/contrib/wpa/hostapd/ctrl_iface.c +++ b/contrib/wpa/hostapd/ctrl_iface.c @@ -19,10 +19,16 @@ #include #include +#ifdef CONFIG_CTRL_IFACE_UDP +#include +#endif /* CONFIG_CTRL_IFACE_UDP */ + #include "utils/common.h" #include "utils/eloop.h" +#include "utils/module_tests.h" #include "common/version.h" #include "common/ieee802_11_defs.h" +#include "common/ctrl_iface_common.h" #include "crypto/tls.h" #include "drivers/driver.h" #include "eapol_auth/eapol_auth_sm.h" @@ -42,6 +48,8 @@ #include "ap/wnm_ap.h" #include "ap/wpa_auth.h" #include "ap/beacon.h" +#include "ap/neighbor_db.h" +#include "ap/rrm.h" #include "wps/wps_defs.h" #include "wps/wps.h" #include "fst/fst_ctrl_iface.h" @@ -51,14 +59,15 @@ #define HOSTAPD_CLI_DUP_VALUE_MAX_LEN 256 -struct wpa_ctrl_dst { - struct wpa_ctrl_dst *next; - struct sockaddr_un addr; - socklen_t addrlen; - int debug_level; - int errors; -}; - +#ifdef CONFIG_CTRL_IFACE_UDP +#define COOKIE_LEN 8 +static unsigned char cookie[COOKIE_LEN]; +static unsigned char gcookie[COOKIE_LEN]; +#define HOSTAPD_CTRL_IFACE_PORT 8877 +#define HOSTAPD_CTRL_IFACE_PORT_LIMIT 50 +#define HOSTAPD_GLOBAL_CTRL_IFACE_PORT 8878 +#define HOSTAPD_GLOBAL_CTRL_IFACE_PORT_LIMIT 50 +#endif /* CONFIG_CTRL_IFACE_UDP */ static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, enum wpa_msg_type type, @@ -66,81 +75,27 @@ static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, static int hostapd_ctrl_iface_attach(struct hostapd_data *hapd, - struct sockaddr_un *from, + struct sockaddr_storage *from, socklen_t fromlen) { - struct wpa_ctrl_dst *dst; - - dst = os_zalloc(sizeof(*dst)); - if (dst == NULL) - return -1; - os_memcpy(&dst->addr, from, sizeof(struct sockaddr_un)); - dst->addrlen = fromlen; - dst->debug_level = MSG_INFO; - dst->next = hapd->ctrl_dst; - hapd->ctrl_dst = dst; - wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached", - (u8 *) from->sun_path, - fromlen - offsetof(struct sockaddr_un, sun_path)); - return 0; + return ctrl_iface_attach(&hapd->ctrl_dst, from, fromlen); } static int hostapd_ctrl_iface_detach(struct hostapd_data *hapd, - struct sockaddr_un *from, + struct sockaddr_storage *from, socklen_t fromlen) { - struct wpa_ctrl_dst *dst, *prev = NULL; - - dst = hapd->ctrl_dst; - while (dst) { - if (fromlen == dst->addrlen && - os_memcmp(from->sun_path, dst->addr.sun_path, - fromlen - offsetof(struct sockaddr_un, sun_path)) - == 0) { - wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor detached", - (u8 *) from->sun_path, - fromlen - - offsetof(struct sockaddr_un, sun_path)); - if (prev == NULL) - hapd->ctrl_dst = dst->next; - else - prev->next = dst->next; - os_free(dst); - return 0; - } - prev = dst; - dst = dst->next; - } - return -1; + return ctrl_iface_detach(&hapd->ctrl_dst, from, fromlen); } static int hostapd_ctrl_iface_level(struct hostapd_data *hapd, - struct sockaddr_un *from, + struct sockaddr_storage *from, socklen_t fromlen, char *level) { - struct wpa_ctrl_dst *dst; - - wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level); - - dst = hapd->ctrl_dst; - while (dst) { - if (fromlen == dst->addrlen && - os_memcmp(from->sun_path, dst->addr.sun_path, - fromlen - offsetof(struct sockaddr_un, sun_path)) - == 0) { - wpa_hexdump(MSG_DEBUG, "CTRL_IFACE changed monitor " - "level", (u8 *) from->sun_path, fromlen - - offsetof(struct sockaddr_un, sun_path)); - dst->debug_level = atoi(level); - return 0; - } - dst = dst->next; - } - - return -1; + return ctrl_iface_level(&hapd->ctrl_dst, from, fromlen, level); } @@ -884,6 +839,8 @@ static int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd, int ret; u8 nei_rep[1000]; u8 *nei_pos = nei_rep; + u8 mbo[10]; + size_t mbo_len = 0; if (hwaddr_aton(cmd, addr)) { wpa_printf(MSG_DEBUG, "Invalid STA MAC address"); @@ -1049,10 +1006,66 @@ static int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd, if (os_strstr(cmd, " disassoc_imminent=1")) req_mode |= WNM_BSS_TM_REQ_DISASSOC_IMMINENT; +#ifdef CONFIG_MBO + pos = os_strstr(cmd, "mbo="); + if (pos) { + unsigned int mbo_reason, cell_pref, reassoc_delay; + u8 *mbo_pos = mbo; + + ret = sscanf(pos, "mbo=%u:%u:%u", &mbo_reason, + &reassoc_delay, &cell_pref); + if (ret != 3) { + wpa_printf(MSG_DEBUG, + "MBO requires three arguments: mbo=::"); + return -1; + } + + if (mbo_reason > MBO_TRANSITION_REASON_PREMIUM_AP) { + wpa_printf(MSG_DEBUG, + "Invalid MBO transition reason code %u", + mbo_reason); + return -1; + } + + /* Valid values for Cellular preference are: 0, 1, 255 */ + if (cell_pref != 0 && cell_pref != 1 && cell_pref != 255) { + wpa_printf(MSG_DEBUG, + "Invalid MBO cellular capability %u", + cell_pref); + return -1; + } + + if (reassoc_delay > 65535 || + (reassoc_delay && + !(req_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT))) { + wpa_printf(MSG_DEBUG, + "MBO: Assoc retry delay is only valid in disassoc imminent mode"); + return -1; + } + + *mbo_pos++ = MBO_ATTR_ID_TRANSITION_REASON; + *mbo_pos++ = 1; + *mbo_pos++ = mbo_reason; + *mbo_pos++ = MBO_ATTR_ID_CELL_DATA_PREF; + *mbo_pos++ = 1; + *mbo_pos++ = cell_pref; + + if (reassoc_delay) { + *mbo_pos++ = MBO_ATTR_ID_ASSOC_RETRY_DELAY; + *mbo_pos++ = 2; + WPA_PUT_LE16(mbo_pos, reassoc_delay); + mbo_pos += 2; + } + + mbo_len = mbo_pos - mbo; + } +#endif /* CONFIG_MBO */ + ret = wnm_send_bss_tm_req(hapd, sta, req_mode, disassoc_timer, valid_int, bss_term_dur, url, nei_pos > nei_rep ? nei_rep : NULL, - nei_pos - nei_rep); + nei_pos - nei_rep, mbo_len ? mbo : NULL, + mbo_len); os_free(url); return ret; } @@ -1320,9 +1333,28 @@ static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd) } else if (os_strcasecmp(cmd, "ext_eapol_frame_io") == 0) { hapd->ext_eapol_frame_io = atoi(value); #endif /* CONFIG_TESTING_OPTIONS */ +#ifdef CONFIG_MBO + } else if (os_strcasecmp(cmd, "mbo_assoc_disallow") == 0) { + int val; + + if (!hapd->conf->mbo_enabled) + return -1; + + val = atoi(value); + if (val < 0 || val > 1) + return -1; + + hapd->mbo_assoc_disallow = val; + ieee802_11_update_beacons(hapd->iface); + + /* + * TODO: Need to configure drivers that do AP MLME offload with + * disallowing station logic. + */ +#endif /* CONFIG_MBO */ } else { struct sta_info *sta; - int vlan_id; + struct vlan_description vlan_id; ret = hostapd_set_iface(hapd->iconf, hapd->conf, cmd, value); if (ret) @@ -1334,7 +1366,8 @@ static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd) hapd->conf->deny_mac, hapd->conf->num_deny_mac, sta->addr, &vlan_id) && - (!vlan_id || vlan_id == sta->vlan_id)) + (!vlan_id.notempty || + !vlan_compare(&vlan_id, sta->vlan_desc))) ap_sta_disconnect( hapd, sta, sta->addr, WLAN_REASON_UNSPECIFIED); @@ -1346,7 +1379,8 @@ static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd) hapd->conf->accept_mac, hapd->conf->num_accept_mac, sta->addr, &vlan_id) || - (vlan_id && vlan_id != sta->vlan_id)) + (vlan_id.notempty && + vlan_compare(&vlan_id, sta->vlan_desc))) ap_sta_disconnect( hapd, sta, sta->addr, WLAN_REASON_UNSPECIFIED); @@ -1557,8 +1591,8 @@ static u16 ipv4_hdr_checksum(const void *buf, size_t len) #define HWSIM_PACKETLEN 1500 #define HWSIM_IP_LEN (HWSIM_PACKETLEN - sizeof(struct ether_header)) -void hostapd_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf, - size_t len) +static void hostapd_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) { struct hostapd_data *hapd = ctx; const struct ether_header *eth; @@ -1745,8 +1779,6 @@ done: static int hostapd_ctrl_test_alloc_fail(struct hostapd_data *hapd, char *cmd) { #ifdef WPA_TRACE_BFD - extern char wpa_trace_fail_func[256]; - extern unsigned int wpa_trace_fail_after; char *pos; wpa_trace_fail_after = atoi(cmd); @@ -1770,9 +1802,6 @@ static int hostapd_ctrl_get_alloc_fail(struct hostapd_data *hapd, char *buf, size_t buflen) { #ifdef WPA_TRACE_BFD - extern char wpa_trace_fail_func[256]; - extern unsigned int wpa_trace_fail_after; - return os_snprintf(buf, buflen, "%u:%s", wpa_trace_fail_after, wpa_trace_fail_func); #else /* WPA_TRACE_BFD */ @@ -1784,8 +1813,6 @@ static int hostapd_ctrl_get_alloc_fail(struct hostapd_data *hapd, static int hostapd_ctrl_test_fail(struct hostapd_data *hapd, char *cmd) { #ifdef WPA_TRACE_BFD - extern char wpa_trace_test_fail_func[256]; - extern unsigned int wpa_trace_test_fail_after; char *pos; wpa_trace_test_fail_after = atoi(cmd); @@ -1809,9 +1836,6 @@ static int hostapd_ctrl_get_fail(struct hostapd_data *hapd, char *buf, size_t buflen) { #ifdef WPA_TRACE_BFD - extern char wpa_trace_test_fail_func[256]; - extern unsigned int wpa_trace_test_fail_after; - return os_snprintf(buf, buflen, "%u:%s", wpa_trace_test_fail_after, wpa_trace_test_fail_func); #else /* WPA_TRACE_BFD */ @@ -1875,13 +1899,13 @@ static int hostapd_ctrl_iface_vendor(struct hostapd_data *hapd, char *cmd, /* cmd: [] */ vendor_id = strtoul(cmd, &pos, 16); - if (!isblank(*pos)) + if (!isblank((unsigned char) *pos)) return -EINVAL; subcmd = strtoul(pos, &pos, 10); if (*pos != '\0') { - if (!isblank(*pos++)) + if (!isblank((unsigned char) *pos++)) return -EINVAL; data_len = os_strlen(pos); } @@ -2016,6 +2040,9 @@ static int hostapd_ctrl_iface_track_sta_list(struct hostapd_data *hapd, struct hostapd_sta_info *info; struct os_reltime now; + if (!iface->num_sta_seen) + return 0; + sta_track_expire(iface, 0); pos = buf; @@ -2040,10 +2067,228 @@ static int hostapd_ctrl_iface_track_sta_list(struct hostapd_data *hapd, #endif /* NEED_AP_MLME */ +static int hostapd_ctrl_iface_req_lci(struct hostapd_data *hapd, + const char *cmd) +{ + u8 addr[ETH_ALEN]; + + if (hwaddr_aton(cmd, addr)) { + wpa_printf(MSG_INFO, "CTRL: REQ_LCI: Invalid MAC address"); + return -1; + } + + return hostapd_send_lci_req(hapd, addr); +} + + +static int hostapd_ctrl_iface_req_range(struct hostapd_data *hapd, char *cmd) +{ + u8 addr[ETH_ALEN]; + char *token, *context = NULL; + int random_interval, min_ap; + u8 responders[ETH_ALEN * RRM_RANGE_REQ_MAX_RESPONDERS]; + unsigned int n_responders; + + token = str_token(cmd, " ", &context); + if (!token || hwaddr_aton(token, addr)) { + wpa_printf(MSG_INFO, + "CTRL: REQ_RANGE - Bad destination address"); + return -1; + } + + token = str_token(cmd, " ", &context); + if (!token) + return -1; + + random_interval = atoi(token); + if (random_interval < 0 || random_interval > 0xffff) + return -1; + + token = str_token(cmd, " ", &context); + if (!token) + return -1; + + min_ap = atoi(token); + if (min_ap <= 0 || min_ap > WLAN_RRM_RANGE_REQ_MAX_MIN_AP) + return -1; + + n_responders = 0; + while ((token = str_token(cmd, " ", &context))) { + if (n_responders == RRM_RANGE_REQ_MAX_RESPONDERS) { + wpa_printf(MSG_INFO, + "CTRL: REQ_RANGE: Too many responders"); + return -1; + } + + if (hwaddr_aton(token, responders + n_responders * ETH_ALEN)) { + wpa_printf(MSG_INFO, + "CTRL: REQ_RANGE: Bad responder address"); + return -1; + } + + n_responders++; + } + + if (!n_responders) { + wpa_printf(MSG_INFO, + "CTRL: REQ_RANGE - No FTM responder address"); + return -1; + } + + return hostapd_send_range_req(hapd, addr, random_interval, min_ap, + responders, n_responders); +} + + +static int hostapd_ctrl_iface_set_neighbor(struct hostapd_data *hapd, char *buf) +{ + struct wpa_ssid_value ssid; + u8 bssid[ETH_ALEN]; + struct wpabuf *nr, *lci = NULL, *civic = NULL; + char *tmp; + int ret; + + if (!(hapd->conf->radio_measurements[0] & + WLAN_RRM_CAPS_NEIGHBOR_REPORT)) { + wpa_printf(MSG_ERROR, + "CTRL: SET_NEIGHBOR: Neighbor report is not enabled"); + return -1; + } + + if (hwaddr_aton(buf, bssid)) { + wpa_printf(MSG_ERROR, "CTRL: SET_NEIGHBOR: Bad BSSID"); + return -1; + } + + tmp = os_strstr(buf, "ssid="); + if (!tmp || ssid_parse(tmp + 5, &ssid)) { + wpa_printf(MSG_ERROR, + "CTRL: SET_NEIGHBOR: Bad or missing SSID"); + return -1; + } + buf = os_strchr(tmp + 6, tmp[5] == '"' ? '"' : ' '); + if (!buf) + return -1; + + tmp = os_strstr(buf, "nr="); + if (!tmp) { + wpa_printf(MSG_ERROR, + "CTRL: SET_NEIGHBOR: Missing Neighbor Report element"); + return -1; + } + + buf = os_strchr(tmp, ' '); + if (buf) + *buf++ = '\0'; + + nr = wpabuf_parse_bin(tmp + 3); + if (!nr) { + wpa_printf(MSG_ERROR, + "CTRL: SET_NEIGHBOR: Bad Neighbor Report element"); + return -1; + } + + if (!buf) + goto set; + + tmp = os_strstr(buf, "lci="); + if (tmp) { + buf = os_strchr(tmp, ' '); + if (buf) + *buf++ = '\0'; + lci = wpabuf_parse_bin(tmp + 4); + if (!lci) { + wpa_printf(MSG_ERROR, + "CTRL: SET_NEIGHBOR: Bad LCI subelement"); + wpabuf_free(nr); + return -1; + } + } + + if (!buf) + goto set; + + tmp = os_strstr(buf, "civic="); + if (tmp) { + buf = os_strchr(tmp, ' '); + if (buf) + *buf++ = '\0'; + civic = wpabuf_parse_bin(tmp + 6); + if (!civic) { + wpa_printf(MSG_ERROR, + "CTRL: SET_NEIGHBOR: Bad civic subelement"); + wpabuf_free(nr); + wpabuf_free(lci); + return -1; + } + } + +set: + ret = hostapd_neighbor_set(hapd, bssid, &ssid, nr, lci, civic); + + wpabuf_free(nr); + wpabuf_free(lci); + wpabuf_free(civic); + + return ret; +} + + +static int hostapd_ctrl_iface_remove_neighbor(struct hostapd_data *hapd, + char *buf) +{ + struct wpa_ssid_value ssid; + u8 bssid[ETH_ALEN]; + char *tmp; + + if (hwaddr_aton(buf, bssid)) { + wpa_printf(MSG_ERROR, "CTRL: REMOVE_NEIGHBOR: Bad BSSID"); + return -1; + } + + tmp = os_strstr(buf, "ssid="); + if (!tmp || ssid_parse(tmp + 5, &ssid)) { + wpa_printf(MSG_ERROR, + "CTRL: REMOVE_NEIGHBORr: Bad or missing SSID"); + return -1; + } + + return hostapd_neighbor_remove(hapd, bssid, &ssid); +} + + +static int hostapd_ctrl_driver_flags(struct hostapd_iface *iface, char *buf, + size_t buflen) +{ + int ret, i; + char *pos, *end; + + ret = os_snprintf(buf, buflen, "%016llX:\n", + (long long unsigned) iface->drv_flags); + if (os_snprintf_error(buflen, ret)) + return -1; + + pos = buf + ret; + end = buf + buflen; + + for (i = 0; i < 64; i++) { + if (iface->drv_flags & (1LLU << i)) { + ret = os_snprintf(pos, end - pos, "%s\n", + driver_flag_to_string(1LLU << i)); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } + } + + return pos - buf; +} + + static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, char *buf, char *reply, int reply_size, - struct sockaddr_un *from, + struct sockaddr_storage *from, socklen_t fromlen) { int reply_len, res; @@ -2122,6 +2367,14 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, } else if (os_strncmp(buf, "DISASSOCIATE ", 13) == 0) { if (hostapd_ctrl_iface_disassociate(hapd, buf + 13)) reply_len = -1; +#ifdef CONFIG_TAXONOMY + } else if (os_strncmp(buf, "SIGNATURE ", 10) == 0) { + reply_len = hostapd_ctrl_iface_signature(hapd, buf + 10, + reply, reply_size); +#endif /* CONFIG_TAXONOMY */ + } else if (os_strncmp(buf, "POLL_STA ", 9) == 0) { + if (hostapd_ctrl_iface_poll_sta(hapd, buf + 9)) + reply_len = -1; } else if (os_strcmp(buf, "STOP_AP") == 0) { if (hostapd_ctrl_iface_stop_ap(hapd)) reply_len = -1; @@ -2276,6 +2529,26 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, reply_len = hostapd_ctrl_iface_track_sta_list( hapd, reply, reply_size); #endif /* NEED_AP_MLME */ + } else if (os_strcmp(buf, "PMKSA") == 0) { + reply_len = hostapd_ctrl_iface_pmksa_list(hapd, reply, + reply_size); + } else if (os_strcmp(buf, "PMKSA_FLUSH") == 0) { + hostapd_ctrl_iface_pmksa_flush(hapd); + } else if (os_strncmp(buf, "SET_NEIGHBOR ", 13) == 0) { + if (hostapd_ctrl_iface_set_neighbor(hapd, buf + 13)) + reply_len = -1; + } else if (os_strncmp(buf, "REMOVE_NEIGHBOR ", 16) == 0) { + if (hostapd_ctrl_iface_remove_neighbor(hapd, buf + 16)) + reply_len = -1; + } else if (os_strncmp(buf, "REQ_LCI ", 8) == 0) { + if (hostapd_ctrl_iface_req_lci(hapd, buf + 8)) + reply_len = -1; + } else if (os_strncmp(buf, "REQ_RANGE ", 10) == 0) { + if (hostapd_ctrl_iface_req_range(hapd, buf + 10)) + reply_len = -1; + } else if (os_strcmp(buf, "DRIVER_FLAGS") == 0) { + reply_len = hostapd_ctrl_driver_flags(hapd->iface, reply, + reply_size); } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; @@ -2296,12 +2569,15 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, struct hostapd_data *hapd = eloop_ctx; char buf[4096]; int res; - struct sockaddr_un from; + struct sockaddr_storage from; socklen_t fromlen = sizeof(from); - char *reply; + char *reply, *pos = buf; const int reply_size = 4096; int reply_len; int level = MSG_DEBUG; +#ifdef CONFIG_CTRL_IFACE_UDP + unsigned char lcookie[COOKIE_LEN]; +#endif /* CONFIG_CTRL_IFACE_UDP */ res = recvfrom(sock, buf, sizeof(buf) - 1, 0, (struct sockaddr *) &from, &fromlen); @@ -2311,9 +2587,6 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, return; } buf[res] = '\0'; - if (os_strcmp(buf, "PING") == 0) - level = MSG_EXCESSIVE; - wpa_hexdump_ascii(level, "RX ctrl_iface", (u8 *) buf, res); reply = os_malloc(reply_size); if (reply == NULL) { @@ -2325,10 +2598,46 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, return; } - reply_len = hostapd_ctrl_iface_receive_process(hapd, buf, +#ifdef CONFIG_CTRL_IFACE_UDP + if (os_strcmp(buf, "GET_COOKIE") == 0) { + os_memcpy(reply, "COOKIE=", 7); + wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1, + cookie, COOKIE_LEN); + reply_len = 7 + 2 * COOKIE_LEN; + goto done; + } + + if (os_strncmp(buf, "COOKIE=", 7) != 0 || + hexstr2bin(buf + 7, lcookie, COOKIE_LEN) < 0) { + wpa_printf(MSG_DEBUG, + "CTRL: No cookie in the request - drop request"); + os_free(reply); + return; + } + + if (os_memcmp(cookie, lcookie, COOKIE_LEN) != 0) { + wpa_printf(MSG_DEBUG, + "CTRL: Invalid cookie in the request - drop request"); + os_free(reply); + return; + } + + pos = buf + 7 + 2 * COOKIE_LEN; + while (*pos == ' ') + pos++; +#endif /* CONFIG_CTRL_IFACE_UDP */ + + if (os_strcmp(pos, "PING") == 0) + level = MSG_EXCESSIVE; + wpa_hexdump_ascii(level, "RX ctrl_iface", pos, res); + + reply_len = hostapd_ctrl_iface_receive_process(hapd, pos, reply, reply_size, &from, fromlen); +#ifdef CONFIG_CTRL_IFACE_UDP +done: +#endif /* CONFIG_CTRL_IFACE_UDP */ if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen) < 0) { wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s", @@ -2338,6 +2647,7 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, } +#ifndef CONFIG_CTRL_IFACE_UDP static char * hostapd_ctrl_iface_path(struct hostapd_data *hapd) { char *buf; @@ -2357,6 +2667,7 @@ static char * hostapd_ctrl_iface_path(struct hostapd_data *hapd) buf[len - 1] = '\0'; return buf; } +#endif /* CONFIG_CTRL_IFACE_UDP */ static void hostapd_ctrl_iface_msg_cb(void *ctx, int level, @@ -2372,6 +2683,99 @@ static void hostapd_ctrl_iface_msg_cb(void *ctx, int level, int hostapd_ctrl_iface_init(struct hostapd_data *hapd) { +#ifdef CONFIG_CTRL_IFACE_UDP + int port = HOSTAPD_CTRL_IFACE_PORT; + char p[32] = { 0 }; + char port_str[40], *tmp; + char *pos; + struct addrinfo hints = { 0 }, *res, *saveres; + int n; + + if (hapd->ctrl_sock > -1) { + wpa_printf(MSG_DEBUG, "ctrl_iface already exists!"); + return 0; + } + + if (hapd->conf->ctrl_interface == NULL) + return 0; + + pos = os_strstr(hapd->conf->ctrl_interface, "udp:"); + if (pos) { + pos += 4; + port = atoi(pos); + if (port <= 0) { + wpa_printf(MSG_ERROR, "Invalid ctrl_iface UDP port"); + goto fail; + } + } + + dl_list_init(&hapd->ctrl_dst); + hapd->ctrl_sock = -1; + os_get_random(cookie, COOKIE_LEN); + +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + hints.ai_flags = AI_PASSIVE; +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ + +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + hints.ai_family = AF_INET6; +#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ + hints.ai_family = AF_INET; +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ + hints.ai_socktype = SOCK_DGRAM; + +try_again: + os_snprintf(p, sizeof(p), "%d", port); + n = getaddrinfo(NULL, p, &hints, &res); + if (n) { + wpa_printf(MSG_ERROR, "getaddrinfo(): %s", gai_strerror(n)); + goto fail; + } + + saveres = res; + hapd->ctrl_sock = socket(res->ai_family, res->ai_socktype, + res->ai_protocol); + if (hapd->ctrl_sock < 0) { + wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno)); + goto fail; + } + + if (bind(hapd->ctrl_sock, res->ai_addr, res->ai_addrlen) < 0) { + port--; + if ((HOSTAPD_CTRL_IFACE_PORT - port) < + HOSTAPD_CTRL_IFACE_PORT_LIMIT && !pos) + goto try_again; + wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno)); + goto fail; + } + + freeaddrinfo(saveres); + + os_snprintf(port_str, sizeof(port_str), "udp:%d", port); + tmp = os_strdup(port_str); + if (tmp) { + os_free(hapd->conf->ctrl_interface); + hapd->conf->ctrl_interface = tmp; + } + wpa_printf(MSG_DEBUG, "ctrl_iface_init UDP port: %d", port); + + if (eloop_register_read_sock(hapd->ctrl_sock, + hostapd_ctrl_iface_receive, hapd, NULL) < + 0) { + hostapd_ctrl_iface_deinit(hapd); + return -1; + } + + hapd->msg_ctx = hapd; + wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb); + + return 0; + +fail: + if (hapd->ctrl_sock >= 0) + close(hapd->ctrl_sock); + return -1; +#else /* CONFIG_CTRL_IFACE_UDP */ struct sockaddr_un addr; int s = -1; char *fname = NULL; @@ -2381,6 +2785,8 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd) return 0; } + dl_list_init(&hapd->ctrl_dst); + if (hapd->conf->ctrl_interface == NULL) return 0; @@ -2520,6 +2926,7 @@ fail: os_free(fname); } return -1; +#endif /* CONFIG_CTRL_IFACE_UDP */ } @@ -2528,10 +2935,14 @@ void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd) struct wpa_ctrl_dst *dst, *prev; if (hapd->ctrl_sock > -1) { +#ifndef CONFIG_CTRL_IFACE_UDP char *fname; +#endif /* !CONFIG_CTRL_IFACE_UDP */ + eloop_unregister_read_sock(hapd->ctrl_sock); close(hapd->ctrl_sock); hapd->ctrl_sock = -1; +#ifndef CONFIG_CTRL_IFACE_UDP fname = hostapd_ctrl_iface_path(hapd); if (fname) unlink(fname); @@ -2550,15 +2961,12 @@ void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd) strerror(errno)); } } +#endif /* !CONFIG_CTRL_IFACE_UDP */ } - dst = hapd->ctrl_dst; - hapd->ctrl_dst = NULL; - while (dst) { - prev = dst; - dst = dst->next; - os_free(prev); - } + dl_list_for_each_safe(dst, prev, &hapd->ctrl_dst, struct wpa_ctrl_dst, + list) + os_free(dst); #ifdef CONFIG_TESTING_OPTIONS l2_packet_deinit(hapd->l2_test); @@ -2590,54 +2998,18 @@ static int hostapd_ctrl_iface_remove(struct hapd_interfaces *interfaces, static int hostapd_global_ctrl_iface_attach(struct hapd_interfaces *interfaces, - struct sockaddr_un *from, + struct sockaddr_storage *from, socklen_t fromlen) { - struct wpa_ctrl_dst *dst; - - dst = os_zalloc(sizeof(*dst)); - if (dst == NULL) - return -1; - os_memcpy(&dst->addr, from, sizeof(struct sockaddr_un)); - dst->addrlen = fromlen; - dst->debug_level = MSG_INFO; - dst->next = interfaces->global_ctrl_dst; - interfaces->global_ctrl_dst = dst; - wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached (global)", - from->sun_path, - fromlen - offsetof(struct sockaddr_un, sun_path)); - return 0; + return ctrl_iface_attach(&interfaces->global_ctrl_dst, from, fromlen); } static int hostapd_global_ctrl_iface_detach(struct hapd_interfaces *interfaces, - struct sockaddr_un *from, + struct sockaddr_storage *from, socklen_t fromlen) { - struct wpa_ctrl_dst *dst, *prev = NULL; - - dst = interfaces->global_ctrl_dst; - while (dst) { - if (fromlen == dst->addrlen && - os_memcmp(from->sun_path, dst->addr.sun_path, - fromlen - offsetof(struct sockaddr_un, sun_path)) - == 0) { - wpa_hexdump(MSG_DEBUG, - "CTRL_IFACE monitor detached (global)", - from->sun_path, - fromlen - - offsetof(struct sockaddr_un, sun_path)); - if (prev == NULL) - interfaces->global_ctrl_dst = dst->next; - else - prev->next = dst->next; - os_free(dst); - return 0; - } - prev = dst; - dst = dst->next; - } - return -1; + return ctrl_iface_detach(&interfaces->global_ctrl_dst, from, fromlen); } @@ -2790,6 +3162,51 @@ error_return: } +static int +hostapd_global_ctrl_iface_interfaces(struct hapd_interfaces *interfaces, + const char *input, + char *reply, int reply_size) +{ + size_t i, j; + int res; + char *pos, *end; + struct hostapd_iface *iface; + int show_ctrl = 0; + + if (input) + show_ctrl = !!os_strstr(input, "ctrl"); + + pos = reply; + end = reply + reply_size; + + for (i = 0; i < interfaces->count; i++) { + iface = interfaces->iface[i]; + + for (j = 0; j < iface->num_bss; j++) { + struct hostapd_bss_config *conf; + + conf = iface->conf->bss[j]; + if (show_ctrl) + res = os_snprintf(pos, end - pos, + "%s ctrl_iface=%s\n", + conf->iface, + conf->ctrl_interface ? + conf->ctrl_interface : "N/A"); + else + res = os_snprintf(pos, end - pos, "%s\n", + conf->iface); + if (os_snprintf_error(end - pos, res)) { + *pos = '\0'; + return pos - reply; + } + pos += res; + } + } + + return pos - reply; +} + + static int hostapd_global_ctrl_iface_dup_network(struct hapd_interfaces *interfaces, char *cmd) @@ -2839,7 +3256,7 @@ static int hostapd_global_ctrl_iface_ifname(struct hapd_interfaces *interfaces, const char *ifname, char *buf, char *reply, int reply_size, - struct sockaddr_un *from, + struct sockaddr_storage *from, socklen_t fromlen) { struct hostapd_data *hapd; @@ -2863,15 +3280,18 @@ static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx, void *sock_ctx) { void *interfaces = eloop_ctx; - char buf[256]; + char buffer[256], *buf = buffer; int res; - struct sockaddr_un from; + struct sockaddr_storage from; socklen_t fromlen = sizeof(from); char *reply; int reply_len; const int reply_size = 4096; +#ifdef CONFIG_CTRL_IFACE_UDP + unsigned char lcookie[COOKIE_LEN]; +#endif /* CONFIG_CTRL_IFACE_UDP */ - res = recvfrom(sock, buf, sizeof(buf) - 1, 0, + res = recvfrom(sock, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *) &from, &fromlen); if (res < 0) { wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s", @@ -2894,6 +3314,35 @@ static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx, os_memcpy(reply, "OK\n", 3); reply_len = 3; +#ifdef CONFIG_CTRL_IFACE_UDP + if (os_strcmp(buf, "GET_COOKIE") == 0) { + os_memcpy(reply, "COOKIE=", 7); + wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1, + gcookie, COOKIE_LEN); + reply_len = 7 + 2 * COOKIE_LEN; + goto send_reply; + } + + if (os_strncmp(buf, "COOKIE=", 7) != 0 || + hexstr2bin(buf + 7, lcookie, COOKIE_LEN) < 0) { + wpa_printf(MSG_DEBUG, + "CTRL: No cookie in the request - drop request"); + os_free(reply); + return; + } + + if (os_memcmp(gcookie, lcookie, COOKIE_LEN) != 0) { + wpa_printf(MSG_DEBUG, + "CTRL: Invalid cookie in the request - drop request"); + os_free(reply); + return; + } + + buf += 7 + 2 * COOKIE_LEN; + while (*buf == ' ') + buf++; +#endif /* CONFIG_CTRL_IFACE_UDP */ + if (os_strncmp(buf, "IFNAME=", 7) == 0) { char *pos = os_strchr(buf + 7, ' '); @@ -2930,7 +3379,6 @@ static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx, reply_len = -1; #ifdef CONFIG_MODULE_TESTS } else if (os_strcmp(buf, "MODULE_TESTS") == 0) { - int hapd_module_tests(void); if (hapd_module_tests() < 0) reply_len = -1; #endif /* CONFIG_MODULE_TESTS */ @@ -2954,6 +3402,11 @@ static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx, reply_len = os_snprintf(reply, reply_size, "OK\n"); else reply_len = -1; + } else if (os_strncmp(buf, "INTERFACES", 10) == 0) { + reply_len = hostapd_global_ctrl_iface_interfaces( + interfaces, buf + 10, reply, sizeof(buffer)); + } else if (os_strcmp(buf, "TERMINATE") == 0) { + eloop_terminate(); } else { wpa_printf(MSG_DEBUG, "Unrecognized global ctrl_iface command " "ignored"); @@ -2975,6 +3428,7 @@ send_reply: } +#ifndef CONFIG_CTRL_IFACE_UDP static char * hostapd_global_ctrl_iface_path(struct hapd_interfaces *interface) { char *buf; @@ -2994,10 +3448,95 @@ static char * hostapd_global_ctrl_iface_path(struct hapd_interfaces *interface) buf[len - 1] = '\0'; return buf; } +#endif /* CONFIG_CTRL_IFACE_UDP */ int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface) { +#ifdef CONFIG_CTRL_IFACE_UDP + int port = HOSTAPD_GLOBAL_CTRL_IFACE_PORT; + char p[32] = { 0 }; + char *pos; + struct addrinfo hints = { 0 }, *res, *saveres; + int n; + + if (interface->global_ctrl_sock > -1) { + wpa_printf(MSG_DEBUG, "ctrl_iface already exists!"); + return 0; + } + + if (interface->global_iface_path == NULL) + return 0; + + pos = os_strstr(interface->global_iface_path, "udp:"); + if (pos) { + pos += 4; + port = atoi(pos); + if (port <= 0) { + wpa_printf(MSG_ERROR, "Invalid global ctrl UDP port"); + goto fail; + } + } + + dl_list_init(&interface->global_ctrl_dst); + interface->global_ctrl_sock = -1; + os_get_random(gcookie, COOKIE_LEN); + +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + hints.ai_flags = AI_PASSIVE; +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ + +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + hints.ai_family = AF_INET6; +#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ + hints.ai_family = AF_INET; +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ + hints.ai_socktype = SOCK_DGRAM; + +try_again: + os_snprintf(p, sizeof(p), "%d", port); + n = getaddrinfo(NULL, p, &hints, &res); + if (n) { + wpa_printf(MSG_ERROR, "getaddrinfo(): %s", gai_strerror(n)); + goto fail; + } + + saveres = res; + interface->global_ctrl_sock = socket(res->ai_family, res->ai_socktype, + res->ai_protocol); + if (interface->global_ctrl_sock < 0) { + wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno)); + goto fail; + } + + if (bind(interface->global_ctrl_sock, res->ai_addr, res->ai_addrlen) < + 0) { + port++; + if ((port - HOSTAPD_GLOBAL_CTRL_IFACE_PORT) < + HOSTAPD_GLOBAL_CTRL_IFACE_PORT_LIMIT && !pos) + goto try_again; + wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno)); + goto fail; + } + + freeaddrinfo(saveres); + + wpa_printf(MSG_DEBUG, "global ctrl_iface_init UDP port: %d", port); + + if (eloop_register_read_sock(interface->global_ctrl_sock, + hostapd_global_ctrl_iface_receive, + interface, NULL) < 0) { + hostapd_global_ctrl_iface_deinit(interface); + return -1; + } + + return 0; + +fail: + if (interface->global_ctrl_sock >= 0) + close(interface->global_ctrl_sock); + return -1; +#else /* CONFIG_CTRL_IFACE_UDP */ struct sockaddr_un addr; int s = -1; char *fname = NULL; @@ -3103,18 +3642,22 @@ fail: os_free(fname); } return -1; +#endif /* CONFIG_CTRL_IFACE_UDP */ } void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interfaces) { +#ifndef CONFIG_CTRL_IFACE_UDP char *fname = NULL; +#endif /* CONFIG_CTRL_IFACE_UDP */ struct wpa_ctrl_dst *dst, *prev; if (interfaces->global_ctrl_sock > -1) { eloop_unregister_read_sock(interfaces->global_ctrl_sock); close(interfaces->global_ctrl_sock); interfaces->global_ctrl_sock = -1; +#ifndef CONFIG_CTRL_IFACE_UDP fname = hostapd_global_ctrl_iface_path(interfaces); if (fname) { unlink(fname); @@ -3134,18 +3677,15 @@ void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interfaces) strerror(errno)); } } +#endif /* CONFIG_CTRL_IFACE_UDP */ } os_free(interfaces->global_iface_path); interfaces->global_iface_path = NULL; - dst = interfaces->global_ctrl_dst; - interfaces->global_ctrl_dst = NULL; - while (dst) { - prev = dst; - dst = dst->next; - os_free(prev); - } + dl_list_for_each_safe(dst, prev, &interfaces->global_ctrl_dst, + struct wpa_ctrl_dst, list) + os_free(dst); } @@ -3154,6 +3694,7 @@ static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, const char *buf, size_t len) { struct wpa_ctrl_dst *dst, *next; + struct dl_list *ctrl_dst; struct msghdr msg; int idx; struct iovec io[2]; @@ -3162,13 +3703,13 @@ static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, if (type != WPA_MSG_ONLY_GLOBAL) { s = hapd->ctrl_sock; - dst = hapd->ctrl_dst; + ctrl_dst = &hapd->ctrl_dst; } else { s = hapd->iface->interfaces->global_ctrl_sock; - dst = hapd->iface->interfaces->global_ctrl_dst; + ctrl_dst = &hapd->iface->interfaces->global_ctrl_dst; } - if (s < 0 || dst == NULL) + if (s < 0 || dl_list_empty(ctrl_dst)) return; os_snprintf(levelstr, sizeof(levelstr), "<%d>", level); @@ -3181,12 +3722,10 @@ static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, msg.msg_iovlen = 2; idx = 0; - while (dst) { - next = dst->next; + dl_list_for_each_safe(dst, next, ctrl_dst, struct wpa_ctrl_dst, list) { if (level >= dst->debug_level) { - wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor send", - (u8 *) dst->addr.sun_path, dst->addrlen - - offsetof(struct sockaddr_un, sun_path)); + sockaddr_print(MSG_DEBUG, "CTRL_IFACE monitor send", + &dst->addr, dst->addrlen); msg.msg_name = &dst->addr; msg.msg_namelen = dst->addrlen; if (sendmsg(s, &msg, 0) < 0) { @@ -3210,7 +3749,6 @@ static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, dst->errors = 0; } idx++; - dst = next; } } diff --git a/contrib/wpa/hostapd/defconfig b/contrib/wpa/hostapd/defconfig index 430f7584b167..4659dd1e6bcb 100644 --- a/contrib/wpa/hostapd/defconfig +++ b/contrib/wpa/hostapd/defconfig @@ -18,6 +18,9 @@ CONFIG_DRIVER_HOSTAP=y # Driver interface for drivers using the nl80211 kernel interface CONFIG_DRIVER_NL80211=y +# QCA vendor extensions to nl80211 +#CONFIG_DRIVER_NL80211_QCA=y + # driver_nl80211.c requires libnl. If you are compiling it yourself # you may need to point hostapd to your version of libnl. # @@ -246,6 +249,9 @@ CONFIG_IPV6=y # Should we use epoll instead of select? Select is used by default. #CONFIG_ELOOP_EPOLL=y +# Should we use kqueue instead of select? Select is used by default. +#CONFIG_ELOOP_KQUEUE=y + # Select TLS implementation # openssl = OpenSSL (default) # gnutls = GnuTLS @@ -326,3 +332,14 @@ CONFIG_IPV6=y # http://wireless.kernel.org/en/users/Documentation/acs # #CONFIG_ACS=y + +# Multiband Operation support +# These extentions facilitate efficient use of multiple frequency bands +# available to the AP and the devices that may associate with it. +#CONFIG_MBO=y + +# Client Taxonomy +# Has the AP retain the Probe Request and (Re)Association Request frames from +# a client, from which a signature can be produced which can identify the model +# of client device like "Nexus 6P" or "iPhone 5s". +#CONFIG_TAXONOMY=y diff --git a/contrib/wpa/hostapd/hapd_module_tests.c b/contrib/wpa/hostapd/hapd_module_tests.c index f7887ebfb7d3..a5016f22bf09 100644 --- a/contrib/wpa/hostapd/hapd_module_tests.c +++ b/contrib/wpa/hostapd/hapd_module_tests.c @@ -9,6 +9,7 @@ #include "utils/includes.h" #include "utils/common.h" +#include "utils/module_tests.h" int hapd_module_tests(void) { diff --git a/contrib/wpa/hostapd/hlr_auc_gw.c b/contrib/wpa/hostapd/hlr_auc_gw.c index 84d0308262e6..2117d3423a1b 100644 --- a/contrib/wpa/hostapd/hlr_auc_gw.c +++ b/contrib/wpa/hostapd/hlr_auc_gw.c @@ -1,6 +1,6 @@ /* * HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator - * Copyright (c) 2005-2007, 2012-2013, Jouni Malinen + * Copyright (c) 2005-2007, 2012-2016, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -284,7 +284,7 @@ static int read_gsm_triplets(const char *fname) f = fopen(fname, "r"); if (f == NULL) { - printf("Could not open GSM tripler data file '%s'\n", fname); + printf("Could not open GSM triplet data file '%s'\n", fname); return -1; } @@ -312,66 +312,40 @@ static int read_gsm_triplets(const char *fname) } /* IMSI */ - pos2 = strchr(pos, ':'); - if (pos2 == NULL) { - printf("%s:%d - Invalid IMSI (%s)\n", - fname, line, pos); - ret = -1; - break; - } - *pos2 = '\0'; - if (strlen(pos) >= sizeof(g->imsi)) { - printf("%s:%d - Too long IMSI (%s)\n", - fname, line, pos); + pos2 = NULL; + pos = str_token(buf, ":", &pos2); + if (!pos || os_strlen(pos) >= sizeof(g->imsi)) { + printf("%s:%d - Invalid IMSI\n", fname, line); ret = -1; break; } os_strlcpy(g->imsi, pos, sizeof(g->imsi)); - pos = pos2 + 1; /* Kc */ - pos2 = strchr(pos, ':'); - if (pos2 == NULL) { - printf("%s:%d - Invalid Kc (%s)\n", fname, line, pos); + pos = str_token(buf, ":", &pos2); + if (!pos || os_strlen(pos) != 16 || hexstr2bin(pos, g->kc, 8)) { + printf("%s:%d - Invalid Kc\n", fname, line); ret = -1; break; } - *pos2 = '\0'; - if (strlen(pos) != 16 || hexstr2bin(pos, g->kc, 8)) { - printf("%s:%d - Invalid Kc (%s)\n", fname, line, pos); - ret = -1; - break; - } - pos = pos2 + 1; /* SRES */ - pos2 = strchr(pos, ':'); - if (pos2 == NULL) { - printf("%s:%d - Invalid SRES (%s)\n", fname, line, - pos); + pos = str_token(buf, ":", &pos2); + if (!pos || os_strlen(pos) != 8 || + hexstr2bin(pos, g->sres, 4)) { + printf("%s:%d - Invalid SRES\n", fname, line); ret = -1; break; } - *pos2 = '\0'; - if (strlen(pos) != 8 || hexstr2bin(pos, g->sres, 4)) { - printf("%s:%d - Invalid SRES (%s)\n", fname, line, - pos); - ret = -1; - break; - } - pos = pos2 + 1; /* RAND */ - pos2 = strchr(pos, ':'); - if (pos2) - *pos2 = '\0'; - if (strlen(pos) != 32 || hexstr2bin(pos, g->_rand, 16)) { - printf("%s:%d - Invalid RAND (%s)\n", fname, line, - pos); + pos = str_token(buf, ":", &pos2); + if (!pos || os_strlen(pos) != 32 || + hexstr2bin(pos, g->_rand, 16)) { + printf("%s:%d - Invalid RAND\n", fname, line); ret = -1; break; } - pos = pos2 + 1; g->next = gsm_db; gsm_db = g; @@ -450,86 +424,58 @@ static int read_milenage(const char *fname) } /* IMSI */ - pos2 = strchr(pos, ' '); - if (pos2 == NULL) { - printf("%s:%d - Invalid IMSI (%s)\n", - fname, line, pos); - ret = -1; - break; - } - *pos2 = '\0'; - if (strlen(pos) >= sizeof(m->imsi)) { - printf("%s:%d - Too long IMSI (%s)\n", - fname, line, pos); + pos2 = NULL; + pos = str_token(buf, " ", &pos2); + if (!pos || os_strlen(pos) >= sizeof(m->imsi)) { + printf("%s:%d - Invalid IMSI\n", fname, line); ret = -1; break; } os_strlcpy(m->imsi, pos, sizeof(m->imsi)); - pos = pos2 + 1; /* Ki */ - pos2 = strchr(pos, ' '); - if (pos2 == NULL) { - printf("%s:%d - Invalid Ki (%s)\n", fname, line, pos); + pos = str_token(buf, " ", &pos2); + if (!pos || os_strlen(pos) != 32 || + hexstr2bin(pos, m->ki, 16)) { + printf("%s:%d - Invalid Ki\n", fname, line); ret = -1; break; } - *pos2 = '\0'; - if (strlen(pos) != 32 || hexstr2bin(pos, m->ki, 16)) { - printf("%s:%d - Invalid Ki (%s)\n", fname, line, pos); - ret = -1; - break; - } - pos = pos2 + 1; /* OPc */ - pos2 = strchr(pos, ' '); - if (pos2 == NULL) { - printf("%s:%d - Invalid OPc (%s)\n", fname, line, pos); + pos = str_token(buf, " ", &pos2); + if (!pos || os_strlen(pos) != 32 || + hexstr2bin(pos, m->opc, 16)) { + printf("%s:%d - Invalid OPc\n", fname, line); ret = -1; break; } - *pos2 = '\0'; - if (strlen(pos) != 32 || hexstr2bin(pos, m->opc, 16)) { - printf("%s:%d - Invalid OPc (%s)\n", fname, line, pos); - ret = -1; - break; - } - pos = pos2 + 1; /* AMF */ - pos2 = strchr(pos, ' '); - if (pos2 == NULL) { - printf("%s:%d - Invalid AMF (%s)\n", fname, line, pos); + pos = str_token(buf, " ", &pos2); + if (!pos || os_strlen(pos) != 4 || hexstr2bin(pos, m->amf, 2)) { + printf("%s:%d - Invalid AMF\n", fname, line); ret = -1; break; } - *pos2 = '\0'; - if (strlen(pos) != 4 || hexstr2bin(pos, m->amf, 2)) { - printf("%s:%d - Invalid AMF (%s)\n", fname, line, pos); - ret = -1; - break; - } - pos = pos2 + 1; /* SQN */ - pos2 = strchr(pos, ' '); - if (pos2) - *pos2 = '\0'; - if (strlen(pos) != 12 || hexstr2bin(pos, m->sqn, 6)) { - printf("%s:%d - Invalid SEQ (%s)\n", fname, line, pos); + pos = str_token(buf, " ", &pos2); + if (!pos || os_strlen(pos) != 12 || + hexstr2bin(pos, m->sqn, 6)) { + printf("%s:%d - Invalid SEQ\n", fname, line); ret = -1; break; } - if (pos2) { - pos = pos2 + 1; + pos = str_token(buf, " ", &pos2); + if (pos) { m->res_len = atoi(pos); if (m->res_len && (m->res_len < EAP_AKA_RES_MIN_LEN || m->res_len > EAP_AKA_RES_MAX_LEN)) { - printf("%s:%d - Invalid RES_len (%s)\n", - fname, line, pos); + printf("%s:%d - Invalid RES_len\n", + fname, line); ret = -1; break; } @@ -1027,7 +973,7 @@ static void usage(void) { printf("HLR/AuC testing gateway for hostapd EAP-SIM/AKA " "database/authenticator\n" - "Copyright (c) 2005-2007, 2012-2013, Jouni Malinen \n" + "Copyright (c) 2005-2016, Jouni Malinen \n" "\n" "usage:\n" "hlr_auc_gw [-hu] [-s] [-g] " diff --git a/contrib/wpa/hostapd/hostapd.conf b/contrib/wpa/hostapd/hostapd.conf index a0071f7d82c4..fa9a855a6e5f 100644 --- a/contrib/wpa/hostapd/hostapd.conf +++ b/contrib/wpa/hostapd/hostapd.conf @@ -3,6 +3,8 @@ # AP netdevice name (without 'ap' postfix, i.e., wlan0 uses wlan0ap for # management frames with the Host AP driver); wlan0 with many nl80211 drivers +# Note: This attribute can be overridden by the values supplied with the '-i' +# command line parameter. interface=wlan0 # In case of atheros and nl80211 driver interfaces, an additional @@ -125,11 +127,13 @@ ssid=test # ieee80211d=1 and local_pwr_constraint configured. #spectrum_mgmt_required=1 -# Operation mode (a = IEEE 802.11a, b = IEEE 802.11b, g = IEEE 802.11g, -# ad = IEEE 802.11ad (60 GHz); a/g options are used with IEEE 802.11n, too, to -# specify band). When using ACS (see channel parameter), a special value "any" -# can be used to indicate that any support band can be used. This special case -# is currently supported only with drivers with which offloaded ACS is used. +# Operation mode (a = IEEE 802.11a (5 GHz), b = IEEE 802.11b (2.4 GHz), +# g = IEEE 802.11g (2.4 GHz), ad = IEEE 802.11ad (60 GHz); a/g options are used +# with IEEE 802.11n (HT), too, to specify band). For IEEE 802.11ac (VHT), this +# needs to be set to hw_mode=a. When using ACS (see channel parameter), a +# special value "any" can be used to indicate that any support band can be used. +# This special case is currently supported only with drivers with which +# offloaded ACS is used. # Default: IEEE 802.11b hw_mode=g @@ -173,7 +177,7 @@ channel=1 # Channel list restriction. This option allows hostapd to select one of the # provided channels when a channel should be automatically selected. # Channel list can be provided as range using hyphen ('-') or individual -# channels can be specified by space (' ') seperated values +# channels can be specified by space (' ') separated values # Default: all channels allowed in selected hw_mode #chanlist=100 104 108 112 116 #chanlist=1 6 11-13 @@ -192,16 +196,16 @@ dtim_period=2 # (default: 2007) max_num_sta=255 -# RTS/CTS threshold; 2347 = disabled (default); range 0..2347 +# RTS/CTS threshold; -1 = disabled (default); range -1..65535 # If this field is not included in hostapd.conf, hostapd will not control # RTS threshold and 'iwconfig wlan# rts ' can be used to set it. -rts_threshold=2347 +rts_threshold=-1 -# Fragmentation threshold; 2346 = disabled (default); range 256..2346 +# Fragmentation threshold; -1 = disabled (default); range -1, 256..2346 # If this field is not included in hostapd.conf, hostapd will not control # fragmentation threshold and 'iwconfig wlan# frag ' can be used to set # it. -fragm_threshold=2346 +fragm_threshold=-1 # Rate configuration # Default is to enable all rates supported by the hardware. This configuration @@ -267,13 +271,27 @@ auth_algs=3 # requests for broadcast SSID ignore_broadcast_ssid=0 -# Additional vendor specfic elements for Beacon and Probe Response frames +# Do not reply to broadcast Probe Request frames from unassociated STA if there +# is no room for additional stations (max_num_sta). This can be used to +# discourage a STA from trying to associate with this AP if the association +# would be rejected due to maximum STA limit. +# Default: 0 (disabled) +#no_probe_resp_if_max_sta=0 + +# Additional vendor specific elements for Beacon and Probe Response frames # This parameter can be used to add additional vendor specific element(s) into # the end of the Beacon and Probe Response frames. The format for these # element(s) is a hexdump of the raw information elements (id+len+payload for # one or more elements) #vendor_elements=dd0411223301 +# Additional vendor specific elements for (Re)Association Response frames +# This parameter can be used to add additional vendor specific element(s) into +# the end of the (Re)Association Response frames. The format for these +# element(s) is a hexdump of the raw information elements (id+len+payload for +# one or more elements) +#assocresp_elements=dd0411223301 + # TX queue parameters (EDCF / bursting) # tx_queue__ # queues: data0, data1, data2, data3, after_beacon, beacon @@ -470,6 +488,7 @@ wmm_ac_vo_acm=0 # 0 = disabled (default) # 1 = enabled # Note: You will also need to enable WMM for full HT functionality. +# Note: hw_mode=g (2.4 GHz) and hw_mode=a (5 GHz) is used to specify the band. #ieee80211n=1 # ht_capab: HT capabilities (list of flags) @@ -523,6 +542,7 @@ wmm_ac_vo_acm=0 # 0 = disabled (default) # 1 = enabled # Note: You will also need to enable WMM for full VHT functionality. +# Note: hw_mode=a is used to specify that 5 GHz band is used with VHT. #ieee80211ac=1 # vht_capab: VHT capabilities (list of flags) @@ -605,9 +625,9 @@ wmm_ac_vo_acm=0 # VHT TXOP PS: [VHT-TXOP-PS] # Indicates whether or not the AP supports VHT TXOP Power Save Mode # or whether or not the STA is in VHT TXOP Power Save mode -# 0 = VHT AP doesnt support VHT TXOP PS mode (OR) VHT Sta not in VHT TXOP PS +# 0 = VHT AP doesn't support VHT TXOP PS mode (OR) VHT STA not in VHT TXOP PS # mode -# 1 = VHT AP supports VHT TXOP PS mode (OR) VHT Sta is in VHT TXOP power save +# 1 = VHT AP supports VHT TXOP PS mode (OR) VHT STA is in VHT TXOP power save # mode # # +HTC-VHT Capable: [HTC-VHT] @@ -665,6 +685,13 @@ wmm_ac_vo_acm=0 # #vht_oper_centr_freq_seg1_idx=159 +# Workaround to use station's nsts capability in (Re)Association Response frame +# This may be needed with some deployed devices as an interoperability +# workaround for beamforming if the AP's capability is greater than the +# station's capability. This is disabled by default and can be enabled by +# setting use_sta_nsts=1. +#use_sta_nsts=0 + ##### IEEE 802.1X-2004 related configuration ################################## # Require IEEE 802.1X authorization @@ -788,6 +815,11 @@ eap_server=0 # -respout /tmp/ocsp-cache.der #ocsp_stapling_response=/tmp/ocsp-cache.der +# Cached OCSP stapling response list (DER encoded OCSPResponseList) +# This is similar to ocsp_stapling_response, but the extended version defined in +# RFC 6961 to allow multiple OCSP responses to be provided. +#ocsp_stapling_response_multi=/tmp/ocsp-multi-cache.der + # dh_file: File path to DH/DSA parameters file (in PEM format) # This is an optional configuration file for setting parameters for an # ephemeral DH key exchange. In most cases, the default RSA authentication does @@ -825,6 +857,11 @@ eap_server=0 #eap_sim_db=unix:/tmp/hlr_auc_gw.sock #eap_sim_db=unix:/tmp/hlr_auc_gw.sock db=/tmp/hostapd.db +# EAP-SIM DB request timeout +# This parameter sets the maximum time to wait for a database request response. +# The parameter value is in seconds. +#eap_sim_db_timeout=1 + # Encryption key for EAP-FAST PAC-Opaque values. This key must be a secret, # random value. It is configured as a 16-octet value in hex format. It can be # generated, e.g., with the following command: @@ -888,11 +925,23 @@ eap_server=0 # The own IP address of the access point (used as NAS-IP-Address) own_ip_addr=127.0.0.1 -# Optional NAS-Identifier string for RADIUS messages. When used, this should be -# a unique to the NAS within the scope of the RADIUS server. For example, a -# fully qualified domain name can be used here. +# NAS-Identifier string for RADIUS messages. When used, this should be unique +# to the NAS within the scope of the RADIUS server. Please note that hostapd +# uses a separate RADIUS client for each BSS and as such, a unique +# nas_identifier value should be configured separately for each BSS. This is +# particularly important for cases where RADIUS accounting is used +# (Accounting-On/Off messages are interpreted as clearing all ongoing sessions +# and that may get interpreted as applying to all BSSes if the same +# NAS-Identifier value is used.) For example, a fully qualified domain name +# prefixed with a unique identifier of the BSS (e.g., BSSID) can be used here. +# # When using IEEE 802.11r, nas_identifier must be set and must be between 1 and # 48 octets long. +# +# It is mandatory to configure either own_ip_addr or nas_identifier to be +# compliant with the RADIUS protocol. When using RADIUS accounting, it is +# strongly recommended that nas_identifier is set to a unique value for each +# BSS. #nas_identifier=ap.example.com # RADIUS client forced local IP address for the access point @@ -957,6 +1006,17 @@ own_ip_addr=127.0.0.1 # 2 = required; reject authentication if RADIUS server does not include VLAN ID #dynamic_vlan=0 +# Per-Station AP_VLAN interface mode +# If enabled, each station is assigned its own AP_VLAN interface. +# This implies per-station group keying and ebtables filtering of inter-STA +# traffic (when passed through the AP). +# If the sta is not assigned to any VLAN, then its AP_VLAN interface will be +# added to the bridge given by the "bridge" configuration option (see above). +# Otherwise, it will be added to the per-VLAN bridge. +# 0 = disabled (default) +# 1 = enabled +#per_sta_vif=0 + # VLAN interface list for dynamic VLAN mode is read from a separate text file. # This list is used to map VLAN ID from the RADIUS server to a network # interface. Each station is bound to one interface in the same way as with @@ -1035,6 +1095,9 @@ own_ip_addr=127.0.0.1 # # DAS require Event-Timestamp #radius_das_require_event_timestamp=1 +# +# DAS require Message-Authenticator +#radius_das_require_message_authenticator=1 ##### RADIUS authentication server configuration ############################## @@ -1228,6 +1291,7 @@ own_ip_addr=127.0.0.1 # PMK-R1 Key Holder identifier (dot11FTR1KeyHolderID) # 6-octet identifier as a hex string. +# Defaults to BSSID. #r1_key_holder=000102030405 # Reassociation deadline in time units (TUs / 1.024 ms; range 1000..65535) @@ -1684,6 +1748,24 @@ own_ip_addr=127.0.0.1 # username/password #nai_realm=0,example.org,13[5:6],21[2:4][5:7] +# Arbitrary ANQP-element configuration +# Additional ANQP-elements with arbitrary values can be defined by specifying +# their contents in raw format as a hexdump of the payload. Note that these +# values will override ANQP-element contents that may have been specified in the +# more higher layer configuration parameters listed above. +# format: anqp_elem=: +# For example, AP Geospatial Location ANQP-element with unknown location: +#anqp_elem=265:0000 +# For example, AP Civic Location ANQP-element with unknown location: +#anqp_elem=266:000000 + +# GAS Address 3 behavior +# 0 = P2P specification (Address3 = AP BSSID) workaround enabled by default +# based on GAS request Address3 +# 1 = IEEE 802.11 standard compliant regardless of GAS request Address3 +# 2 = Force non-compliant behavior (Address3 = AP BSSID for all cases) +#gas_address3=0 + # QoS Map Set configuration # # Comma delimited QoS Map Set in decimal values @@ -1823,6 +1905,27 @@ own_ip_addr=127.0.0.1 # Transitioning between states). #fst_llt=100 +##### Radio measurements / location ########################################### + +# The content of a LCI measurement subelement +#lci= + +# The content of a location civic measurement subelement +#civic= + +# Enable neighbor report via radio measurements +#rrm_neighbor_report=1 + +# Publish fine timing measurement (FTM) responder functionality +# This parameter only controls publishing via Extended Capabilities element. +# Actual functionality is managed outside hostapd. +#ftm_responder=0 + +# Publish fine timing measurement (FTM) initiator functionality +# This parameter only controls publishing via Extended Capabilities element. +# Actual functionality is managed outside hostapd. +#ftm_initiator=0 + ##### TESTING OPTIONS ######################################################### # # The options in this section are only available when the build configuration @@ -1844,6 +1947,10 @@ own_ip_addr=127.0.0.1 # # Corrupt Key MIC in GTK rekey EAPOL-Key frames with the given probability #corrupt_gtk_rekey_mic_probability=0.0 +# +# Include only ECSA IE without CSA IE where possible +# (channel switch operating class is needed) +#ecsa_ie_only=0 ##### Multiple BSSID support ################################################## # @@ -1866,6 +1973,10 @@ own_ip_addr=127.0.0.1 # - is not the same as the MAC address of the radio # - is not the same as any other explicitly specified BSSID # +# Alternatively, the 'use_driver_iface_addr' parameter can be used to request +# hostapd to use the driver auto-generated interface address (e.g., to use the +# exact MAC addresses allocated to the device). +# # Not all drivers support multiple BSSes. The exact mechanism for determining # the driver capabilities is driver specific. With the current (i.e., a recent # kernel) drivers using nl80211, this information can be checked with "iw list" diff --git a/contrib/wpa/hostapd/hostapd_cli.c b/contrib/wpa/hostapd/hostapd_cli.c index 46c2f37e4601..5e6254244b31 100644 --- a/contrib/wpa/hostapd/hostapd_cli.c +++ b/contrib/wpa/hostapd/hostapd_cli.c @@ -1,6 +1,6 @@ /* * hostapd - command line interface for hostapd daemon - * Copyright (c) 2004-2015, Jouni Malinen + * Copyright (c) 2004-2016, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -15,79 +15,13 @@ #include "utils/eloop.h" #include "utils/edit.h" #include "common/version.h" +#include "common/cli.h" +#ifndef CONFIG_NO_CTRL_IFACE static const char *const hostapd_cli_version = "hostapd_cli v" VERSION_STR "\n" -"Copyright (c) 2004-2015, Jouni Malinen and contributors"; - - -static const char *const hostapd_cli_license = -"This software may be distributed under the terms of the BSD license.\n" -"See README for more details.\n"; - -static const char *const hostapd_cli_full_license = -"This software may be distributed under the terms of the BSD license.\n" -"\n" -"Redistribution and use in source and binary forms, with or without\n" -"modification, are permitted provided that the following conditions are\n" -"met:\n" -"\n" -"1. Redistributions of source code must retain the above copyright\n" -" notice, this list of conditions and the following disclaimer.\n" -"\n" -"2. Redistributions in binary form must reproduce the above copyright\n" -" notice, this list of conditions and the following disclaimer in the\n" -" documentation and/or other materials provided with the distribution.\n" -"\n" -"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n" -" names of its contributors may be used to endorse or promote products\n" -" derived from this software without specific prior written permission.\n" -"\n" -"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n" -"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n" -"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n" -"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n" -"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n" -"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n" -"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n" -"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n" -"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n" -"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n" -"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" -"\n"; - -static const char *const commands_help = -"Commands:\n" -" mib get MIB variables (dot1x, dot11, radius)\n" -" sta get MIB variables for one station\n" -" all_sta get MIB variables for all stations\n" -" new_sta add a new station\n" -" deauthenticate deauthenticate a station\n" -" disassociate disassociate a station\n" -#ifdef CONFIG_IEEE80211W -" sa_query send SA Query to a station\n" -#endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_WPS -" wps_pin [timeout] [addr] add WPS Enrollee PIN\n" -" wps_check_pin verify PIN checksum\n" -" wps_pbc indicate button pushed to initiate PBC\n" -" wps_cancel cancel the pending WPS operation\n" -#ifdef CONFIG_WPS_NFC -" wps_nfc_tag_read report read NFC tag with WPS data\n" -" wps_nfc_config_token build NFC configuration token\n" -" wps_nfc_token manager NFC password token\n" -#endif /* CONFIG_WPS_NFC */ -" wps_ap_pin [params..] enable/disable AP PIN\n" -" wps_config configure AP\n" -" wps_get_status show current WPS status\n" -#endif /* CONFIG_WPS */ -" get_config show current configuration\n" -" help show this usage help\n" -" interface [ifname] show interfaces/select interface\n" -" level change debug level\n" -" license show full hostapd_cli license\n" -" quit exit hostapd_cli\n"; +"Copyright (c) 2004-2016, Jouni Malinen and contributors"; static struct wpa_ctrl *ctrl_conn; static int hostapd_cli_quit = 0; @@ -104,6 +38,13 @@ static const char *pid_file = NULL; static const char *action_file = NULL; static int ping_interval = 5; static int interactive = 0; +static int event_handler_registered = 0; + +static DEFINE_DL_LIST(stations); /* struct cli_txt_entry */ + +static void print_help(FILE *stream, const char *cmd); +static char ** list_cmd_list(void); +static void hostapd_cli_receive(int sock, void *eloop_ctx, void *sock_ctx); static void usage(void) @@ -128,20 +69,49 @@ static void usage(void) " -B run a daemon in the background\n" " -i Interface to listen on (default: first " "interface found in the\n" - " socket path)\n\n" - "%s", - commands_help); + " socket path)\n\n"); + print_help(stderr, NULL); +} + + +static void register_event_handler(struct wpa_ctrl *ctrl) +{ + if (!ctrl_conn) + return; + if (interactive) { + event_handler_registered = + !eloop_register_read_sock(wpa_ctrl_get_fd(ctrl), + hostapd_cli_receive, + NULL, NULL); + } +} + + +static void unregister_event_handler(struct wpa_ctrl *ctrl) +{ + if (!ctrl_conn) + return; + if (interactive && event_handler_registered) { + eloop_unregister_read_sock(wpa_ctrl_get_fd(ctrl)); + event_handler_registered = 0; + } } static struct wpa_ctrl * hostapd_cli_open_connection(const char *ifname) { +#ifndef CONFIG_CTRL_IFACE_UDP char *cfile; int flen; +#endif /* !CONFIG_CTRL_IFACE_UDP */ if (ifname == NULL) return NULL; +#ifdef CONFIG_CTRL_IFACE_UDP + ctrl_conn = wpa_ctrl_open(ifname); + return ctrl_conn; +#else /* CONFIG_CTRL_IFACE_UDP */ flen = strlen(ctrl_iface_dir) + strlen(ifname) + 2; cfile = malloc(flen); if (cfile == NULL) @@ -158,6 +128,7 @@ static struct wpa_ctrl * hostapd_cli_open_connection(const char *ifname) ctrl_conn = wpa_ctrl_open2(cfile, client_socket_dir); free(cfile); return ctrl_conn; +#endif /* CONFIG_CTRL_IFACE_UDP */ } @@ -166,6 +137,7 @@ static void hostapd_cli_close_connection(void) if (ctrl_conn == NULL) return; + unregister_event_handler(ctrl_conn); if (hostapd_cli_attached) { wpa_ctrl_detach(ctrl_conn); hostapd_cli_attached = 0; @@ -215,6 +187,22 @@ static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd) } +static int hostapd_cli_cmd(struct wpa_ctrl *ctrl, const char *cmd, + int min_args, int argc, char *argv[]) +{ + char buf[4096]; + + if (argc < min_args) { + printf("Invalid %s command - at least %d argument%s required.\n", + cmd, min_args, min_args > 1 ? "s are" : " is"); + return -1; + } + if (write_cmd(buf, sizeof(buf), cmd, argc, argv) < 0) + return -1; + return wpa_ctrl_command(ctrl, buf); +} + + static int hostapd_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[]) { return wpa_ctrl_command(ctrl, "PING"); @@ -330,6 +318,21 @@ static int hostapd_cli_cmd_deauthenticate(struct wpa_ctrl *ctrl, int argc, } +static char ** hostapd_complete_deauthenticate(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + char **res = NULL; + + switch (arg) { + case 1: + res = cli_txt_list_array(&stations); + break; + } + + return res; +} + + static int hostapd_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -348,6 +351,37 @@ static int hostapd_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc, } +static char ** hostapd_complete_disassociate(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + char **res = NULL; + + switch (arg) { + case 1: + res = cli_txt_list_array(&stations); + break; + } + + return res; +} + + +#ifdef CONFIG_TAXONOMY +static int hostapd_cli_cmd_signature(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char buf[64]; + + if (argc != 1) { + printf("Invalid 'signature' command - exactly one argument, STA address, is required.\n"); + return -1; + } + os_snprintf(buf, sizeof(buf), "SIGNATURE %s", argv[0]); + return wpa_ctrl_command(ctrl, buf); +} +#endif /* CONFIG_TAXONOMY */ + + #ifdef CONFIG_IEEE80211W static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc, char *argv[]) @@ -720,15 +754,30 @@ static int hostapd_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc, static int hostapd_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - printf("%s", commands_help); + print_help(stdout, argc > 0 ? argv[0] : NULL); return 0; } +static char ** hostapd_cli_complete_help(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + char **res = NULL; + + switch (arg) { + case 1: + res = list_cmd_list(); + break; + } + + return res; +} + + static int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - printf("%s\n\n%s\n", hostapd_cli_version, hostapd_cli_full_license); + printf("%s\n\n%s\n", hostapd_cli_version, cli_full_license); return 0; } @@ -839,6 +888,28 @@ static int hostapd_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +static void hostapd_cli_get_interfaces(struct wpa_ctrl *ctrl, + struct dl_list *interfaces) +{ + struct dirent *dent; + DIR *dir; + + if (!ctrl || !interfaces) + return; + dir = opendir(ctrl_iface_dir); + if (dir == NULL) + return; + + while ((dent = readdir(dir))) { + if (strcmp(dent->d_name, ".") == 0 || + strcmp(dent->d_name, "..") == 0) + continue; + cli_txt_list_add(interfaces, dent->d_name); + } + closedir(dir); +} + + static void hostapd_cli_list_interfaces(struct wpa_ctrl *ctrl) { struct dirent *dent; @@ -880,6 +951,7 @@ static int hostapd_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc, printf("Connected to interface '%s.\n", ctrl_ifname); if (wpa_ctrl_attach(ctrl_conn) == 0) { hostapd_cli_attached = 1; + register_event_handler(ctrl_conn); } else { printf("Warning: Failed to attach to " "hostapd.\n"); @@ -892,6 +964,24 @@ static int hostapd_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc, } +static char ** hostapd_complete_interface(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + char **res = NULL; + DEFINE_DL_LIST(interfaces); + + switch (arg) { + case 1: + hostapd_cli_get_interfaces(ctrl_conn, &interfaces); + res = cli_txt_list_array(&interfaces); + cli_txt_list_flush(&interfaces); + break; + } + + return res; +} + + static int hostapd_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[]) { char cmd[256]; @@ -1068,68 +1158,245 @@ static int hostapd_cli_cmd_log_level(struct wpa_ctrl *ctrl, int argc, } +static int hostapd_cli_cmd_raw(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + if (argc == 0) + return -1; + return hostapd_cli_cmd(ctrl, argv[0], 0, argc - 1, &argv[1]); +} + + +static int hostapd_cli_cmd_pmksa(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "PMKSA"); +} + + +static int hostapd_cli_cmd_pmksa_flush(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "PMKSA_FLUSH"); +} + + +static int hostapd_cli_cmd_set_neighbor(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[2048]; + int res; + + if (argc < 3 || argc > 5) { + printf("Invalid set_neighbor command: needs 3-5 arguments\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "SET_NEIGHBOR %s %s %s %s %s", + argv[0], argv[1], argv[2], argc >= 4 ? argv[3] : "", + argc == 5 ? argv[4] : ""); + if (os_snprintf_error(sizeof(cmd), res)) { + printf("Too long SET_NEIGHBOR command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int hostapd_cli_cmd_remove_neighbor(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[400]; + int res; + + if (argc != 2) { + printf("Invalid remove_neighbor command: needs 2 arguments\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "REMOVE_NEIGHBOR %s %s", + argv[0], argv[1]); + if (os_snprintf_error(sizeof(cmd), res)) { + printf("Too long REMOVE_NEIGHBOR command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int hostapd_cli_cmd_req_lci(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 1) { + printf("Invalid req_lci command - requires destination address\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "REQ_LCI %s", argv[0]); + if (os_snprintf_error(sizeof(cmd), res)) { + printf("Too long REQ_LCI command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int hostapd_cli_cmd_req_range(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + if (argc < 4) { + printf("Invalid req_range command: needs at least 4 arguments - dest address, randomization interval, min AP count, and 1 to 16 AP addresses\n"); + return -1; + } + + return hostapd_cli_cmd(ctrl, "REQ_RANGE", 4, argc, argv); +} + + +static int hostapd_cli_cmd_driver_flags(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "DRIVER_FLAGS"); +} + + struct hostapd_cli_cmd { const char *cmd; int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]); + char ** (*completion)(const char *str, int pos); + const char *usage; }; static const struct hostapd_cli_cmd hostapd_cli_commands[] = { - { "ping", hostapd_cli_cmd_ping }, - { "mib", hostapd_cli_cmd_mib }, - { "relog", hostapd_cli_cmd_relog }, - { "status", hostapd_cli_cmd_status }, - { "sta", hostapd_cli_cmd_sta }, - { "all_sta", hostapd_cli_cmd_all_sta }, - { "new_sta", hostapd_cli_cmd_new_sta }, - { "deauthenticate", hostapd_cli_cmd_deauthenticate }, - { "disassociate", hostapd_cli_cmd_disassociate }, + { "ping", hostapd_cli_cmd_ping, NULL, + "= pings hostapd" }, + { "mib", hostapd_cli_cmd_mib, NULL, + "= get MIB variables (dot1x, dot11, radius)" }, + { "relog", hostapd_cli_cmd_relog, NULL, NULL }, + { "status", hostapd_cli_cmd_status, NULL, NULL }, + { "sta", hostapd_cli_cmd_sta, NULL, + " = get MIB variables for one station" }, + { "all_sta", hostapd_cli_cmd_all_sta, NULL, + "= get MIB variables for all stations" }, + { "new_sta", hostapd_cli_cmd_new_sta, NULL, + " = add a new station" }, + { "deauthenticate", hostapd_cli_cmd_deauthenticate, + hostapd_complete_deauthenticate, + " = deauthenticate a station" }, + { "disassociate", hostapd_cli_cmd_disassociate, + hostapd_complete_disassociate, + " = disassociate a station" }, +#ifdef CONFIG_TAXONOMY + { "signature", hostapd_cli_cmd_signature, NULL, + " = get taxonomy signature for a station" }, +#endif /* CONFIG_TAXONOMY */ #ifdef CONFIG_IEEE80211W - { "sa_query", hostapd_cli_cmd_sa_query }, + { "sa_query", hostapd_cli_cmd_sa_query, NULL, + " = send SA Query to a station" }, #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_WPS - { "wps_pin", hostapd_cli_cmd_wps_pin }, - { "wps_check_pin", hostapd_cli_cmd_wps_check_pin }, - { "wps_pbc", hostapd_cli_cmd_wps_pbc }, - { "wps_cancel", hostapd_cli_cmd_wps_cancel }, + { "wps_pin", hostapd_cli_cmd_wps_pin, NULL, + " [timeout] [addr] = add WPS Enrollee PIN" }, + { "wps_check_pin", hostapd_cli_cmd_wps_check_pin, NULL, + " = verify PIN checksum" }, + { "wps_pbc", hostapd_cli_cmd_wps_pbc, NULL, + "= indicate button pushed to initiate PBC" }, + { "wps_cancel", hostapd_cli_cmd_wps_cancel, NULL, + "= cancel the pending WPS operation" }, #ifdef CONFIG_WPS_NFC - { "wps_nfc_tag_read", hostapd_cli_cmd_wps_nfc_tag_read }, - { "wps_nfc_config_token", hostapd_cli_cmd_wps_nfc_config_token }, - { "wps_nfc_token", hostapd_cli_cmd_wps_nfc_token }, - { "nfc_get_handover_sel", hostapd_cli_cmd_nfc_get_handover_sel }, + { "wps_nfc_tag_read", hostapd_cli_cmd_wps_nfc_tag_read, NULL, + " = report read NFC tag with WPS data" }, + { "wps_nfc_config_token", hostapd_cli_cmd_wps_nfc_config_token, NULL, + " = build NFC configuration token" }, + { "wps_nfc_token", hostapd_cli_cmd_wps_nfc_token, NULL, + " = manager NFC password token" }, + { "nfc_get_handover_sel", hostapd_cli_cmd_nfc_get_handover_sel, NULL, + NULL }, #endif /* CONFIG_WPS_NFC */ - { "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin }, - { "wps_config", hostapd_cli_cmd_wps_config }, - { "wps_get_status", hostapd_cli_cmd_wps_get_status }, + { "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin, NULL, + " [params..] = enable/disable AP PIN" }, + { "wps_config", hostapd_cli_cmd_wps_config, NULL, + " = configure AP" }, + { "wps_get_status", hostapd_cli_cmd_wps_get_status, NULL, + "= show current WPS status" }, #endif /* CONFIG_WPS */ - { "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent }, - { "ess_disassoc", hostapd_cli_cmd_ess_disassoc }, - { "bss_tm_req", hostapd_cli_cmd_bss_tm_req }, - { "get_config", hostapd_cli_cmd_get_config }, - { "help", hostapd_cli_cmd_help }, - { "interface", hostapd_cli_cmd_interface }, + { "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent, NULL, NULL }, + { "ess_disassoc", hostapd_cli_cmd_ess_disassoc, NULL, NULL }, + { "bss_tm_req", hostapd_cli_cmd_bss_tm_req, NULL, NULL }, + { "get_config", hostapd_cli_cmd_get_config, NULL, + "= show current configuration" }, + { "help", hostapd_cli_cmd_help, hostapd_cli_complete_help, + "= show this usage help" }, + { "interface", hostapd_cli_cmd_interface, hostapd_complete_interface, + "[ifname] = show interfaces/select interface" }, #ifdef CONFIG_FST - { "fst", hostapd_cli_cmd_fst }, + { "fst", hostapd_cli_cmd_fst, NULL, NULL }, #endif /* CONFIG_FST */ - { "level", hostapd_cli_cmd_level }, - { "license", hostapd_cli_cmd_license }, - { "quit", hostapd_cli_cmd_quit }, - { "set", hostapd_cli_cmd_set }, - { "get", hostapd_cli_cmd_get }, - { "set_qos_map_set", hostapd_cli_cmd_set_qos_map_set }, - { "send_qos_map_conf", hostapd_cli_cmd_send_qos_map_conf }, - { "chan_switch", hostapd_cli_cmd_chan_switch }, - { "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif }, - { "hs20_deauth_req", hostapd_cli_cmd_hs20_deauth_req }, - { "vendor", hostapd_cli_cmd_vendor }, - { "enable", hostapd_cli_cmd_enable }, - { "reload", hostapd_cli_cmd_reload }, - { "disable", hostapd_cli_cmd_disable }, - { "erp_flush", hostapd_cli_cmd_erp_flush }, - { "log_level", hostapd_cli_cmd_log_level }, - { NULL, NULL } + { "raw", hostapd_cli_cmd_raw, NULL, NULL }, + { "level", hostapd_cli_cmd_level, NULL, + " = change debug level" }, + { "license", hostapd_cli_cmd_license, NULL, + "= show full hostapd_cli license" }, + { "quit", hostapd_cli_cmd_quit, NULL, + "= exit hostapd_cli" }, + { "set", hostapd_cli_cmd_set, NULL, NULL }, + { "get", hostapd_cli_cmd_get, NULL, NULL }, + { "set_qos_map_set", hostapd_cli_cmd_set_qos_map_set, NULL, NULL }, + { "send_qos_map_conf", hostapd_cli_cmd_send_qos_map_conf, NULL, NULL }, + { "chan_switch", hostapd_cli_cmd_chan_switch, NULL, NULL }, + { "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif, NULL, NULL }, + { "hs20_deauth_req", hostapd_cli_cmd_hs20_deauth_req, NULL, NULL }, + { "vendor", hostapd_cli_cmd_vendor, NULL, NULL }, + { "enable", hostapd_cli_cmd_enable, NULL, NULL }, + { "reload", hostapd_cli_cmd_reload, NULL, NULL }, + { "disable", hostapd_cli_cmd_disable, NULL, NULL }, + { "erp_flush", hostapd_cli_cmd_erp_flush, NULL, NULL }, + { "log_level", hostapd_cli_cmd_log_level, NULL, NULL }, + { "pmksa", hostapd_cli_cmd_pmksa, NULL, NULL }, + { "pmksa_flush", hostapd_cli_cmd_pmksa_flush, NULL, NULL }, + { "set_neighbor", hostapd_cli_cmd_set_neighbor, NULL, NULL }, + { "remove_neighbor", hostapd_cli_cmd_remove_neighbor, NULL, NULL }, + { "req_lci", hostapd_cli_cmd_req_lci, NULL, NULL }, + { "req_range", hostapd_cli_cmd_req_range, NULL, NULL }, + { "driver_flags", hostapd_cli_cmd_driver_flags, NULL, NULL }, + { NULL, NULL, NULL, NULL } }; +/* + * Prints command usage, lines are padded with the specified string. + */ +static void print_cmd_help(FILE *stream, const struct hostapd_cli_cmd *cmd, + const char *pad) +{ + char c; + size_t n; + + if (cmd->usage == NULL) + return; + fprintf(stream, "%s%s ", pad, cmd->cmd); + for (n = 0; (c = cmd->usage[n]); n++) { + fprintf(stream, "%c", c); + if (c == '\n') + fprintf(stream, "%s", pad); + } + fprintf(stream, "\n"); +} + + +static void print_help(FILE *stream, const char *cmd) +{ + int n; + + fprintf(stream, "commands:\n"); + for (n = 0; hostapd_cli_commands[n].cmd; n++) { + if (cmd == NULL || str_starts(hostapd_cli_commands[n].cmd, cmd)) + print_cmd_help(stream, &hostapd_cli_commands[n], " "); + } +} + + static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[]) { const struct hostapd_cli_cmd *cmd, *match = NULL; @@ -1169,6 +1436,34 @@ static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +static void cli_event(const char *str) +{ + const char *start, *s; + + start = os_strchr(str, '>'); + if (start == NULL) + return; + + start++; + + if (str_starts(start, AP_STA_CONNECTED)) { + s = os_strchr(start, ' '); + if (s == NULL) + return; + cli_txt_list_add(&stations, s + 1); + return; + } + + if (str_starts(start, AP_STA_DISCONNECTED)) { + s = os_strchr(start, ' '); + if (s == NULL) + return; + cli_txt_list_del_addr(&stations, s + 1); + return; + } +} + + static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read, int action_monitor) { @@ -1183,6 +1478,7 @@ static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read, if (action_monitor) hostapd_cli_action_process(buf, len); else { + cli_event(buf); if (in_read && first) printf("\n"); first = 0; @@ -1196,35 +1492,9 @@ static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read, } -#define max_args 10 - -static int tokenize_cmd(char *cmd, char *argv[]) +static void hostapd_cli_receive(int sock, void *eloop_ctx, void *sock_ctx) { - char *pos; - int argc = 0; - - pos = cmd; - for (;;) { - while (*pos == ' ') - pos++; - if (*pos == '\0') - break; - argv[argc] = pos; - argc++; - if (argc == max_args) - break; - if (*pos == '"') { - char *pos2 = os_strrchr(pos, '"'); - if (pos2) - pos = pos2 + 1; - } - while (*pos != '\0' && *pos != ' ') - pos++; - if (*pos == ' ') - *pos++ = '\0'; - } - - return argc; + hostapd_cli_recv_pending(ctrl_conn, 0, 0); } @@ -1240,6 +1510,7 @@ static void hostapd_cli_ping(void *eloop_ctx, void *timeout_ctx) printf("Connection to hostapd re-established\n"); if (wpa_ctrl_attach(ctrl_conn) == 0) { hostapd_cli_attached = 1; + register_event_handler(ctrl_conn); } else { printf("Warning: Failed to attach to " "hostapd.\n"); @@ -1274,17 +1545,82 @@ static void hostapd_cli_edit_eof_cb(void *ctx) } +static char ** list_cmd_list(void) +{ + char **res; + int i, count; + + count = ARRAY_SIZE(hostapd_cli_commands); + res = os_calloc(count + 1, sizeof(char *)); + if (res == NULL) + return NULL; + + for (i = 0; hostapd_cli_commands[i].cmd; i++) { + res[i] = os_strdup(hostapd_cli_commands[i].cmd); + if (res[i] == NULL) + break; + } + + return res; +} + + +static char ** hostapd_cli_cmd_completion(const char *cmd, const char *str, + int pos) +{ + int i; + + for (i = 0; hostapd_cli_commands[i].cmd; i++) { + if (os_strcasecmp(hostapd_cli_commands[i].cmd, cmd) != 0) + continue; + if (hostapd_cli_commands[i].completion) + return hostapd_cli_commands[i].completion(str, pos); + if (!hostapd_cli_commands[i].usage) + return NULL; + edit_clear_line(); + printf("\r%s\n", hostapd_cli_commands[i].usage); + edit_redraw(); + break; + } + + return NULL; +} + + +static char ** hostapd_cli_edit_completion_cb(void *ctx, const char *str, + int pos) +{ + char **res; + const char *end; + char *cmd; + + end = os_strchr(str, ' '); + if (end == NULL || str + pos < end) + return list_cmd_list(); + + cmd = os_malloc(pos + 1); + if (cmd == NULL) + return NULL; + os_memcpy(cmd, str, pos); + cmd[end - str] = '\0'; + res = hostapd_cli_cmd_completion(cmd, str, pos); + os_free(cmd); + return res; +} + + static void hostapd_cli_interactive(void) { printf("\nInteractive mode\n\n"); eloop_register_signal_terminate(hostapd_cli_eloop_terminate, NULL); edit_init(hostapd_cli_edit_cmd_cb, hostapd_cli_edit_eof_cb, - NULL, NULL, NULL, NULL); + hostapd_cli_edit_completion_cb, NULL, NULL, NULL); eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL); eloop_run(); + cli_txt_list_flush(&stations); edit_deinit(NULL, NULL); eloop_cancel_timeout(hostapd_cli_ping, NULL, NULL); } @@ -1388,8 +1724,7 @@ int main(int argc, char *argv[]) interactive = (argc == optind) && (action_file == NULL); if (interactive) { - printf("%s\n\n%s\n\n", hostapd_cli_version, - hostapd_cli_license); + printf("%s\n\n%s\n\n", hostapd_cli_version, cli_license); } if (eloop_init()) @@ -1437,6 +1772,7 @@ int main(int argc, char *argv[]) if (interactive || action_file) { if (wpa_ctrl_attach(ctrl_conn) == 0) { hostapd_cli_attached = 1; + register_event_handler(ctrl_conn); } else { printf("Warning: Failed to attach to hostapd.\n"); if (action_file) @@ -1444,7 +1780,7 @@ int main(int argc, char *argv[]) } } - if (daemonize && os_daemonize(pid_file)) + if (daemonize && os_daemonize(pid_file) && eloop_sock_requeue()) return -1; if (interactive) @@ -1454,8 +1790,18 @@ int main(int argc, char *argv[]) else wpa_request(ctrl_conn, argc - optind, &argv[optind]); + unregister_event_handler(ctrl_conn); os_free(ctrl_ifname); eloop_destroy(); hostapd_cli_cleanup(); return 0; } + +#else /* CONFIG_NO_CTRL_IFACE */ + +int main(int argc, char *argv[]) +{ + return -1; +} + +#endif /* CONFIG_NO_CTRL_IFACE */ diff --git a/contrib/wpa/hostapd/main.c b/contrib/wpa/hostapd/main.c index 6c7406af447e..2c8dbd30a274 100644 --- a/contrib/wpa/hostapd/main.c +++ b/contrib/wpa/hostapd/main.c @@ -1,6 +1,6 @@ /* * hostapd / main() - * Copyright (c) 2002-2015, Jouni Malinen + * Copyright (c) 2002-2016, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -171,7 +171,8 @@ static int hostapd_driver_init(struct hostapd_iface *iface) if (global.drv_priv[i] == NULL && wpa_drivers[i]->global_init) { - global.drv_priv[i] = wpa_drivers[i]->global_init(); + global.drv_priv[i] = + wpa_drivers[i]->global_init(iface->interfaces); if (global.drv_priv[i] == NULL) { wpa_printf(MSG_ERROR, "Failed to initialize " "driver '%s'", @@ -216,11 +217,20 @@ static int hostapd_driver_init(struct hostapd_iface *iface) iface->drv_flags = capa.flags; iface->smps_modes = capa.smps_modes; iface->probe_resp_offloads = capa.probe_resp_offloads; + /* + * Use default extended capa values from per-radio information + */ iface->extended_capa = capa.extended_capa; iface->extended_capa_mask = capa.extended_capa_mask; iface->extended_capa_len = capa.extended_capa_len; iface->drv_max_acl_mac_addrs = capa.max_acl_mac_addrs; + /* + * Override extended capa with per-interface type (AP), if + * available from the driver. + */ + hostapd_get_ext_capa(iface); + triggs = wpa_get_wowlan_triggers(conf->wowlan_triggers, &capa); if (triggs && hapd->driver->set_wowlan) { if (hapd->driver->set_wowlan(hapd->drv_priv, triggs)) @@ -241,7 +251,7 @@ static int hostapd_driver_init(struct hostapd_iface *iface) * interfaces. No actiual driver operations are started. */ static struct hostapd_iface * -hostapd_interface_init(struct hapd_interfaces *interfaces, +hostapd_interface_init(struct hapd_interfaces *interfaces, const char *if_name, const char *config_fname, int debug) { struct hostapd_iface *iface; @@ -251,6 +261,12 @@ hostapd_interface_init(struct hapd_interfaces *interfaces, iface = hostapd_init(interfaces, config_fname); if (!iface) return NULL; + + if (if_name) { + os_strlcpy(iface->conf->bss[0]->iface, if_name, + sizeof(iface->conf->bss[0]->iface)); + } + iface->interfaces = interfaces; for (k = 0; k < debug; k++) { @@ -260,7 +276,8 @@ hostapd_interface_init(struct hapd_interfaces *interfaces, if (iface->conf->bss[0]->iface[0] == '\0' && !hostapd_drv_none(iface->bss[0])) { - wpa_printf(MSG_ERROR, "Interface name not specified in %s", + wpa_printf(MSG_ERROR, + "Interface name not specified in %s, nor by '-i' parameter", config_fname); hostapd_interface_deinit_free(iface); return NULL; @@ -329,6 +346,7 @@ static int hostapd_global_init(struct hapd_interfaces *interfaces, wpa_printf(MSG_ERROR, "Failed to initialize event loop"); return -1; } + interfaces->eloop_initialized = 1; random_init(entropy_file); @@ -356,7 +374,7 @@ static int hostapd_global_init(struct hapd_interfaces *interfaces, } -static void hostapd_global_deinit(const char *pid_file) +static void hostapd_global_deinit(const char *pid_file, int eloop_initialized) { int i; @@ -374,7 +392,8 @@ static void hostapd_global_deinit(const char *pid_file) random_deinit(); - eloop_destroy(); + if (eloop_initialized) + eloop_destroy(); #ifndef CONFIG_NATIVE_WINDOWS closelog(); @@ -408,9 +427,16 @@ static int hostapd_global_run(struct hapd_interfaces *ifaces, int daemonize, } #endif /* EAP_SERVER_TNC */ - if (daemonize && os_daemonize(pid_file)) { - wpa_printf(MSG_ERROR, "daemon: %s", strerror(errno)); - return -1; + if (daemonize) { + if (os_daemonize(pid_file)) { + wpa_printf(MSG_ERROR, "daemon: %s", strerror(errno)); + return -1; + } + if (eloop_sock_requeue()) { + wpa_printf(MSG_ERROR, "eloop_sock_requeue: %s", + strerror(errno)); + return -1; + } } eloop_run(); @@ -425,7 +451,7 @@ static void show_version(void) "hostapd v" VERSION_STR "\n" "User space daemon for IEEE 802.11 AP management,\n" "IEEE 802.1X/WPA/WPA2/EAP/RADIUS Authenticator\n" - "Copyright (c) 2002-2015, Jouni Malinen " + "Copyright (c) 2002-2016, Jouni Malinen " "and contributors\n"); } @@ -437,7 +463,8 @@ static void usage(void) "\n" "usage: hostapd [-hdBKtv] [-P ] [-e ] " "\\\n" - " [-g ] [-G ] \\\n" + " [-g ] [-G ]\\\n" + " [-i ]\\\n" " \n" "\n" "options:\n" @@ -456,6 +483,8 @@ 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 */ + " -i list of interface names to use\n" + " -S start all the interfaces synchronously\n" " -t include timestamps in some debug messages\n" " -v show hostapd version\n"); @@ -466,9 +495,8 @@ static void usage(void) static const char * hostapd_msg_ifname_cb(void *ctx) { struct hostapd_data *hapd = ctx; - if (hapd && hapd->iconf && hapd->iconf->bss && - hapd->iconf->num_bss > 0 && hapd->iconf->bss[0]) - return hapd->iconf->bss[0]->iface; + if (hapd && hapd->conf) + return hapd->conf->iface; return NULL; } @@ -476,11 +504,16 @@ static const char * hostapd_msg_ifname_cb(void *ctx) static int hostapd_get_global_ctrl_iface(struct hapd_interfaces *interfaces, const char *path) { +#ifndef CONFIG_CTRL_IFACE_UDP char *pos; +#endif /* !CONFIG_CTRL_IFACE_UDP */ + os_free(interfaces->global_iface_path); interfaces->global_iface_path = os_strdup(path); if (interfaces->global_iface_path == NULL) return -1; + +#ifndef CONFIG_CTRL_IFACE_UDP pos = os_strrchr(interfaces->global_iface_path, '/'); if (pos == NULL) { wpa_printf(MSG_ERROR, "No '/' in the global control interface " @@ -492,6 +525,7 @@ static int hostapd_get_global_ctrl_iface(struct hapd_interfaces *interfaces, *pos = '\0'; interfaces->global_iface_name = pos + 1; +#endif /* !CONFIG_CTRL_IFACE_UDP */ return 0; } @@ -513,6 +547,43 @@ static int hostapd_get_ctrl_iface_group(struct hapd_interfaces *interfaces, } +static int hostapd_get_interface_names(char ***if_names, + size_t *if_names_size, + char *optarg) +{ + char *if_name, *tmp, **nnames; + size_t i; + + if (!optarg) + return -1; + if_name = strtok_r(optarg, ",", &tmp); + + while (if_name) { + nnames = os_realloc_array(*if_names, 1 + *if_names_size, + sizeof(char *)); + if (!nnames) + goto fail; + *if_names = nnames; + + (*if_names)[*if_names_size] = os_strdup(if_name); + if (!(*if_names)[*if_names_size]) + goto fail; + (*if_names_size)++; + if_name = strtok_r(NULL, ",", &tmp); + } + + return 0; + +fail: + for (i = 0; i < *if_names_size; i++) + os_free((*if_names)[i]); + os_free(*if_names); + *if_names = NULL; + *if_names_size = 0; + return -1; +} + + #ifdef CONFIG_WPS static int gen_uuid(const char *txt_addr) { @@ -570,6 +641,9 @@ int main(int argc, char *argv[]) #ifdef CONFIG_DEBUG_LINUX_TRACING int enable_trace_dbg = 0; #endif /* CONFIG_DEBUG_LINUX_TRACING */ + int start_ifaces_in_sync = 0; + char **if_names = NULL; + size_t if_names_size = 0; if (os_program_init()) return -1; @@ -584,10 +658,10 @@ int main(int argc, char *argv[]) interfaces.global_iface_path = NULL; interfaces.global_iface_name = NULL; interfaces.global_ctrl_sock = -1; - interfaces.global_ctrl_dst = NULL; + dl_list_init(&interfaces.global_ctrl_dst); for (;;) { - c = getopt(argc, argv, "b:Bde:f:hKP:Ttu:vg:G:"); + c = getopt(argc, argv, "b:Bde:f:hi:KP:STtu:vg:G:"); if (c < 0) break; switch (c) { @@ -644,10 +718,18 @@ int main(int argc, char *argv[]) bss_config = tmp_bss; bss_config[num_bss_configs++] = optarg; break; + case 'S': + start_ifaces_in_sync = 1; + break; #ifdef CONFIG_WPS case 'u': return gen_uuid(optarg); #endif /* CONFIG_WPS */ + case 'i': + if (hostapd_get_interface_names(&if_names, + &if_names_size, optarg)) + goto out; + break; default: usage(); break; @@ -705,13 +787,21 @@ int main(int argc, char *argv[]) /* Allocate and parse configuration for full interface files */ for (i = 0; i < interfaces.count; i++) { + char *if_name = NULL; + + if (i < if_names_size) + if_name = if_names[i]; + interfaces.iface[i] = hostapd_interface_init(&interfaces, + if_name, argv[optind + i], debug); if (!interfaces.iface[i]) { wpa_printf(MSG_ERROR, "Failed to initialize interface"); goto out; } + if (start_ifaces_in_sync) + interfaces.iface[i]->need_to_start_in_sync = 1; } /* Allocate and parse configuration for per-BSS files */ @@ -787,8 +877,9 @@ int main(int argc, char *argv[]) } os_free(interfaces.iface); - eloop_cancel_timeout(hostapd_periodic, &interfaces, NULL); - hostapd_global_deinit(pid_file); + if (interfaces.eloop_initialized) + eloop_cancel_timeout(hostapd_periodic, &interfaces, NULL); + hostapd_global_deinit(pid_file, interfaces.eloop_initialized); os_free(pid_file); if (log_file) @@ -797,6 +888,10 @@ int main(int argc, char *argv[]) os_free(bss_config); + for (i = 0; i < if_names_size; i++) + os_free(if_names[i]); + os_free(if_names); + fst_global_deinit(); os_program_deinit(); diff --git a/contrib/wpa/hs20/client/Android.mk b/contrib/wpa/hs20/client/Android.mk index b23ac17b4b62..e4db32208d50 100644 --- a/contrib/wpa/hs20/client/Android.mk +++ b/contrib/wpa/hs20/client/Android.mk @@ -4,7 +4,6 @@ INCLUDES = $(LOCAL_PATH) INCLUDES += $(LOCAL_PATH)/../../src/utils INCLUDES += $(LOCAL_PATH)/../../src/common INCLUDES += $(LOCAL_PATH)/../../src -INCLUDES += external/openssl/include INCLUDES += external/libxml2/include INCLUDES += external/curl/include INCLUDES += external/webkit/Source/WebKit/gtk @@ -55,6 +54,7 @@ OBJS += ../../src/crypto/crypto_internal.c OBJS += ../../src/crypto/md5-internal.c OBJS += ../../src/crypto/sha1-internal.c OBJS += ../../src/crypto/sha256-internal.c +OBJS += ../../src/crypto/tls_openssl_ocsp.c L_CFLAGS += -DEAP_TLS_OPENSSL diff --git a/contrib/wpa/hs20/client/Makefile b/contrib/wpa/hs20/client/Makefile index 94cd5f14df14..fc9b61940c4f 100644 --- a/contrib/wpa/hs20/client/Makefile +++ b/contrib/wpa/hs20/client/Makefile @@ -76,6 +76,7 @@ LIBS += -lcurl endif CFLAGS += -DEAP_TLS_OPENSSL +OBJS += ../../src/crypto/tls_openssl_ocsp.o LIBS += -lssl -lcrypto hs20-osu-client: $(OBJS) diff --git a/contrib/wpa/hs20/client/est.c b/contrib/wpa/hs20/client/est.c index ec05bc4e0f62..9f1519bf4e4e 100644 --- a/contrib/wpa/hs20/client/est.c +++ b/contrib/wpa/hs20/client/est.c @@ -16,6 +16,9 @@ #include #include #include +#ifdef OPENSSL_IS_BORINGSSL +#include +#endif /* OPENSSL_IS_BORINGSSL */ #include "common.h" #include "utils/base64.h" @@ -27,12 +30,28 @@ static int pkcs7_to_cert(struct hs20_osu_client *ctx, const u8 *pkcs7, size_t len, char *pem_file, char *der_file) { +#ifdef OPENSSL_IS_BORINGSSL + CBS pkcs7_cbs; +#else /* OPENSSL_IS_BORINGSSL */ PKCS7 *p7 = NULL; const unsigned char *p = pkcs7; +#endif /* OPENSSL_IS_BORINGSSL */ STACK_OF(X509) *certs; int i, num, ret = -1; BIO *out = NULL; +#ifdef OPENSSL_IS_BORINGSSL + certs = sk_X509_new_null(); + if (!certs) + goto fail; + CBS_init(&pkcs7_cbs, pkcs7, len); + if (!PKCS7_get_certificates(certs, &pkcs7_cbs)) { + wpa_printf(MSG_INFO, "Could not parse PKCS#7 object: %s", + ERR_error_string(ERR_get_error(), NULL)); + write_result(ctx, "Could not parse PKCS#7 object from EST"); + goto fail; + } +#else /* OPENSSL_IS_BORINGSSL */ p7 = d2i_PKCS7(NULL, &p, len); if (p7 == NULL) { wpa_printf(MSG_INFO, "Could not parse PKCS#7 object: %s", @@ -52,6 +71,7 @@ static int pkcs7_to_cert(struct hs20_osu_client *ctx, const u8 *pkcs7, certs = NULL; break; } +#endif /* OPENSSL_IS_BORINGSSL */ if (!certs || ((num = sk_X509_num(certs)) == 0)) { wpa_printf(MSG_INFO, "No certificates found in PKCS#7 object"); @@ -84,7 +104,12 @@ static int pkcs7_to_cert(struct hs20_osu_client *ctx, const u8 *pkcs7, ret = 0; fail: +#ifdef OPENSSL_IS_BORINGSSL + if (certs) + sk_X509_pop_free(certs, X509_free); +#else /* OPENSSL_IS_BORINGSSL */ PKCS7_free(p7); +#endif /* OPENSSL_IS_BORINGSSL */ if (out) BIO_free_all(out); @@ -310,6 +335,23 @@ static void add_csrattrs(struct hs20_osu_client *ctx, CsrAttrs *csrattrs, if (!csrattrs || ! csrattrs->attrs) return; +#ifdef OPENSSL_IS_BORINGSSL + num = sk_num(CHECKED_CAST(_STACK *, STACK_OF(AttrOrOID) *, + csrattrs->attrs)); + for (i = 0; i < num; i++) { + AttrOrOID *ao = sk_value( + CHECKED_CAST(_STACK *, const STACK_OF(AttrOrOID) *, + csrattrs->attrs), i); + switch (ao->type) { + case 0: + add_csrattrs_oid(ctx, ao->d.oid, exts); + break; + case 1: + add_csrattrs_attr(ctx, ao->d.attribute, exts); + break; + } + } +#else /* OPENSSL_IS_BORINGSSL */ num = SKM_sk_num(AttrOrOID, csrattrs->attrs); for (i = 0; i < num; i++) { AttrOrOID *ao = SKM_sk_value(AttrOrOID, csrattrs->attrs, i); @@ -322,6 +364,7 @@ static void add_csrattrs(struct hs20_osu_client *ctx, CsrAttrs *csrattrs, break; } } +#endif /* OPENSSL_IS_BORINGSSL */ } @@ -340,6 +383,7 @@ static int generate_csr(struct hs20_osu_client *ctx, char *key_pem, STACK_OF(X509_EXTENSION) *exts = NULL; X509_EXTENSION *ex; BIO *out; + CONF *ctmp = NULL; wpa_printf(MSG_INFO, "Generate RSA private key"); write_summary(ctx, "Generate RSA private key"); @@ -421,20 +465,20 @@ static int generate_csr(struct hs20_osu_client *ctx, char *key_pem, if (!exts) goto fail; - ex = X509V3_EXT_conf_nid(NULL, NULL, NID_basic_constraints, - "CA:FALSE"); + ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_basic_constraints, + "CA:FALSE"); if (ex == NULL || !sk_X509_EXTENSION_push(exts, ex)) goto fail; - ex = X509V3_EXT_conf_nid(NULL, NULL, NID_key_usage, - "nonRepudiation,digitalSignature,keyEncipherment"); + ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_key_usage, + "nonRepudiation,digitalSignature,keyEncipherment"); if (ex == NULL || !sk_X509_EXTENSION_push(exts, ex)) goto fail; - ex = X509V3_EXT_conf_nid(NULL, NULL, NID_ext_key_usage, - "1.3.6.1.4.1.40808.1.1.2"); + ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_ext_key_usage, + "1.3.6.1.4.1.40808.1.1.2"); if (ex == NULL || !sk_X509_EXTENSION_push(exts, ex)) goto fail; @@ -454,7 +498,9 @@ static int generate_csr(struct hs20_osu_client *ctx, char *key_pem, char *txt; size_t rlen; +#if !defined(ANDROID) || !defined(OPENSSL_IS_BORINGSSL) X509_REQ_print(out, req); +#endif rlen = BIO_ctrl_pending(out); txt = os_malloc(rlen + 1); if (txt) { @@ -473,7 +519,9 @@ static int generate_csr(struct hs20_osu_client *ctx, char *key_pem, FILE *f = fopen(csr_pem, "w"); if (f == NULL) goto fail; +#if !defined(ANDROID) || !defined(OPENSSL_IS_BORINGSSL) X509_REQ_print_fp(f, req); +#endif if (!PEM_write_X509_REQ(f, req)) { fclose(f); goto fail; diff --git a/contrib/wpa/hs20/client/osu_client.c b/contrib/wpa/hs20/client/osu_client.c index 0315f7b75ad4..c05c57d44f89 100644 --- a/contrib/wpa/hs20/client/osu_client.c +++ b/contrib/wpa/hs20/client/osu_client.c @@ -2229,7 +2229,7 @@ static int cmd_osu_select(struct hs20_osu_client *ctx, const char *dir, fprintf(f, "
BSSID: %s
\n" "SSID: %s
\n", last->bssid, last->osu_ssid); - if (last->osu_nai) + if (last->osu_nai[0]) fprintf(f, "NAI: %s
\n", last->osu_nai); fprintf(f, "URL: %s
\n" "methods:%s%s
\n" @@ -2339,12 +2339,23 @@ static int cmd_signup(struct hs20_osu_client *ctx, int no_prod_assoc, return -1; snprintf(fname, sizeof(fname), "%s/osu-info", dir); - if (mkdir(fname, S_IRWXU | S_IRWXG) < 0 && errno != EEXIST) { + if (mkdir(fname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0 && + errno != EEXIST) { wpa_printf(MSG_INFO, "mkdir(%s) failed: %s", fname, strerror(errno)); return -1; } +#ifdef ANDROID + /* Allow processes running with Group ID as AID_WIFI + * to read/write files from osu-info directory + */ + if (chown(fname, -1, AID_WIFI)) { + wpa_printf(MSG_INFO, "Could not chown osu-info directory: %s", + strerror(errno)); + } +#endif /* ANDROID */ + snprintf(buf, sizeof(buf), "SET osu_dir %s", fname); if (wpa_command(ifname, buf) < 0) { wpa_printf(MSG_INFO, "Failed to configure osu_dir to wpa_supplicant"); @@ -2559,7 +2570,7 @@ static int cmd_pol_upd(struct hs20_osu_client *ctx, const char *address, if (!pps_fname) { char buf[256]; wpa_printf(MSG_INFO, "Determining PPS file based on Home SP information"); - if (os_strncmp(address, "fqdn=", 5) == 0) { + if (address && os_strncmp(address, "fqdn=", 5) == 0) { wpa_printf(MSG_INFO, "Use requested FQDN from command line"); os_snprintf(buf, sizeof(buf), "%s", address + 5); address = NULL; @@ -3122,20 +3133,12 @@ int main(int argc, char *argv[]) usage(); exit(0); } - if (argc - optind < 2) - wpa_printf(MSG_ERROR, "Server URL missing from command line"); - else - ret = cmd_sub_rem(&ctx, argv[optind + 1], - argc > optind + 2 ? - argv[optind + 2] : NULL, - argc > optind + 3 ? - argv[optind + 3] : NULL); + ret = cmd_sub_rem(&ctx, argv[optind + 1], + argc > optind + 2 ? argv[optind + 2] : NULL, + argc > optind + 3 ? argv[optind + 3] : NULL); } else if (strcmp(argv[optind], "pol_upd") == 0) { - if (argc - optind < 2) { - usage(); - exit(0); - } - ret = cmd_pol_upd(&ctx, argc > 2 ? argv[optind + 1] : NULL, + ret = cmd_pol_upd(&ctx, + argc > optind + 1 ? argv[optind + 1] : NULL, argc > optind + 2 ? argv[optind + 2] : NULL, argc > optind + 3 ? argv[optind + 3] : NULL); } else if (strcmp(argv[optind], "prov") == 0) { diff --git a/contrib/wpa/patches/openssl-0.9.8za-tls-extensions.patch b/contrib/wpa/patches/openssl-0.9.8za-tls-extensions.patch deleted file mode 100644 index 82bfe2302ea3..000000000000 --- a/contrib/wpa/patches/openssl-0.9.8za-tls-extensions.patch +++ /dev/null @@ -1,397 +0,0 @@ -This patch adds support for TLS SessionTicket extension (RFC 5077) for -the parts used by EAP-FAST (RFC 4851). - -This is based on the patch from Alexey Kobozev -(sent to openssl-dev mailing list on Tue, 07 Jun 2005 15:40:58 +0300). - -OpenSSL 0.9.8za does not enable TLS extension support by default, so it -will need to be enabled by adding enable-tlsext to config script -command line. - - -diff -upr openssl-0.9.8za.orig/ssl/s3_clnt.c openssl-0.9.8za/ssl/s3_clnt.c ---- openssl-0.9.8za.orig/ssl/s3_clnt.c 2014-06-05 11:09:26.000000000 +0300 -+++ openssl-0.9.8za/ssl/s3_clnt.c 2014-06-05 20:37:09.221387312 +0300 -@@ -767,6 +767,22 @@ int ssl3_get_server_hello(SSL *s) - goto f_err; - } - -+#ifndef OPENSSL_NO_TLSEXT -+ /* check if we want to resume the session based on external pre-shared secret */ -+ if (s->version >= TLS1_VERSION && s->tls_session_secret_cb) -+ { -+ SSL_CIPHER *pref_cipher=NULL; -+ s->session->master_key_length=sizeof(s->session->master_key); -+ if (s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, -+ NULL, &pref_cipher, s->tls_session_secret_cb_arg)) -+ { -+ s->session->cipher=pref_cipher ? -+ pref_cipher : ssl_get_cipher_by_char(s,p+j); -+ s->s3->flags |= SSL3_FLAGS_CCS_OK; -+ } -+ } -+#endif /* OPENSSL_NO_TLSEXT */ -+ - if (j != 0 && j == s->session->session_id_length - && memcmp(p,s->session->session_id,j) == 0) - { -@@ -2745,11 +2760,8 @@ int ssl3_check_finished(SSL *s) - { - int ok; - long n; -- /* If we have no ticket or session ID is non-zero length (a match of -- * a non-zero session length would never reach here) it cannot be a -- * resumed session. -- */ -- if (!s->session->tlsext_tick || s->session->session_id_length) -+ /* If we have no ticket it cannot be a resumed session. */ -+ if (!s->session->tlsext_tick) - return 1; - /* this function is called when we really expect a Certificate - * message, so permit appropriate message length */ -diff -upr openssl-0.9.8za.orig/ssl/s3_srvr.c openssl-0.9.8za/ssl/s3_srvr.c ---- openssl-0.9.8za.orig/ssl/s3_srvr.c 2014-06-05 11:09:26.000000000 +0300 -+++ openssl-0.9.8za/ssl/s3_srvr.c 2014-06-05 20:37:09.225387312 +0300 -@@ -1011,6 +1011,59 @@ int ssl3_get_client_hello(SSL *s) - SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_CLIENTHELLO_TLSEXT); - goto err; - } -+ -+ /* Check if we want to use external pre-shared secret for this -+ * handshake for not reused session only. We need to generate -+ * server_random before calling tls_session_secret_cb in order to allow -+ * SessionTicket processing to use it in key derivation. */ -+ { -+ unsigned long Time; -+ unsigned char *pos; -+ Time=(unsigned long)time(NULL); /* Time */ -+ pos=s->s3->server_random; -+ l2n(Time,pos); -+ if (RAND_pseudo_bytes(pos,SSL3_RANDOM_SIZE-4) <= 0) -+ { -+ al=SSL_AD_INTERNAL_ERROR; -+ goto f_err; -+ } -+ } -+ -+ if (!s->hit && s->version >= TLS1_VERSION && s->tls_session_secret_cb) -+ { -+ SSL_CIPHER *pref_cipher=NULL; -+ -+ s->session->master_key_length=sizeof(s->session->master_key); -+ if(s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, -+ ciphers, &pref_cipher, s->tls_session_secret_cb_arg)) -+ { -+ s->hit=1; -+ s->session->ciphers=ciphers; -+ s->session->verify_result=X509_V_OK; -+ -+ ciphers=NULL; -+ -+ /* check if some cipher was preferred by call back */ -+ pref_cipher=pref_cipher ? pref_cipher : ssl3_choose_cipher(s, s->session->ciphers, SSL_get_ciphers(s)); -+ if (pref_cipher == NULL) -+ { -+ al=SSL_AD_HANDSHAKE_FAILURE; -+ SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_NO_SHARED_CIPHER); -+ goto f_err; -+ } -+ -+ s->session->cipher=pref_cipher; -+ -+ if (s->cipher_list) -+ sk_SSL_CIPHER_free(s->cipher_list); -+ -+ if (s->cipher_list_by_id) -+ sk_SSL_CIPHER_free(s->cipher_list_by_id); -+ -+ s->cipher_list = sk_SSL_CIPHER_dup(s->session->ciphers); -+ s->cipher_list_by_id = sk_SSL_CIPHER_dup(s->session->ciphers); -+ } -+ } - #endif - /* Worst case, we will use the NULL compression, but if we have other - * options, we will now look for them. We have i-1 compression -@@ -1161,16 +1214,22 @@ int ssl3_send_server_hello(SSL *s) - unsigned char *buf; - unsigned char *p,*d; - int i,sl; -- unsigned long l,Time; -+ unsigned long l; -+#ifdef OPENSSL_NO_TLSEXT -+ unsigned long Time; -+#endif - - if (s->state == SSL3_ST_SW_SRVR_HELLO_A) - { - buf=(unsigned char *)s->init_buf->data; -+#ifdef OPENSSL_NO_TLSEXT - p=s->s3->server_random; -+ /* Generate server_random if it was not needed previously */ - Time=(unsigned long)time(NULL); /* Time */ - l2n(Time,p); - if (RAND_pseudo_bytes(p,SSL3_RANDOM_SIZE-4) <= 0) - return -1; -+#endif - /* Do the message type and length last */ - d=p= &(buf[4]); - -diff -upr openssl-0.9.8za.orig/ssl/ssl_err.c openssl-0.9.8za/ssl/ssl_err.c ---- openssl-0.9.8za.orig/ssl/ssl_err.c 2014-06-05 11:09:08.000000000 +0300 -+++ openssl-0.9.8za/ssl/ssl_err.c 2014-06-05 20:37:09.225387312 +0300 -@@ -265,6 +265,7 @@ static ERR_STRING_DATA SSL_str_functs[]= - {ERR_FUNC(SSL_F_TLS1_ENC), "TLS1_ENC"}, - {ERR_FUNC(SSL_F_TLS1_SETUP_KEY_BLOCK), "TLS1_SETUP_KEY_BLOCK"}, - {ERR_FUNC(SSL_F_WRITE_PENDING), "WRITE_PENDING"}, -+{ERR_FUNC(SSL_F_SSL_SET_SESSION_TICKET_EXT), "SSL_set_session_ticket_ext"}, - {0,NULL} - }; - -diff -upr openssl-0.9.8za.orig/ssl/ssl.h openssl-0.9.8za/ssl/ssl.h ---- openssl-0.9.8za.orig/ssl/ssl.h 2014-06-05 11:09:08.000000000 +0300 -+++ openssl-0.9.8za/ssl/ssl.h 2014-06-05 20:37:09.229387312 +0300 -@@ -344,6 +344,7 @@ extern "C" { - * 'struct ssl_st *' function parameters used to prototype callbacks - * in SSL_CTX. */ - typedef struct ssl_st *ssl_crock_st; -+typedef struct tls_session_ticket_ext_st TLS_SESSION_TICKET_EXT; - - /* used to hold info on the particular ciphers used */ - typedef struct ssl_cipher_st -@@ -362,6 +363,9 @@ typedef struct ssl_cipher_st - - DECLARE_STACK_OF(SSL_CIPHER) - -+typedef int (*tls_session_ticket_ext_cb_fn)(SSL *s, const unsigned char *data, int len, void *arg); -+typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg); -+ - /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */ - typedef struct ssl_method_st - { -@@ -1053,6 +1057,18 @@ struct ssl_st - - /* RFC4507 session ticket expected to be received or sent */ - int tlsext_ticket_expected; -+ -+ /* TLS Session Ticket extension override */ -+ TLS_SESSION_TICKET_EXT *tlsext_session_ticket; -+ -+ /* TLS Session Ticket extension callback */ -+ tls_session_ticket_ext_cb_fn tls_session_ticket_ext_cb; -+ void *tls_session_ticket_ext_cb_arg; -+ -+ /* TLS pre-shared secret session resumption */ -+ tls_session_secret_cb_fn tls_session_secret_cb; -+ void *tls_session_secret_cb_arg; -+ - SSL_CTX * initial_ctx; /* initial ctx, used to store sessions */ - #define session_ctx initial_ctx - #else -@@ -1668,6 +1684,15 @@ void *SSL_COMP_get_compression_methods(v - int SSL_COMP_add_compression_method(int id,void *cm); - #endif - -+/* TLS extensions functions */ -+int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len); -+ -+int SSL_set_session_ticket_ext_cb(SSL *s, tls_session_ticket_ext_cb_fn cb, -+ void *arg); -+ -+/* Pre-shared secret session resumption functions */ -+int SSL_set_session_secret_cb(SSL *s, tls_session_secret_cb_fn tls_session_secret_cb, void *arg); -+ - /* BEGIN ERROR CODES */ - /* The following lines are auto generated by the script mkerr.pl. Any changes - * made after this point may be overwritten when the script is next run. -@@ -1872,6 +1897,7 @@ void ERR_load_SSL_strings(void); - #define SSL_F_TLS1_ENC 210 - #define SSL_F_TLS1_SETUP_KEY_BLOCK 211 - #define SSL_F_WRITE_PENDING 212 -+#define SSL_F_SSL_SET_SESSION_TICKET_EXT 213 - - /* Reason codes. */ - #define SSL_R_APP_DATA_IN_HANDSHAKE 100 -diff -upr openssl-0.9.8za.orig/ssl/ssl_sess.c openssl-0.9.8za/ssl/ssl_sess.c ---- openssl-0.9.8za.orig/ssl/ssl_sess.c 2014-06-05 11:09:08.000000000 +0300 -+++ openssl-0.9.8za/ssl/ssl_sess.c 2014-06-05 20:37:09.229387312 +0300 -@@ -712,6 +712,61 @@ long SSL_CTX_get_timeout(const SSL_CTX * - return(s->session_timeout); - } - -+#ifndef OPENSSL_NO_TLSEXT -+int SSL_set_session_secret_cb(SSL *s, int (*tls_session_secret_cb)(SSL *s, void *secret, int *secret_len, -+ STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg), void *arg) -+ { -+ if (s == NULL) return(0); -+ s->tls_session_secret_cb = tls_session_secret_cb; -+ s->tls_session_secret_cb_arg = arg; -+ return(1); -+ } -+ -+int SSL_set_session_ticket_ext_cb(SSL *s, tls_session_ticket_ext_cb_fn cb, -+ void *arg) -+ { -+ if (s == NULL) return(0); -+ s->tls_session_ticket_ext_cb = cb; -+ s->tls_session_ticket_ext_cb_arg = arg; -+ return(1); -+ } -+ -+int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len) -+ { -+ if (s->version >= TLS1_VERSION) -+ { -+ if (s->tlsext_session_ticket) -+ { -+ OPENSSL_free(s->tlsext_session_ticket); -+ s->tlsext_session_ticket = NULL; -+ } -+ -+ s->tlsext_session_ticket = OPENSSL_malloc(sizeof(TLS_SESSION_TICKET_EXT) + ext_len); -+ if (!s->tlsext_session_ticket) -+ { -+ SSLerr(SSL_F_SSL_SET_SESSION_TICKET_EXT, ERR_R_MALLOC_FAILURE); -+ return 0; -+ } -+ -+ if (ext_data) -+ { -+ s->tlsext_session_ticket->length = ext_len; -+ s->tlsext_session_ticket->data = s->tlsext_session_ticket + 1; -+ memcpy(s->tlsext_session_ticket->data, ext_data, ext_len); -+ } -+ else -+ { -+ s->tlsext_session_ticket->length = 0; -+ s->tlsext_session_ticket->data = NULL; -+ } -+ -+ return 1; -+ } -+ -+ return 0; -+ } -+#endif /* OPENSSL_NO_TLSEXT */ -+ - typedef struct timeout_param_st - { - SSL_CTX *ctx; -diff -upr openssl-0.9.8za.orig/ssl/t1_lib.c openssl-0.9.8za/ssl/t1_lib.c ---- openssl-0.9.8za.orig/ssl/t1_lib.c 2014-06-05 11:09:08.000000000 +0300 -+++ openssl-0.9.8za/ssl/t1_lib.c 2014-06-05 20:37:09.229387312 +0300 -@@ -106,6 +106,12 @@ int tls1_new(SSL *s) - - void tls1_free(SSL *s) - { -+#ifndef OPENSSL_NO_TLSEXT -+ if (s->tlsext_session_ticket) -+ { -+ OPENSSL_free(s->tlsext_session_ticket); -+ } -+#endif - ssl3_free(s); - } - -@@ -206,8 +212,23 @@ unsigned char *ssl_add_clienthello_tlsex - int ticklen; - if (!s->new_session && s->session && s->session->tlsext_tick) - ticklen = s->session->tlsext_ticklen; -+ else if (s->session && s->tlsext_session_ticket && -+ s->tlsext_session_ticket->data) -+ { -+ ticklen = s->tlsext_session_ticket->length; -+ s->session->tlsext_tick = OPENSSL_malloc(ticklen); -+ if (!s->session->tlsext_tick) -+ return NULL; -+ memcpy(s->session->tlsext_tick, -+ s->tlsext_session_ticket->data, -+ ticklen); -+ s->session->tlsext_ticklen = ticklen; -+ } - else - ticklen = 0; -+ if (ticklen == 0 && s->tlsext_session_ticket && -+ s->tlsext_session_ticket->data == NULL) -+ goto skip_ext; - /* Check for enough room 2 for extension type, 2 for len - * rest for ticket - */ -@@ -221,6 +242,7 @@ unsigned char *ssl_add_clienthello_tlsex - ret += ticklen; - } - } -+ skip_ext: - - if (s->tlsext_status_type == TLSEXT_STATUSTYPE_ocsp && - s->version != DTLS1_VERSION) -@@ -574,6 +596,15 @@ int ssl_parse_clienthello_tlsext(SSL *s, - return 0; - renegotiate_seen = 1; - } -+ else if (type == TLSEXT_TYPE_session_ticket) -+ { -+ if (s->tls_session_ticket_ext_cb && -+ !s->tls_session_ticket_ext_cb(s, data, size, s->tls_session_ticket_ext_cb_arg)) -+ { -+ *al = TLS1_AD_INTERNAL_ERROR; -+ return 0; -+ } -+ } - else if (type == TLSEXT_TYPE_status_request && - s->version != DTLS1_VERSION && s->ctx->tlsext_status_cb) - { -@@ -751,6 +782,12 @@ int ssl_parse_serverhello_tlsext(SSL *s, - } - else if (type == TLSEXT_TYPE_session_ticket) - { -+ if (s->tls_session_ticket_ext_cb && -+ !s->tls_session_ticket_ext_cb(s, data, size, s->tls_session_ticket_ext_cb_arg)) -+ { -+ *al = TLS1_AD_INTERNAL_ERROR; -+ return 0; -+ } - if ((SSL_get_options(s) & SSL_OP_NO_TICKET) - || (size > 0)) - { -@@ -1043,6 +1080,15 @@ int tls1_process_ticket(SSL *s, unsigned - s->tlsext_ticket_expected = 1; - return 0; /* Cache miss */ - } -+ if (s->tls_session_secret_cb) -+ { -+ /* Indicate cache miss here and instead of -+ * generating the session from ticket now, -+ * trigger abbreviated handshake based on -+ * external mechanism to calculate the master -+ * secret later. */ -+ return 0; -+ } - return tls_decrypt_ticket(s, p, size, session_id, len, - ret); - } -diff -upr openssl-0.9.8za.orig/ssl/tls1.h openssl-0.9.8za/ssl/tls1.h ---- openssl-0.9.8za.orig/ssl/tls1.h 2014-06-05 11:09:08.000000000 +0300 -+++ openssl-0.9.8za/ssl/tls1.h 2014-06-05 20:37:09.229387312 +0300 -@@ -415,6 +415,13 @@ SSL_CTX_callback_ctrl(ssl,SSL_CTRL_SET_T - #define TLS_MD_MASTER_SECRET_CONST "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74" /*master secret*/ - #endif - -+/* TLS extension struct */ -+struct tls_session_ticket_ext_st -+ { -+ unsigned short length; -+ void *data; -+ }; -+ - #ifdef __cplusplus - } - #endif -diff -upr openssl-0.9.8za.orig/util/ssleay.num openssl-0.9.8za/util/ssleay.num ---- openssl-0.9.8za.orig/util/ssleay.num 2014-06-05 12:38:45.000000000 +0300 -+++ openssl-0.9.8za/util/ssleay.num 2014-06-05 20:37:09.229387312 +0300 -@@ -242,3 +242,5 @@ SSL_set_SSL_CTX - SSL_get_servername 291 EXIST::FUNCTION:TLSEXT - SSL_get_servername_type 292 EXIST::FUNCTION:TLSEXT - SSL_CTX_set_client_cert_engine 293 EXIST::FUNCTION:ENGINE -+SSL_set_session_ticket_ext 306 EXIST::FUNCTION:TLSEXT -+SSL_set_session_secret_cb 307 EXIST::FUNCTION:TLSEXT diff --git a/contrib/wpa/patches/openssl-0.9.8zf-tls-extensions.patch b/contrib/wpa/patches/openssl-0.9.8zf-tls-extensions.patch deleted file mode 100644 index 3a8f90e40ce2..000000000000 --- a/contrib/wpa/patches/openssl-0.9.8zf-tls-extensions.patch +++ /dev/null @@ -1,398 +0,0 @@ -This patch adds support for TLS SessionTicket extension (RFC 5077) for -the parts used by EAP-FAST (RFC 4851). - -This is based on the patch from Alexey Kobozev -(sent to openssl-dev mailing list on Tue, 07 Jun 2005 15:40:58 +0300). - -OpenSSL 0.9.8zf does not enable TLS extension support by default, so it -will need to be enabled by adding enable-tlsext to config script -command line. - - -diff -upr openssl-0.9.8zf.orig/ssl/s3_clnt.c openssl-0.9.8zf/ssl/s3_clnt.c ---- openssl-0.9.8zf.orig/ssl/s3_clnt.c 2015-03-19 15:46:46.000000000 +0200 -+++ openssl-0.9.8zf/ssl/s3_clnt.c 2015-03-24 16:19:14.043911769 +0200 -@@ -760,6 +760,23 @@ int ssl3_get_server_hello(SSL *s) - goto f_err; - } - -+#ifndef OPENSSL_NO_TLSEXT -+ /* check if we want to resume the session based on external pre-shared secret */ -+ if (s->version >= TLS1_VERSION && s->tls_session_secret_cb) { -+ SSL_CIPHER *pref_cipher = NULL; -+ -+ s->session->master_key_length = sizeof(s->session->master_key); -+ if (s->tls_session_secret_cb(s, s->session->master_key, -+ &s->session->master_key_length, -+ NULL, &pref_cipher, -+ s->tls_session_secret_cb_arg)) { -+ s->session->cipher = pref_cipher ? -+ pref_cipher : ssl_get_cipher_by_char(s, p + j); -+ s->s3->flags |= SSL3_FLAGS_CCS_OK; -+ } -+ } -+#endif /* OPENSSL_NO_TLSEXT */ -+ - if (j != 0 && j == s->session->session_id_length - && memcmp(p, s->session->session_id, j) == 0) { - if (s->sid_ctx_length != s->session->sid_ctx_length -@@ -2684,12 +2701,8 @@ int ssl3_check_finished(SSL *s) - { - int ok; - long n; -- /* -- * If we have no ticket or session ID is non-zero length (a match of a -- * non-zero session length would never reach here) it cannot be a resumed -- * session. -- */ -- if (!s->session->tlsext_tick || s->session->session_id_length) -+ /* If we have no ticket it cannot be a resumed session. */ -+ if (!s->session->tlsext_tick) - return 1; - /* - * this function is called when we really expect a Certificate message, -diff -upr openssl-0.9.8zf.orig/ssl/s3_srvr.c openssl-0.9.8zf/ssl/s3_srvr.c ---- openssl-0.9.8zf.orig/ssl/s3_srvr.c 2015-03-19 15:46:46.000000000 +0200 -+++ openssl-0.9.8zf/ssl/s3_srvr.c 2015-03-24 16:23:34.567909681 +0200 -@@ -999,6 +999,59 @@ int ssl3_get_client_hello(SSL *s) - SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_CLIENTHELLO_TLSEXT); - goto err; - } -+ -+ /* Check if we want to use external pre-shared secret for this -+ * handshake for not reused session only. We need to generate -+ * server_random before calling tls_session_secret_cb in order to allow -+ * SessionTicket processing to use it in key derivation. */ -+ { -+ unsigned long Time; -+ unsigned char *pos; -+ Time = (unsigned long)time(NULL); /* Time */ -+ pos = s->s3->server_random; -+ l2n(Time, pos); -+ if (RAND_pseudo_bytes(pos, SSL3_RANDOM_SIZE - 4) <= 0) { -+ al = SSL_AD_INTERNAL_ERROR; -+ goto f_err; -+ } -+ } -+ -+ if (!s->hit && s->version >= TLS1_VERSION && s->tls_session_secret_cb) { -+ SSL_CIPHER *pref_cipher = NULL; -+ -+ s->session->master_key_length = sizeof(s->session->master_key); -+ if (s->tls_session_secret_cb(s, s->session->master_key, -+ &s->session->master_key_length, -+ ciphers, &pref_cipher, -+ s->tls_session_secret_cb_arg)) { -+ s->hit = 1; -+ s->session->ciphers = ciphers; -+ s->session->verify_result = X509_V_OK; -+ -+ ciphers = NULL; -+ -+ /* check if some cipher was preferred by call back */ -+ pref_cipher = pref_cipher ? pref_cipher : -+ ssl3_choose_cipher(s, s->session->ciphers, -+ SSL_get_ciphers(s)); -+ if (pref_cipher == NULL) { -+ al = SSL_AD_HANDSHAKE_FAILURE; -+ SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_NO_SHARED_CIPHER); -+ goto f_err; -+ } -+ -+ s->session->cipher = pref_cipher; -+ -+ if (s->cipher_list) -+ sk_SSL_CIPHER_free(s->cipher_list); -+ -+ if (s->cipher_list_by_id) -+ sk_SSL_CIPHER_free(s->cipher_list_by_id); -+ -+ s->cipher_list = sk_SSL_CIPHER_dup(s->session->ciphers); -+ s->cipher_list_by_id = sk_SSL_CIPHER_dup(s->session->ciphers); -+ } -+ } - #endif - /* - * Worst case, we will use the NULL compression, but if we have other -@@ -1143,15 +1196,21 @@ int ssl3_send_server_hello(SSL *s) - unsigned char *buf; - unsigned char *p, *d; - int i, sl; -- unsigned long l, Time; -+ unsigned long l; -+#ifdef OPENSSL_NO_TLSEXT -+ unsigned long Time; -+#endif - - if (s->state == SSL3_ST_SW_SRVR_HELLO_A) { - buf = (unsigned char *)s->init_buf->data; -+#ifdef OPENSSL_NO_TLSEXT - p = s->s3->server_random; -+ /* Generate server_random if it was not needed previously */ - Time = (unsigned long)time(NULL); /* Time */ - l2n(Time, p); - if (RAND_pseudo_bytes(p, SSL3_RANDOM_SIZE - 4) <= 0) - return -1; -+#endif - /* Do the message type and length last */ - d = p = &(buf[4]); - -diff -upr openssl-0.9.8zf.orig/ssl/ssl_err.c openssl-0.9.8zf/ssl/ssl_err.c ---- openssl-0.9.8zf.orig/ssl/ssl_err.c 2015-03-19 15:46:46.000000000 +0200 -+++ openssl-0.9.8zf/ssl/ssl_err.c 2015-03-24 16:35:58.627903717 +0200 -@@ -316,6 +316,7 @@ static ERR_STRING_DATA SSL_str_functs[] - {ERR_FUNC(SSL_F_TLS1_ENC), "TLS1_ENC"}, - {ERR_FUNC(SSL_F_TLS1_SETUP_KEY_BLOCK), "TLS1_SETUP_KEY_BLOCK"}, - {ERR_FUNC(SSL_F_WRITE_PENDING), "WRITE_PENDING"}, -+ {ERR_FUNC(SSL_F_SSL_SET_SESSION_TICKET_EXT), "SSL_set_session_ticket_ext"}, - {0, NULL} - }; - -diff -upr openssl-0.9.8zf.orig/ssl/ssl.h openssl-0.9.8zf/ssl/ssl.h ---- openssl-0.9.8zf.orig/ssl/ssl.h 2015-03-19 15:46:46.000000000 +0200 -+++ openssl-0.9.8zf/ssl/ssl.h 2015-03-24 16:25:44.339908641 +0200 -@@ -349,6 +349,7 @@ extern "C" { - * function parameters used to prototype callbacks in SSL_CTX. - */ - typedef struct ssl_st *ssl_crock_st; -+typedef struct tls_session_ticket_ext_st TLS_SESSION_TICKET_EXT; - - /* used to hold info on the particular ciphers used */ - typedef struct ssl_cipher_st { -@@ -366,6 +367,12 @@ typedef struct ssl_cipher_st { - - DECLARE_STACK_OF(SSL_CIPHER) - -+typedef int (*tls_session_ticket_ext_cb_fn)(SSL *s, const unsigned char *data, -+ int len, void *arg); -+typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, -+ STACK_OF(SSL_CIPHER) *peer_ciphers, -+ SSL_CIPHER **cipher, void *arg); -+ - /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */ - typedef struct ssl_method_st { - int version; -@@ -1116,6 +1123,18 @@ struct ssl_st { - int tlsext_ocsp_resplen; - /* RFC4507 session ticket expected to be received or sent */ - int tlsext_ticket_expected; -+ -+ /* TLS Session Ticket extension override */ -+ TLS_SESSION_TICKET_EXT *tlsext_session_ticket; -+ -+ /* TLS Session Ticket extension callback */ -+ tls_session_ticket_ext_cb_fn tls_session_ticket_ext_cb; -+ void *tls_session_ticket_ext_cb_arg; -+ -+ /* TLS pre-shared secret session resumption */ -+ tls_session_secret_cb_fn tls_session_secret_cb; -+ void *tls_session_secret_cb_arg; -+ - SSL_CTX *initial_ctx; /* initial ctx, used to store sessions */ - # define session_ctx initial_ctx - # else -@@ -1772,6 +1791,17 @@ void *SSL_COMP_get_compression_methods(v - int SSL_COMP_add_compression_method(int id, void *cm); - # endif - -+/* TLS extensions functions */ -+int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len); -+ -+int SSL_set_session_ticket_ext_cb(SSL *s, tls_session_ticket_ext_cb_fn cb, -+ void *arg); -+ -+/* Pre-shared secret session resumption functions */ -+int SSL_set_session_secret_cb(SSL *s, -+ tls_session_secret_cb_fn tls_session_secret_cb, -+ void *arg); -+ - /* BEGIN ERROR CODES */ - /* - * The following lines are auto generated by the script mkerr.pl. Any changes -@@ -1977,6 +2007,7 @@ void ERR_load_SSL_strings(void); - # define SSL_F_TLS1_ENC 210 - # define SSL_F_TLS1_SETUP_KEY_BLOCK 211 - # define SSL_F_WRITE_PENDING 212 -+#define SSL_F_SSL_SET_SESSION_TICKET_EXT 213 - - /* Reason codes. */ - # define SSL_R_APP_DATA_IN_HANDSHAKE 100 -diff -upr openssl-0.9.8zf.orig/ssl/ssl_sess.c openssl-0.9.8zf/ssl/ssl_sess.c ---- openssl-0.9.8zf.orig/ssl/ssl_sess.c 2015-03-19 15:46:46.000000000 +0200 -+++ openssl-0.9.8zf/ssl/ssl_sess.c 2015-03-24 16:28:04.819907515 +0200 -@@ -716,6 +716,61 @@ long SSL_CTX_get_timeout(const SSL_CTX * - return (s->session_timeout); - } - -+#ifndef OPENSSL_NO_TLSEXT -+int SSL_set_session_secret_cb( -+ SSL *s, -+ int (*tls_session_secret_cb)(SSL *s, void *secret, int *secret_len, -+ STACK_OF(SSL_CIPHER) *peer_ciphers, -+ SSL_CIPHER **cipher, void *arg), void *arg) -+{ -+ if (s == NULL) -+ return 0; -+ s->tls_session_secret_cb = tls_session_secret_cb; -+ s->tls_session_secret_cb_arg = arg; -+ return 1; -+} -+ -+int SSL_set_session_ticket_ext_cb(SSL *s, tls_session_ticket_ext_cb_fn cb, -+ void *arg) -+{ -+ if (s == NULL) -+ return 0; -+ s->tls_session_ticket_ext_cb = cb; -+ s->tls_session_ticket_ext_cb_arg = arg; -+ return 1; -+} -+ -+int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len) -+{ -+ if (s->version >= TLS1_VERSION) { -+ if (s->tlsext_session_ticket) { -+ OPENSSL_free(s->tlsext_session_ticket); -+ s->tlsext_session_ticket = NULL; -+ } -+ -+ s->tlsext_session_ticket = OPENSSL_malloc( -+ sizeof(TLS_SESSION_TICKET_EXT) + ext_len); -+ if (!s->tlsext_session_ticket) { -+ SSLerr(SSL_F_SSL_SET_SESSION_TICKET_EXT, ERR_R_MALLOC_FAILURE); -+ return 0; -+ } -+ -+ if (ext_data) { -+ s->tlsext_session_ticket->length = ext_len; -+ s->tlsext_session_ticket->data = s->tlsext_session_ticket + 1; -+ memcpy(s->tlsext_session_ticket->data, ext_data, ext_len); -+ } else { -+ s->tlsext_session_ticket->length = 0; -+ s->tlsext_session_ticket->data = NULL; -+ } -+ -+ return 1; -+ } -+ -+ return 0; -+} -+#endif /* OPENSSL_NO_TLSEXT */ -+ - typedef struct timeout_param_st { - SSL_CTX *ctx; - long time; -diff -upr openssl-0.9.8zf.orig/ssl/t1_lib.c openssl-0.9.8zf/ssl/t1_lib.c ---- openssl-0.9.8zf.orig/ssl/t1_lib.c 2015-03-19 15:46:46.000000000 +0200 -+++ openssl-0.9.8zf/ssl/t1_lib.c 2015-03-24 16:32:46.923905254 +0200 -@@ -108,6 +108,11 @@ int tls1_new(SSL *s) - - void tls1_free(SSL *s) - { -+#ifndef OPENSSL_NO_TLSEXT -+ if (s->tlsext_session_ticket) { -+ OPENSSL_free(s->tlsext_session_ticket); -+ } -+#endif - ssl3_free(s); - } - -@@ -206,8 +211,20 @@ unsigned char *ssl_add_clienthello_tlsex - int ticklen; - if (!s->new_session && s->session && s->session->tlsext_tick) - ticklen = s->session->tlsext_ticklen; -- else -+ else if (s->session && s->tlsext_session_ticket && -+ s->tlsext_session_ticket->data) { -+ ticklen = s->tlsext_session_ticket->length; -+ s->session->tlsext_tick = OPENSSL_malloc(ticklen); -+ if (!s->session->tlsext_tick) -+ return NULL; -+ memcpy(s->session->tlsext_tick, s->tlsext_session_ticket->data, -+ ticklen); -+ s->session->tlsext_ticklen = ticklen; -+ } else - ticklen = 0; -+ if (ticklen == 0 && s->tlsext_session_ticket && -+ s->tlsext_session_ticket->data == NULL) -+ goto skip_ext; - /* - * Check for enough room 2 for extension type, 2 for len rest for - * ticket -@@ -221,6 +238,7 @@ unsigned char *ssl_add_clienthello_tlsex - ret += ticklen; - } - } -+skip_ext: - - if (s->tlsext_status_type == TLSEXT_STATUSTYPE_ocsp && - s->version != DTLS1_VERSION) { -@@ -560,6 +578,14 @@ int ssl_parse_clienthello_tlsext(SSL *s, - if (!ssl_parse_clienthello_renegotiate_ext(s, data, size, al)) - return 0; - renegotiate_seen = 1; -+ } else if (type == TLSEXT_TYPE_session_ticket) { -+ if (s->tls_session_ticket_ext_cb && -+ !s->tls_session_ticket_ext_cb(s, data, size, -+ s->tls_session_ticket_ext_cb_arg)) -+ { -+ *al = TLS1_AD_INTERNAL_ERROR; -+ return 0; -+ } - } else if (type == TLSEXT_TYPE_status_request && - s->version != DTLS1_VERSION && s->ctx->tlsext_status_cb) { - -@@ -710,6 +736,13 @@ int ssl_parse_serverhello_tlsext(SSL *s, - } - tlsext_servername = 1; - } else if (type == TLSEXT_TYPE_session_ticket) { -+ if (s->tls_session_ticket_ext_cb && -+ !s->tls_session_ticket_ext_cb( -+ s, data, size, -+ s->tls_session_ticket_ext_cb_arg)) { -+ *al = TLS1_AD_INTERNAL_ERROR; -+ return 0; -+ } - if ((SSL_get_options(s) & SSL_OP_NO_TICKET) - || (size > 0)) { - *al = TLS1_AD_UNSUPPORTED_EXTENSION; -@@ -993,6 +1026,14 @@ int tls1_process_ticket(SSL *s, unsigned - s->tlsext_ticket_expected = 1; - return 0; /* Cache miss */ - } -+ if (s->tls_session_secret_cb) { -+ /* Indicate cache miss here and instead of -+ * generating the session from ticket now, -+ * trigger abbreviated handshake based on -+ * external mechanism to calculate the master -+ * secret later. */ -+ return 0; -+ } - return tls_decrypt_ticket(s, p, size, session_id, len, ret); - } - p += size; -diff -upr openssl-0.9.8zf.orig/ssl/tls1.h openssl-0.9.8zf/ssl/tls1.h ---- openssl-0.9.8zf.orig/ssl/tls1.h 2015-03-19 15:46:46.000000000 +0200 -+++ openssl-0.9.8zf/ssl/tls1.h 2015-03-24 16:33:31.855904894 +0200 -@@ -460,6 +460,12 @@ SSL_CTX_callback_ctrl(ssl,SSL_CTRL_SET_T - # define TLS_MD_MASTER_SECRET_CONST "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74" - # endif - -+/* TLS extension struct */ -+struct tls_session_ticket_ext_st { -+ unsigned short length; -+ void *data; -+}; -+ - #ifdef __cplusplus - } - #endif -diff -upr openssl-0.9.8zf.orig/util/ssleay.num openssl-0.9.8zf/util/ssleay.num ---- openssl-0.9.8zf.orig/util/ssleay.num 2015-03-19 15:47:15.000000000 +0200 -+++ openssl-0.9.8zf/util/ssleay.num 2015-03-24 16:33:51.127904739 +0200 -@@ -242,3 +242,5 @@ SSL_set_SSL_CTX - SSL_get_servername 291 EXIST::FUNCTION:TLSEXT - SSL_get_servername_type 292 EXIST::FUNCTION:TLSEXT - SSL_CTX_set_client_cert_engine 293 EXIST::FUNCTION:ENGINE -+SSL_set_session_ticket_ext 306 EXIST::FUNCTION:TLSEXT -+SSL_set_session_secret_cb 307 EXIST::FUNCTION:TLSEXT diff --git a/contrib/wpa/src/ap/accounting.c b/contrib/wpa/src/ap/accounting.c index a096de4d3e51..0aacc3c95b08 100644 --- a/contrib/wpa/src/ap/accounting.c +++ b/contrib/wpa/src/ap/accounting.c @@ -1,6 +1,6 @@ /* * hostapd / RADIUS Accounting - * Copyright (c) 2002-2009, 2012, Jouni Malinen + * Copyright (c) 2002-2009, 2012-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -41,6 +41,7 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, size_t len; int i; struct wpabuf *b; + struct os_time now; msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST, radius_client_get_id(hapd->radius)); @@ -49,44 +50,24 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, return NULL; } - if (sta) { - radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta)); - - if ((hapd->conf->wpa & 2) && - !hapd->conf->disable_pmksa_caching && - sta->eapol_sm && sta->eapol_sm->acct_multi_session_id_hi) { - os_snprintf(buf, sizeof(buf), "%08X+%08X", - sta->eapol_sm->acct_multi_session_id_hi, - sta->eapol_sm->acct_multi_session_id_lo); - if (!radius_msg_add_attr( - msg, RADIUS_ATTR_ACCT_MULTI_SESSION_ID, - (u8 *) buf, os_strlen(buf))) { - wpa_printf(MSG_INFO, - "Could not add Acct-Multi-Session-Id"); - goto fail; - } - } - } else { - radius_msg_make_authenticator(msg, (u8 *) hapd, sizeof(*hapd)); - } - if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE, status_type)) { wpa_printf(MSG_INFO, "Could not add Acct-Status-Type"); goto fail; } - if (!hostapd_config_get_radius_attr(hapd->conf->radius_acct_req_attr, - RADIUS_ATTR_ACCT_AUTHENTIC) && - !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC, - hapd->conf->ieee802_1x ? - RADIUS_ACCT_AUTHENTIC_RADIUS : - RADIUS_ACCT_AUTHENTIC_LOCAL)) { - wpa_printf(MSG_INFO, "Could not add Acct-Authentic"); - goto fail; - } - if (sta) { + if (!hostapd_config_get_radius_attr( + hapd->conf->radius_acct_req_attr, + RADIUS_ATTR_ACCT_AUTHENTIC) && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC, + hapd->conf->ieee802_1x ? + RADIUS_ACCT_AUTHENTIC_RADIUS : + RADIUS_ACCT_AUTHENTIC_LOCAL)) { + wpa_printf(MSG_INFO, "Could not add Acct-Authentic"); + goto fail; + } + /* Use 802.1X identity if available */ val = ieee802_1x_get_identity(sta->eapol_sm, &len); @@ -147,6 +128,32 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, wpa_printf(MSG_ERROR, "Could not add CUI from ACL"); goto fail; } + + if (sta->ipaddr && + !radius_msg_add_attr_int32(msg, + RADIUS_ATTR_FRAMED_IP_ADDRESS, + be_to_host32(sta->ipaddr))) { + wpa_printf(MSG_ERROR, + "Could not add Framed-IP-Address"); + goto fail; + } + } + + os_get_time(&now); + if (now.sec > 1000000000 && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP, + now.sec)) { + wpa_printf(MSG_INFO, "Could not add Event-Timestamp"); + goto fail; + } + + /* + * Add Acct-Delay-Time with zero value for the first transmission. This + * will be updated within radius_client.c when retransmitting the frame. + */ + if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_DELAY_TIME, 0)) { + wpa_printf(MSG_INFO, "Could not add Acct-Delay-Time"); + goto fail; } return msg; @@ -164,19 +171,25 @@ static int accounting_sta_update_stats(struct hostapd_data *hapd, if (hostapd_drv_read_sta_data(hapd, data, sta->addr)) return -1; - if (sta->last_rx_bytes > data->rx_bytes) - sta->acct_input_gigawords++; - if (sta->last_tx_bytes > data->tx_bytes) - sta->acct_output_gigawords++; - sta->last_rx_bytes = data->rx_bytes; - sta->last_tx_bytes = data->tx_bytes; + if (!data->bytes_64bit) { + /* Extend 32-bit counters from the driver to 64-bit counters */ + if (sta->last_rx_bytes_lo > data->rx_bytes) + sta->last_rx_bytes_hi++; + sta->last_rx_bytes_lo = data->rx_bytes; + + if (sta->last_tx_bytes_lo > data->tx_bytes) + sta->last_tx_bytes_hi++; + sta->last_tx_bytes_lo = data->tx_bytes; + } hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_DEBUG, "updated TX/RX stats: " - "Acct-Input-Octets=%lu Acct-Input-Gigawords=%u " - "Acct-Output-Octets=%lu Acct-Output-Gigawords=%u", - sta->last_rx_bytes, sta->acct_input_gigawords, - sta->last_tx_bytes, sta->acct_output_gigawords); + HOSTAPD_LEVEL_DEBUG, + "updated TX/RX stats: rx_bytes=%llu [%u:%u] tx_bytes=%llu [%u:%u] bytes_64bit=%d", + data->rx_bytes, sta->last_rx_bytes_hi, + sta->last_rx_bytes_lo, + data->tx_bytes, sta->last_tx_bytes_hi, + sta->last_tx_bytes_lo, + data->bytes_64bit); return 0; } @@ -217,12 +230,14 @@ void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta) hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, - "starting accounting session %08X-%08X", - sta->acct_session_id_hi, sta->acct_session_id_lo); + "starting accounting session %016llX", + (unsigned long long) sta->acct_session_id); os_get_reltime(&sta->acct_session_start); - sta->last_rx_bytes = sta->last_tx_bytes = 0; - sta->acct_input_gigawords = sta->acct_output_gigawords = 0; + sta->last_rx_bytes_hi = 0; + sta->last_rx_bytes_lo = 0; + sta->last_tx_bytes_hi = 0; + sta->last_tx_bytes_lo = 0; hostapd_drv_sta_clear_stats(hapd, sta->addr); if (!hapd->conf->radius->acct_server) @@ -251,8 +266,7 @@ static void accounting_sta_report(struct hostapd_data *hapd, int cause = sta->acct_terminate_cause; struct hostap_sta_driver_data data; struct os_reltime now_r, diff; - struct os_time now; - u32 gigawords; + u64 bytes; if (!hapd->conf->radius->acct_server) return; @@ -266,7 +280,6 @@ static void accounting_sta_report(struct hostapd_data *hapd, } os_get_reltime(&now_r); - os_get_time(&now); os_reltime_sub(&now_r, &sta->acct_session_start, &diff); if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME, diff.sec)) { @@ -287,48 +300,42 @@ static void accounting_sta_report(struct hostapd_data *hapd, wpa_printf(MSG_INFO, "Could not add Acct-Output-Packets"); goto fail; } + if (data.bytes_64bit) + bytes = data.rx_bytes; + else + bytes = ((u64) sta->last_rx_bytes_hi << 32) | + sta->last_rx_bytes_lo; if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_INPUT_OCTETS, - data.rx_bytes)) { + (u32) bytes)) { wpa_printf(MSG_INFO, "Could not add Acct-Input-Octets"); goto fail; } - gigawords = sta->acct_input_gigawords; -#if __WORDSIZE == 64 - gigawords += data.rx_bytes >> 32; -#endif - if (gigawords && - !radius_msg_add_attr_int32( - msg, RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, - gigawords)) { + if (!radius_msg_add_attr_int32(msg, + RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, + (u32) (bytes >> 32))) { wpa_printf(MSG_INFO, "Could not add Acct-Input-Gigawords"); goto fail; } + if (data.bytes_64bit) + bytes = data.tx_bytes; + else + bytes = ((u64) sta->last_tx_bytes_hi << 32) | + sta->last_tx_bytes_lo; if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_OUTPUT_OCTETS, - data.tx_bytes)) { + (u32) bytes)) { wpa_printf(MSG_INFO, "Could not add Acct-Output-Octets"); goto fail; } - gigawords = sta->acct_output_gigawords; -#if __WORDSIZE == 64 - gigawords += data.tx_bytes >> 32; -#endif - if (gigawords && - !radius_msg_add_attr_int32( - msg, RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, - gigawords)) { + if (!radius_msg_add_attr_int32(msg, + RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, + (u32) (bytes >> 32))) { wpa_printf(MSG_INFO, "Could not add Acct-Output-Gigawords"); goto fail; } } - if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP, - now.sec)) { - wpa_printf(MSG_INFO, "Could not add Event-Timestamp"); - goto fail; - } - if (eloop_terminated()) cause = RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT; @@ -375,22 +382,17 @@ void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta) eloop_cancel_timeout(accounting_interim_update, hapd, sta); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, - "stopped accounting session %08X-%08X", - sta->acct_session_id_hi, - sta->acct_session_id_lo); + "stopped accounting session %016llX", + (unsigned long long) sta->acct_session_id); sta->acct_session_started = 0; } } -void accounting_sta_get_id(struct hostapd_data *hapd, - struct sta_info *sta) +int accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta) { - sta->acct_session_id_lo = hapd->acct_session_id_lo++; - if (hapd->acct_session_id_lo == 0) { - hapd->acct_session_id_hi++; - } - sta->acct_session_id_hi = hapd->acct_session_id_hi; + return radius_gen_session_id((u8 *) &sta->acct_session_id, + sizeof(sta->acct_session_id)); } @@ -437,12 +439,14 @@ static void accounting_report_state(struct hostapd_data *hapd, int on) if (!msg) return; - if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE, - RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT)) - { - wpa_printf(MSG_INFO, "Could not add Acct-Terminate-Cause"); - radius_msg_free(msg); - return; + if (hapd->acct_session_id) { + char buf[20]; + + os_snprintf(buf, sizeof(buf), "%016llX", + (unsigned long long) hapd->acct_session_id); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID, + (u8 *) buf, os_strlen(buf))) + wpa_printf(MSG_ERROR, "Could not add Acct-Session-Id"); } if (radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL) < 0) @@ -450,6 +454,63 @@ static void accounting_report_state(struct hostapd_data *hapd, int on) } +static void accounting_interim_error_cb(const u8 *addr, void *ctx) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + unsigned int i, wait_time; + int res; + + sta = ap_get_sta(hapd, addr); + if (!sta) + return; + sta->acct_interim_errors++; + if (sta->acct_interim_errors > 10 /* RADIUS_CLIENT_MAX_RETRIES */) { + wpa_printf(MSG_DEBUG, + "Interim RADIUS accounting update failed for " MACSTR + " - too many errors, abandon this interim accounting update", + MAC2STR(addr)); + sta->acct_interim_errors = 0; + /* Next update will be tried after normal update interval */ + return; + } + + /* + * Use a shorter update interval as an improved retransmission mechanism + * for failed interim accounting updates. This allows the statistics to + * be updated for each retransmission. + * + * RADIUS client code has already waited RADIUS_CLIENT_FIRST_WAIT. + * Schedule the first retry attempt immediately and every following one + * with exponential backoff. + */ + if (sta->acct_interim_errors == 1) { + wait_time = 0; + } else { + wait_time = 3; /* RADIUS_CLIENT_FIRST_WAIT */ + for (i = 1; i < sta->acct_interim_errors; i++) + wait_time *= 2; + } + res = eloop_deplete_timeout(wait_time, 0, accounting_interim_update, + hapd, sta); + if (res == 1) + wpa_printf(MSG_DEBUG, + "Interim RADIUS accounting update failed for " MACSTR + " (error count: %u) - schedule next update in %u seconds", + MAC2STR(addr), sta->acct_interim_errors, wait_time); + else if (res == 0) + wpa_printf(MSG_DEBUG, + "Interim RADIUS accounting update failed for " MACSTR + " (error count: %u)", MAC2STR(addr), + sta->acct_interim_errors); + else + wpa_printf(MSG_DEBUG, + "Interim RADIUS accounting update failed for " MACSTR + " (error count: %u) - no timer found", MAC2STR(addr), + sta->acct_interim_errors); +} + + /** * accounting_init: Initialize accounting * @hapd: hostapd BSS data @@ -457,20 +518,15 @@ static void accounting_report_state(struct hostapd_data *hapd, int on) */ int accounting_init(struct hostapd_data *hapd) { - struct os_time now; - - /* Acct-Session-Id should be unique over reboots. Using a random number - * is preferred. If that is not available, take the current time. Mix - * in microseconds to make this more likely to be unique. */ - os_get_time(&now); - if (os_get_random((u8 *) &hapd->acct_session_id_hi, - sizeof(hapd->acct_session_id_hi)) < 0) - hapd->acct_session_id_hi = now.sec; - hapd->acct_session_id_hi ^= now.usec; + if (radius_gen_session_id((u8 *) &hapd->acct_session_id, + sizeof(hapd->acct_session_id)) < 0) + return -1; if (radius_client_register(hapd->radius, RADIUS_ACCT, accounting_receive, hapd)) return -1; + radius_client_set_interim_error_cb(hapd->radius, + accounting_interim_error_cb, hapd); accounting_report_state(hapd, 1); diff --git a/contrib/wpa/src/ap/accounting.h b/contrib/wpa/src/ap/accounting.h index dcc54ee94b54..de5a33f3c7ce 100644 --- a/contrib/wpa/src/ap/accounting.h +++ b/contrib/wpa/src/ap/accounting.h @@ -10,9 +10,10 @@ #define ACCOUNTING_H #ifdef CONFIG_NO_ACCOUNTING -static inline void accounting_sta_get_id(struct hostapd_data *hapd, - struct sta_info *sta) +static inline int accounting_sta_get_id(struct hostapd_data *hapd, + struct sta_info *sta) { + return 0; } static inline void accounting_sta_start(struct hostapd_data *hapd, @@ -34,7 +35,7 @@ static inline void accounting_deinit(struct hostapd_data *hapd) { } #else /* CONFIG_NO_ACCOUNTING */ -void accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta); +int accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta); void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta); void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta); int accounting_init(struct hostapd_data *hapd); diff --git a/contrib/wpa/src/ap/acs.c b/contrib/wpa/src/ap/acs.c index 03d797fe8836..5e8380535854 100644 --- a/contrib/wpa/src/ap/acs.c +++ b/contrib/wpa/src/ap/acs.c @@ -599,8 +599,7 @@ acs_find_ideal_chan(struct hostapd_iface *iface) wpa_printf(MSG_DEBUG, "ACS: Survey analysis for selected bandwidth %d MHz", n_chans == 1 ? 20 : n_chans == 2 ? 40 : - n_chans == 4 ? 80 : - -1); + 80); for (i = 0; i < iface->current_mode->num_channels; i++) { double total_weight; @@ -933,6 +932,9 @@ enum hostapd_chan_status acs_init(struct hostapd_iface *iface) return HOSTAPD_CHAN_ACS; } + if (!iface->current_mode) + return HOSTAPD_CHAN_INVALID; + acs_cleanup(iface); err = acs_request_scan(iface); diff --git a/contrib/wpa/src/ap/ap_config.c b/contrib/wpa/src/ap/ap_config.c index 9a96e50b7385..228de2baf946 100644 --- a/contrib/wpa/src/ap/ap_config.c +++ b/contrib/wpa/src/ap/ap_config.c @@ -38,6 +38,8 @@ static void hostapd_config_free_vlan(struct hostapd_bss_config *bss) void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) { + dl_list_init(&bss->anqp_elem); + bss->logger_syslog_level = HOSTAPD_LEVEL_INFO; bss->logger_stdout_level = HOSTAPD_LEVEL_INFO; bss->logger_syslog = (unsigned int) -1; @@ -63,6 +65,7 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) bss->dtim_period = 2; bss->radius_server_auth_port = 1812; + bss->eap_sim_db_timeout = 1; bss->ap_max_inactivity = AP_MAX_INACTIVITY; bss->eapol_version = EAPOL_VERSION; @@ -180,6 +183,7 @@ struct hostapd_config * hostapd_config_defaults(void) conf->ignore_assoc_probability = 0.0; conf->ignore_reassoc_probability = 0.0; conf->corrupt_gtk_rekey_mic_probability = 0.0; + conf->ecsa_ie_only = 0; #endif /* CONFIG_TESTING_OPTIONS */ conf->acs = 0; @@ -198,13 +202,6 @@ int hostapd_mac_comp(const void *a, const void *b) } -int hostapd_mac_comp_empty(const void *a) -{ - macaddr empty = { 0 }; - return os_memcmp(a, empty, sizeof(macaddr)); -} - - static int hostapd_config_read_wpa_psk(const char *fname, struct hostapd_ssid *ssid) { @@ -410,6 +407,19 @@ void hostapd_config_clear_wpa_psk(struct hostapd_wpa_psk **l) } +static void hostapd_config_free_anqp_elem(struct hostapd_bss_config *conf) +{ + struct anqp_element *elem; + + while ((elem = dl_list_first(&conf->anqp_elem, struct anqp_element, + list))) { + dl_list_del(&elem->list); + wpabuf_free(elem->payload); + os_free(elem); + } +} + + void hostapd_config_free_bss(struct hostapd_bss_config *conf) { struct hostapd_eap_user *user, *prev_user; @@ -454,6 +464,7 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(conf->private_key); os_free(conf->private_key_passwd); os_free(conf->ocsp_stapling_response); + os_free(conf->ocsp_stapling_response_multi); os_free(conf->dh_file); os_free(conf->openssl_ciphers); os_free(conf->pac_opaque_encr_key); @@ -523,6 +534,7 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(conf->network_auth_type); os_free(conf->anqp_3gpp_cell_net); os_free(conf->domain_name); + hostapd_config_free_anqp_elem(conf); #ifdef CONFIG_RADIUS_TEST os_free(conf->dump_msk_file); @@ -555,6 +567,7 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) #endif /* CONFIG_HS20 */ wpabuf_free(conf->vendor_elements); + wpabuf_free(conf->assocresp_elements); os_free(conf->sae_groups); @@ -594,6 +607,8 @@ void hostapd_config_free(struct hostapd_config *conf) #ifdef CONFIG_ACS os_free(conf->acs_chan_bias); #endif /* CONFIG_ACS */ + wpabuf_free(conf->lci); + wpabuf_free(conf->civic); os_free(conf); } @@ -610,7 +625,7 @@ void hostapd_config_free(struct hostapd_config *conf) * Perform a binary search for given MAC address from a pre-sorted list. */ int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries, - const u8 *addr, int *vlan_id) + const u8 *addr, struct vlan_description *vlan_id) { int start, end, middle, res; @@ -650,11 +665,26 @@ int hostapd_rate_found(int *list, int rate) } -int hostapd_vlan_id_valid(struct hostapd_vlan *vlan, int vlan_id) +int hostapd_vlan_valid(struct hostapd_vlan *vlan, + struct vlan_description *vlan_desc) { struct hostapd_vlan *v = vlan; + int i; + + if (!vlan_desc->notempty || vlan_desc->untagged < 0 || + vlan_desc->untagged > MAX_VLAN_ID) + return 0; + for (i = 0; i < MAX_NUM_TAGGED_VLAN; i++) { + if (vlan_desc->tagged[i] < 0 || + vlan_desc->tagged[i] > MAX_VLAN_ID) + return 0; + } + if (!vlan_desc->untagged && !vlan_desc->tagged[0]) + return 0; + while (v) { - if (v->vlan_id == vlan_id || v->vlan_id == VLAN_ID_WILDCARD) + if (!vlan_compare(&v->vlan_desc, vlan_desc) || + v->vlan_id == VLAN_ID_WILDCARD) return 1; v = v->next; } @@ -756,7 +786,7 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss, return -1; } - if (full_config && hostapd_mac_comp_empty(bss->bssid) != 0) { + if (full_config && !is_zero_ether_addr(bss->bssid)) { size_t i; for (i = 0; i < conf->num_bss; i++) { @@ -811,6 +841,15 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss, } #endif /* CONFIG_IEEE80211N */ +#ifdef CONFIG_IEEE80211AC + if (full_config && conf->ieee80211ac && + bss->ssid.security_policy == SECURITY_STATIC_WEP) { + bss->disable_11ac = 1; + wpa_printf(MSG_ERROR, + "VHT (IEEE 802.11ac) with WEP is not allowed, disabling VHT capabilities"); + } +#endif /* CONFIG_IEEE80211AC */ + #ifdef CONFIG_WPS if (full_config && bss->wps_state && bss->ignore_broadcast_ssid) { wpa_printf(MSG_INFO, "WPS: ignore_broadcast_ssid " @@ -847,6 +886,15 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss, } #endif /* CONFIG_HS20 */ +#ifdef CONFIG_MBO + if (full_config && bss->mbo_enabled && (bss->wpa & 2) && + bss->ieee80211w == NO_MGMT_FRAME_PROTECTION) { + wpa_printf(MSG_ERROR, + "MBO: PMF needs to be enabled whenever using WPA2 with MBO"); + return -1; + } +#endif /* CONFIG_MBO */ + return 0; } diff --git a/contrib/wpa/src/ap/ap_config.h b/contrib/wpa/src/ap/ap_config.h index de470a969b50..8c8f7e286bda 100644 --- a/contrib/wpa/src/ap/ap_config.h +++ b/contrib/wpa/src/ap/ap_config.h @@ -10,12 +10,14 @@ #define HOSTAPD_CONFIG_H #include "common/defs.h" +#include "utils/list.h" #include "ip_addr.h" #include "common/wpa_common.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "wps/wps.h" #include "fst/fst.h" +#include "vlan.h" /** * mesh_conf - local MBSS state and settings @@ -39,6 +41,10 @@ struct mesh_conf { #define MESH_CONF_SEC_AUTH BIT(1) #define MESH_CONF_SEC_AMPE BIT(2) unsigned int security; + enum mfp_options ieee80211w; + unsigned int pairwise_cipher; + unsigned int group_cipher; + unsigned int mgmt_group_cipher; int dot11MeshMaxRetries; int dot11MeshRetryTimeout; /* msec */ int dot11MeshConfirmTimeout; /* msec */ @@ -52,7 +58,7 @@ typedef u8 macaddr[ETH_ALEN]; struct mac_acl_entry { macaddr addr; - int vlan_id; + struct vlan_description vlan_id; }; struct hostapd_radius_servers; @@ -102,6 +108,7 @@ struct hostapd_ssid { #define DYNAMIC_VLAN_NAMING_WITH_DEVICE 1 #define DYNAMIC_VLAN_NAMING_END 2 int vlan_naming; + int per_sta_vif; #ifdef CONFIG_FULL_DYNAMIC_VLAN char *vlan_tagged_interface; #endif /* CONFIG_FULL_DYNAMIC_VLAN */ @@ -113,6 +120,7 @@ struct hostapd_ssid { struct hostapd_vlan { struct hostapd_vlan *next; int vlan_id; /* VLAN ID or -1 (VLAN_ID_WILDCARD) for wildcard entry */ + struct vlan_description vlan_desc; char ifname[IFNAMSIZ + 1]; int configured; int dynamic_vlan; @@ -124,9 +132,14 @@ struct hostapd_vlan { }; #define PMK_LEN 32 +#define MIN_PASSPHRASE_LEN 8 +#define MAX_PASSPHRASE_LEN 63 struct hostapd_sta_wpa_psk_short { struct hostapd_sta_wpa_psk_short *next; + unsigned int is_passphrase:1; u8 psk[PMK_LEN]; + char passphrase[MAX_PASSPHRASE_LEN + 1]; + int ref; /* (number of references held) - 1 */ }; struct hostapd_wpa_psk { @@ -205,6 +218,13 @@ struct hostapd_nai_realm_data { } eap_method[MAX_NAI_EAP_METHODS]; }; +struct anqp_element { + struct dl_list list; + u16 infoid; + struct wpabuf *payload; +}; + + /** * struct hostapd_bss_config - Per-BSS configuration */ @@ -231,6 +251,7 @@ struct hostapd_bss_config { struct hostapd_eap_user *eap_user; char *eap_user_sqlite; char *eap_sim_db; + unsigned int eap_sim_db_timeout; int eap_server_erp; /* Whether ERP is enabled on internal EAP server */ struct hostapd_ip_addr own_ip_addr; char *nas_identifier; @@ -242,6 +263,7 @@ struct hostapd_bss_config { int radius_das_port; unsigned int radius_das_time_window; int radius_das_require_event_timestamp; + int radius_das_require_message_authenticator; struct hostapd_ip_addr radius_das_client_addr; u8 *radius_das_shared_secret; size_t radius_das_shared_secret_len; @@ -332,6 +354,7 @@ struct hostapd_bss_config { int check_crl; unsigned int tls_session_lifetime; char *ocsp_stapling_response; + char *ocsp_stapling_response_multi; char *dh_file; char *openssl_ciphers; u8 *pac_opaque_encr_key; @@ -358,6 +381,7 @@ struct hostapd_bss_config { int ap_max_inactivity; int ignore_broadcast_ssid; + int no_probe_resp_if_max_sta; int wmm_enabled; int wmm_uapsd; @@ -481,8 +505,11 @@ struct hostapd_bss_config { unsigned int nai_realm_count; struct hostapd_nai_realm_data *nai_realm_data; + struct dl_list anqp_elem; /* list of struct anqp_element */ + u16 gas_comeback_delay; int gas_frag_limit; + int gas_address3; u8 qos_map_set[16 + 2 * 21]; unsigned int qos_map_set_len; @@ -536,6 +563,7 @@ struct hostapd_bss_config { #endif /* CONFIG_RADIUS_TEST */ struct wpabuf *vendor_elements; + struct wpabuf *assocresp_elements; unsigned int sae_anti_clogging_threshold; int *sae_groups; @@ -551,12 +579,22 @@ struct hostapd_bss_config { #define MESH_ENABLED BIT(0) int mesh; - int radio_measurements; + u8 radio_measurements[RRM_CAPABILITIES_IE_LEN]; int vendor_vht; + int use_sta_nsts; char *no_probe_resp_if_seen_on; char *no_auth_if_seen_on; + + int pbss; + +#ifdef CONFIG_MBO + int mbo_enabled; +#endif /* CONFIG_MBO */ + + int ftm_responder; + int ftm_initiator; }; @@ -638,6 +676,9 @@ struct hostapd_config { u8 vht_oper_centr_freq_seg0_idx; u8 vht_oper_centr_freq_seg1_idx; + /* Use driver-generated interface addresses when adding multiple BSSs */ + u8 use_driver_iface_addr; + #ifdef CONFIG_FST struct fst_iface_cfg fst_cfg; #endif /* CONFIG_FST */ @@ -652,6 +693,7 @@ struct hostapd_config { double ignore_assoc_probability; double ignore_reassoc_probability; double corrupt_gtk_rekey_mic_probability; + int ecsa_ie_only; #endif /* CONFIG_TESTING_OPTIONS */ #ifdef CONFIG_ACS @@ -662,11 +704,13 @@ struct hostapd_config { } *acs_chan_bias; unsigned int num_acs_chan_bias; #endif /* CONFIG_ACS */ + + struct wpabuf *lci; + struct wpabuf *civic; }; int hostapd_mac_comp(const void *a, const void *b); -int hostapd_mac_comp_empty(const void *a); struct hostapd_config * hostapd_config_defaults(void); void hostapd_config_defaults_bss(struct hostapd_bss_config *bss); void hostapd_config_free_eap_user(struct hostapd_eap_user *user); @@ -674,13 +718,14 @@ void hostapd_config_clear_wpa_psk(struct hostapd_wpa_psk **p); void hostapd_config_free_bss(struct hostapd_bss_config *conf); void hostapd_config_free(struct hostapd_config *conf); int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries, - const u8 *addr, int *vlan_id); + const u8 *addr, struct vlan_description *vlan_id); int hostapd_rate_found(int *list, int rate); const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf, const u8 *addr, const u8 *p2p_dev_addr, const u8 *prev_psk); int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf); -int hostapd_vlan_id_valid(struct hostapd_vlan *vlan, int vlan_id); +int hostapd_vlan_valid(struct hostapd_vlan *vlan, + struct vlan_description *vlan_desc); const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id); struct hostapd_radius_attr * diff --git a/contrib/wpa/src/ap/ap_drv_ops.c b/contrib/wpa/src/ap/ap_drv_ops.c index 6cafcb749351..f1394654d3a8 100644 --- a/contrib/wpa/src/ap/ap_drv_ops.c +++ b/contrib/wpa/src/ap/ap_drv_ops.c @@ -33,10 +33,36 @@ u32 hostapd_sta_flags_to_drv(u32 flags) res |= WPA_STA_SHORT_PREAMBLE; if (flags & WLAN_STA_MFP) res |= WPA_STA_MFP; + if (flags & WLAN_STA_AUTH) + res |= WPA_STA_AUTHENTICATED; + if (flags & WLAN_STA_ASSOC) + res |= WPA_STA_ASSOCIATED; return res; } +static int add_buf(struct wpabuf **dst, const struct wpabuf *src) +{ + if (!src) + return 0; + if (wpabuf_resize(dst, wpabuf_len(src)) != 0) + return -1; + wpabuf_put_buf(*dst, src); + return 0; +} + + +static int add_buf_data(struct wpabuf **dst, const u8 *data, size_t len) +{ + if (!data || !len) + return 0; + if (wpabuf_resize(dst, len) != 0) + return -1; + wpabuf_put_data(*dst, data, len); + return 0; +} + + int hostapd_build_ap_extra_ies(struct hostapd_data *hapd, struct wpabuf **beacon_ret, struct wpabuf **proberesp_ret, @@ -49,82 +75,38 @@ int hostapd_build_ap_extra_ies(struct hostapd_data *hapd, pos = buf; pos = hostapd_eid_time_adv(hapd, pos); - if (pos != buf) { - if (wpabuf_resize(&beacon, pos - buf) != 0) - goto fail; - wpabuf_put_data(beacon, buf, pos - buf); - } + if (add_buf_data(&beacon, buf, pos - buf) < 0) + goto fail; pos = hostapd_eid_time_zone(hapd, pos); - if (pos != buf) { - if (wpabuf_resize(&proberesp, pos - buf) != 0) - goto fail; - wpabuf_put_data(proberesp, buf, pos - buf); - } + if (add_buf_data(&proberesp, buf, pos - buf) < 0) + goto fail; pos = buf; pos = hostapd_eid_ext_capab(hapd, pos); - if (pos != buf) { - if (wpabuf_resize(&assocresp, pos - buf) != 0) - goto fail; - wpabuf_put_data(assocresp, buf, pos - buf); - } + if (add_buf_data(&assocresp, buf, pos - buf) < 0) + goto fail; pos = hostapd_eid_interworking(hapd, pos); pos = hostapd_eid_adv_proto(hapd, pos); pos = hostapd_eid_roaming_consortium(hapd, pos); - if (pos != buf) { - if (wpabuf_resize(&beacon, pos - buf) != 0) - goto fail; - wpabuf_put_data(beacon, buf, pos - buf); - - if (wpabuf_resize(&proberesp, pos - buf) != 0) - goto fail; - wpabuf_put_data(proberesp, buf, pos - buf); - } + if (add_buf_data(&beacon, buf, pos - buf) < 0 || + add_buf_data(&proberesp, buf, pos - buf) < 0) + goto fail; #ifdef CONFIG_FST - if (hapd->iface->fst_ies) { - size_t add = wpabuf_len(hapd->iface->fst_ies); - - if (wpabuf_resize(&beacon, add) < 0) - goto fail; - wpabuf_put_buf(beacon, hapd->iface->fst_ies); - if (wpabuf_resize(&proberesp, add) < 0) - goto fail; - wpabuf_put_buf(proberesp, hapd->iface->fst_ies); - if (wpabuf_resize(&assocresp, add) < 0) - goto fail; - wpabuf_put_buf(assocresp, hapd->iface->fst_ies); - } + if (add_buf(&beacon, hapd->iface->fst_ies) < 0 || + add_buf(&proberesp, hapd->iface->fst_ies) < 0 || + add_buf(&assocresp, hapd->iface->fst_ies) < 0) + goto fail; #endif /* CONFIG_FST */ - if (hapd->wps_beacon_ie) { - if (wpabuf_resize(&beacon, wpabuf_len(hapd->wps_beacon_ie)) < - 0) - goto fail; - wpabuf_put_buf(beacon, hapd->wps_beacon_ie); - } - - if (hapd->wps_probe_resp_ie) { - if (wpabuf_resize(&proberesp, - wpabuf_len(hapd->wps_probe_resp_ie)) < 0) - goto fail; - wpabuf_put_buf(proberesp, hapd->wps_probe_resp_ie); - } + if (add_buf(&beacon, hapd->wps_beacon_ie) < 0 || + add_buf(&proberesp, hapd->wps_probe_resp_ie) < 0) + goto fail; #ifdef CONFIG_P2P - if (hapd->p2p_beacon_ie) { - if (wpabuf_resize(&beacon, wpabuf_len(hapd->p2p_beacon_ie)) < - 0) - goto fail; - wpabuf_put_buf(beacon, hapd->p2p_beacon_ie); - } - - if (hapd->p2p_probe_resp_ie) { - if (wpabuf_resize(&proberesp, - wpabuf_len(hapd->p2p_probe_resp_ie)) < 0) - goto fail; - wpabuf_put_buf(proberesp, hapd->p2p_probe_resp_ie); - } + if (add_buf(&beacon, hapd->p2p_beacon_ie) < 0 || + add_buf(&proberesp, hapd->p2p_probe_resp_ie) < 0) + goto fail; #endif /* CONFIG_P2P */ #ifdef CONFIG_P2P_MANAGER @@ -148,8 +130,7 @@ int hostapd_build_ap_extra_ies(struct hostapd_data *hapd, #ifdef CONFIG_WPS if (hapd->conf->wps_state) { struct wpabuf *a = wps_build_assoc_resp_ie(); - if (a && wpabuf_resize(&assocresp, wpabuf_len(a)) == 0) - wpabuf_put_buf(assocresp, a); + add_buf(&assocresp, a); wpabuf_free(a); } #endif /* CONFIG_WPS */ @@ -169,44 +150,36 @@ int hostapd_build_ap_extra_ies(struct hostapd_data *hapd, if (hapd->p2p_group) { struct wpabuf *a; a = p2p_group_assoc_resp_ie(hapd->p2p_group, P2P_SC_SUCCESS); - if (a && wpabuf_resize(&assocresp, wpabuf_len(a)) == 0) - wpabuf_put_buf(assocresp, a); + add_buf(&assocresp, a); wpabuf_free(a); } #endif /* CONFIG_WIFI_DISPLAY */ #ifdef CONFIG_HS20 - pos = buf; - pos = hostapd_eid_hs20_indication(hapd, pos); - if (pos != buf) { - if (wpabuf_resize(&beacon, pos - buf) != 0) - goto fail; - wpabuf_put_data(beacon, buf, pos - buf); - - if (wpabuf_resize(&proberesp, pos - buf) != 0) - goto fail; - wpabuf_put_data(proberesp, buf, pos - buf); - } + pos = hostapd_eid_hs20_indication(hapd, buf); + if (add_buf_data(&beacon, buf, pos - buf) < 0 || + add_buf_data(&proberesp, buf, pos - buf) < 0) + goto fail; pos = hostapd_eid_osen(hapd, buf); - if (pos != buf) { - if (wpabuf_resize(&beacon, pos - buf) != 0) - goto fail; - wpabuf_put_data(beacon, buf, pos - buf); - - if (wpabuf_resize(&proberesp, pos - buf) != 0) - goto fail; - wpabuf_put_data(proberesp, buf, pos - buf); - } + if (add_buf_data(&beacon, buf, pos - buf) < 0 || + add_buf_data(&proberesp, buf, pos - buf) < 0) + goto fail; #endif /* CONFIG_HS20 */ - if (hapd->conf->vendor_elements) { - size_t add = wpabuf_len(hapd->conf->vendor_elements); - if (wpabuf_resize(&beacon, add) == 0) - wpabuf_put_buf(beacon, hapd->conf->vendor_elements); - if (wpabuf_resize(&proberesp, add) == 0) - wpabuf_put_buf(proberesp, hapd->conf->vendor_elements); +#ifdef CONFIG_MBO + if (hapd->conf->mbo_enabled) { + pos = hostapd_eid_mbo(hapd, buf, sizeof(buf)); + if (add_buf_data(&beacon, buf, pos - buf) < 0 || + add_buf_data(&proberesp, buf, pos - buf) < 0 || + add_buf_data(&assocresp, buf, pos - buf) < 0) + goto fail; } +#endif /* CONFIG_MBO */ + + add_buf(&beacon, hapd->conf->vendor_elements); + add_buf(&proberesp, hapd->conf->vendor_elements); + add_buf(&assocresp, hapd->conf->assocresp_elements); *beacon_ret = beacon; *proberesp_ret = proberesp; @@ -390,7 +363,8 @@ int hostapd_sta_add(struct hostapd_data *hapd, u16 listen_interval, const struct ieee80211_ht_capabilities *ht_capab, const struct ieee80211_vht_capabilities *vht_capab, - u32 flags, u8 qosinfo, u8 vht_opmode) + u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps, + int set) { struct hostapd_sta_add_params params; @@ -412,6 +386,8 @@ int hostapd_sta_add(struct hostapd_data *hapd, params.vht_opmode = vht_opmode; params.flags = hostapd_sta_flags_to_drv(flags); params.qosinfo = qosinfo; + params.support_p2p_ps = supp_p2p_ps; + params.set = set; return hapd->driver->sta_add(hapd->drv_priv, ¶ms); } @@ -468,7 +444,7 @@ int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type, return -1; return hapd->driver->if_add(hapd->drv_priv, type, ifname, addr, bss_ctx, drv_priv, force_ifname, if_addr, - bridge, use_existing); + bridge, use_existing, 1); } @@ -646,17 +622,29 @@ int hostapd_drv_set_key(const char *ifname, struct hostapd_data *hapd, int hostapd_drv_send_mlme(struct hostapd_data *hapd, const void *msg, size_t len, int noack) +{ + if (!hapd->driver || !hapd->driver->send_mlme || !hapd->drv_priv) + return 0; + return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0, + NULL, 0); +} + + +int hostapd_drv_send_mlme_csa(struct hostapd_data *hapd, + const void *msg, size_t len, int noack, + const u16 *csa_offs, size_t csa_offs_len) { if (hapd->driver == NULL || hapd->driver->send_mlme == NULL) return 0; - return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0); + return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0, + csa_offs, csa_offs_len); } int hostapd_drv_sta_deauth(struct hostapd_data *hapd, const u8 *addr, int reason) { - if (hapd->driver == NULL || hapd->driver->sta_deauth == NULL) + if (!hapd->driver || !hapd->driver->sta_deauth || !hapd->drv_priv) return 0; return hapd->driver->sta_deauth(hapd->drv_priv, hapd->own_addr, addr, reason); @@ -666,7 +654,7 @@ int hostapd_drv_sta_deauth(struct hostapd_data *hapd, int hostapd_drv_sta_disassoc(struct hostapd_data *hapd, const u8 *addr, int reason) { - if (hapd->driver == NULL || hapd->driver->sta_disassoc == NULL) + if (!hapd->driver || !hapd->driver->sta_disassoc || !hapd->drv_priv) return 0; return hapd->driver->sta_disassoc(hapd->drv_priv, hapd->own_addr, addr, reason); @@ -686,6 +674,36 @@ int hostapd_drv_wnm_oper(struct hostapd_data *hapd, enum wnm_oper oper, int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq, unsigned int wait, const u8 *dst, const u8 *data, size_t len) +{ + const u8 *bssid; + const u8 wildcard_bssid[ETH_ALEN] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + }; + + if (!hapd->driver || !hapd->driver->send_action || !hapd->drv_priv) + return 0; + bssid = hapd->own_addr; + if (!is_multicast_ether_addr(dst) && + len > 0 && data[0] == WLAN_ACTION_PUBLIC) { + struct sta_info *sta; + + /* + * Public Action frames to a STA that is not a member of the BSS + * shall use wildcard BSSID value. + */ + sta = ap_get_sta(hapd, dst); + if (!sta || !(sta->flags & WLAN_STA_ASSOC)) + bssid = wildcard_bssid; + } + return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst, + hapd->own_addr, bssid, data, len, 0); +} + + +int hostapd_drv_send_action_addr3_ap(struct hostapd_data *hapd, + unsigned int freq, + unsigned int wait, const u8 *dst, + const u8 *data, size_t len) { if (hapd->driver == NULL || hapd->driver->send_action == NULL) return 0; @@ -736,7 +754,7 @@ int hostapd_start_dfs_cac(struct hostapd_iface *iface, int hostapd_drv_set_qos_map(struct hostapd_data *hapd, const u8 *qos_map_set, u8 qos_map_set_len) { - if (hapd->driver == NULL || hapd->driver->set_qos_map == NULL) + if (!hapd->driver || !hapd->driver->set_qos_map || !hapd->drv_priv) return 0; return hapd->driver->set_qos_map(hapd->drv_priv, qos_map_set, qos_map_set_len); @@ -762,6 +780,20 @@ static void hostapd_get_hw_mode_any_channels(struct hostapd_data *hapd, } +void hostapd_get_ext_capa(struct hostapd_iface *iface) +{ + struct hostapd_data *hapd = iface->bss[0]; + + if (!hapd->driver || !hapd->driver->get_ext_capab) + return; + + hapd->driver->get_ext_capab(hapd->drv_priv, WPA_IF_AP_BSS, + &iface->extended_capa, + &iface->extended_capa_mask, + &iface->extended_capa_len); +} + + int hostapd_drv_do_acs(struct hostapd_data *hapd) { struct drv_acs_params params; diff --git a/contrib/wpa/src/ap/ap_drv_ops.h b/contrib/wpa/src/ap/ap_drv_ops.h index 82eaf3f08bb5..0bb7954ec061 100644 --- a/contrib/wpa/src/ap/ap_drv_ops.h +++ b/contrib/wpa/src/ap/ap_drv_ops.h @@ -41,7 +41,8 @@ int hostapd_sta_add(struct hostapd_data *hapd, u16 listen_interval, const struct ieee80211_ht_capabilities *ht_capab, const struct ieee80211_vht_capabilities *vht_capab, - u32 flags, u8 qosinfo, u8 vht_opmode); + u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps, + int set); int hostapd_set_privacy(struct hostapd_data *hapd, int enabled); int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem, size_t elem_len); @@ -88,6 +89,9 @@ int hostapd_drv_set_key(const char *ifname, const u8 *key, size_t key_len); int hostapd_drv_send_mlme(struct hostapd_data *hapd, const void *msg, size_t len, int noack); +int hostapd_drv_send_mlme_csa(struct hostapd_data *hapd, + const void *msg, size_t len, int noack, + const u16 *csa_offs, size_t csa_offs_len); int hostapd_drv_sta_deauth(struct hostapd_data *hapd, const u8 *addr, int reason); int hostapd_drv_sta_disassoc(struct hostapd_data *hapd, @@ -95,6 +99,10 @@ int hostapd_drv_sta_disassoc(struct hostapd_data *hapd, int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq, unsigned int wait, const u8 *dst, const u8 *data, size_t len); +int hostapd_drv_send_action_addr3_ap(struct hostapd_data *hapd, + unsigned int freq, + unsigned int wait, const u8 *dst, + const u8 *data, size_t len); int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr, u16 auth_alg); int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr, @@ -120,6 +128,8 @@ int hostapd_drv_wnm_oper(struct hostapd_data *hapd, int hostapd_drv_set_qos_map(struct hostapd_data *hapd, const u8 *qos_map_set, u8 qos_map_set_len); +void hostapd_get_ext_capa(struct hostapd_iface *iface); + static inline int hostapd_drv_set_countermeasures(struct hostapd_data *hapd, int enabled) { @@ -150,7 +160,7 @@ static inline int hostapd_drv_get_inact_sec(struct hostapd_data *hapd, static inline int hostapd_drv_sta_remove(struct hostapd_data *hapd, const u8 *addr) { - if (hapd->driver == NULL || hapd->driver->sta_remove == NULL) + if (!hapd->driver || !hapd->driver->sta_remove || !hapd->drv_priv) return 0; return hapd->driver->sta_remove(hapd->drv_priv, addr); } @@ -273,7 +283,7 @@ static inline int hostapd_drv_switch_channel(struct hostapd_data *hapd, static inline int hostapd_drv_status(struct hostapd_data *hapd, char *buf, size_t buflen) { - if (hapd->driver == NULL || hapd->driver->status == NULL) + if (!hapd->driver || !hapd->driver->status || !hapd->drv_priv) return -1; return hapd->driver->status(hapd->drv_priv, buf, buflen); } @@ -332,7 +342,7 @@ static inline int hostapd_drv_vendor_cmd(struct hostapd_data *hapd, static inline int hostapd_drv_stop_ap(struct hostapd_data *hapd) { - if (hapd->driver == NULL || hapd->driver->stop_ap == NULL) + if (!hapd->driver || !hapd->driver->stop_ap || !hapd->drv_priv) return 0; return hapd->driver->stop_ap(hapd->drv_priv); } diff --git a/contrib/wpa/src/ap/ap_mlme.c b/contrib/wpa/src/ap/ap_mlme.c index 13604edc4940..e7308a01d743 100644 --- a/contrib/wpa/src/ap/ap_mlme.c +++ b/contrib/wpa/src/ap/ap_mlme.c @@ -59,6 +59,7 @@ void mlme_authenticate_indication(struct hostapd_data *hapd, MAC2STR(sta->addr), mlme_auth_alg_str(sta->auth_alg)); if (sta->auth_alg != WLAN_AUTH_FT && !(sta->flags & WLAN_STA_MFP)) mlme_deletekeys_request(hapd, sta); + ap_sta_clear_disconnect_timeouts(hapd, sta); } @@ -106,6 +107,7 @@ void mlme_associate_indication(struct hostapd_data *hapd, struct sta_info *sta) MAC2STR(sta->addr)); if (sta->auth_alg != WLAN_AUTH_FT) mlme_deletekeys_request(hapd, sta); + ap_sta_clear_disconnect_timeouts(hapd, sta); } @@ -130,6 +132,7 @@ void mlme_reassociate_indication(struct hostapd_data *hapd, MAC2STR(sta->addr)); if (sta->auth_alg != WLAN_AUTH_FT) mlme_deletekeys_request(hapd, sta); + ap_sta_clear_disconnect_timeouts(hapd, sta); } diff --git a/contrib/wpa/src/ap/authsrv.c b/contrib/wpa/src/ap/authsrv.c index 934dcfc8d631..cdb49cdd9d32 100644 --- a/contrib/wpa/src/ap/authsrv.c +++ b/contrib/wpa/src/ap/authsrv.c @@ -173,6 +173,8 @@ int authsrv_init(struct hostapd_data *hapd) params.openssl_ciphers = hapd->conf->openssl_ciphers; params.ocsp_stapling_response = hapd->conf->ocsp_stapling_response; + params.ocsp_stapling_response_multi = + hapd->conf->ocsp_stapling_response_multi; if (tls_global_set_params(hapd->ssl_ctx, ¶ms)) { wpa_printf(MSG_ERROR, "Failed to set TLS parameters"); @@ -193,6 +195,7 @@ int authsrv_init(struct hostapd_data *hapd) if (hapd->conf->eap_sim_db) { hapd->eap_sim_db_priv = eap_sim_db_init(hapd->conf->eap_sim_db, + hapd->conf->eap_sim_db_timeout, hostapd_sim_db_cb, hapd); if (hapd->eap_sim_db_priv == NULL) { wpa_printf(MSG_ERROR, "Failed to initialize EAP-SIM " diff --git a/contrib/wpa/src/ap/beacon.c b/contrib/wpa/src/ap/beacon.c index 5fe8fd5660b4..233320d2e978 100644 --- a/contrib/wpa/src/ap/beacon.c +++ b/contrib/wpa/src/ap/beacon.c @@ -29,6 +29,7 @@ #include "beacon.h" #include "hs20.h" #include "dfs.h" +#include "taxonomy.h" #ifdef NEED_AP_MLME @@ -36,18 +37,21 @@ static u8 * hostapd_eid_rm_enabled_capab(struct hostapd_data *hapd, u8 *eid, size_t len) { - if (!hapd->conf->radio_measurements || len < 2 + 4) + size_t i; + + for (i = 0; i < RRM_CAPABILITIES_IE_LEN; i++) { + if (hapd->conf->radio_measurements[i]) + break; + } + + if (i == RRM_CAPABILITIES_IE_LEN || len < 2 + RRM_CAPABILITIES_IE_LEN) return eid; *eid++ = WLAN_EID_RRM_ENABLED_CAPABILITIES; - *eid++ = 5; - *eid++ = (hapd->conf->radio_measurements & BIT(0)) ? - WLAN_RRM_CAPS_NEIGHBOR_REPORT : 0x00; - *eid++ = 0x00; - *eid++ = 0x00; - *eid++ = 0x00; - *eid++ = 0x00; - return eid; + *eid++ = RRM_CAPABILITIES_IE_LEN; + os_memcpy(eid, hapd->conf->radio_measurements, RRM_CAPABILITIES_IE_LEN); + + return eid + RRM_CAPABILITIES_IE_LEN; } @@ -297,65 +301,65 @@ static u8 * hostapd_eid_wpa(struct hostapd_data *hapd, u8 *eid, size_t len) static u8 * hostapd_eid_csa(struct hostapd_data *hapd, u8 *eid) { - u8 chan; - - if (!hapd->cs_freq_params.freq) +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->iface->cs_oper_class && hapd->iconf->ecsa_ie_only) return eid; +#endif /* CONFIG_TESTING_OPTIONS */ - if (ieee80211_freq_to_chan(hapd->cs_freq_params.freq, &chan) == - NUM_HOSTAPD_MODES) + if (!hapd->cs_freq_params.channel) return eid; *eid++ = WLAN_EID_CHANNEL_SWITCH; *eid++ = 3; *eid++ = hapd->cs_block_tx; - *eid++ = chan; + *eid++ = hapd->cs_freq_params.channel; *eid++ = hapd->cs_count; return eid; } -static u8 * hostapd_eid_secondary_channel(struct hostapd_data *hapd, u8 *eid) +static u8 * hostapd_eid_ecsa(struct hostapd_data *hapd, u8 *eid) { - u8 sec_ch; - - if (!hapd->cs_freq_params.sec_channel_offset) + if (!hapd->cs_freq_params.channel || !hapd->iface->cs_oper_class) return eid; - if (hapd->cs_freq_params.sec_channel_offset == -1) - sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW; - else if (hapd->cs_freq_params.sec_channel_offset == 1) - sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE; - else - return eid; - - *eid++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET; - *eid++ = 1; - *eid++ = sec_ch; + *eid++ = WLAN_EID_EXT_CHANSWITCH_ANN; + *eid++ = 4; + *eid++ = hapd->cs_block_tx; + *eid++ = hapd->iface->cs_oper_class; + *eid++ = hapd->cs_freq_params.channel; + *eid++ = hapd->cs_count; return eid; } -static u8 * hostapd_add_csa_elems(struct hostapd_data *hapd, u8 *pos, - u8 *start, unsigned int *csa_counter_off) +static u8 * hostapd_eid_supported_op_classes(struct hostapd_data *hapd, u8 *eid) { - u8 *old_pos = pos; + u8 op_class, channel; - if (!csa_counter_off) - return pos; + if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) || + !hapd->iface->freq) + return eid; - *csa_counter_off = 0; - pos = hostapd_eid_csa(hapd, pos); + if (ieee80211_freq_to_channel_ext(hapd->iface->freq, + hapd->iconf->secondary_channel, + hapd->iconf->vht_oper_chwidth, + &op_class, &channel) == + NUM_HOSTAPD_MODES) + return eid; - if (pos != old_pos) { - /* save an offset to the counter - should be last byte */ - *csa_counter_off = pos - start - 1; - pos = hostapd_eid_secondary_channel(hapd, pos); - } + *eid++ = WLAN_EID_SUPPORTED_OPERATING_CLASSES; + *eid++ = 2; - return pos; + /* Current Operating Class */ + *eid++ = op_class; + + /* TODO: Advertise all the supported operating classes */ + *eid++ = 0; + + return eid; } @@ -364,7 +368,7 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, int is_p2p, size_t *resp_len) { struct ieee80211_mgmt *resp; - u8 *pos, *epos; + u8 *pos, *epos, *csa_pos; size_t buflen; #define MAX_PROBERESP_LEN 768 @@ -387,6 +391,9 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, buflen += 5 + 2 + sizeof(struct ieee80211_vht_capabilities) + 2 + sizeof(struct ieee80211_vht_operation); } + + buflen += hostapd_mbo_ie_len(hapd); + resp = os_zalloc(buflen); if (resp == NULL) return NULL; @@ -424,6 +431,12 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, /* Power Constraint element */ pos = hostapd_eid_pwr_constraint(hapd, pos); + /* CSA IE */ + csa_pos = hostapd_eid_csa(hapd, pos); + if (csa_pos != pos) + hapd->cs_c_off_proberesp = csa_pos - (u8 *) resp - 1; + pos = csa_pos; + /* ERP Information element */ pos = hostapd_eid_erp_info(hapd, pos); @@ -437,7 +450,19 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, pos = hostapd_eid_rm_enabled_capab(hapd, pos, epos - pos); + /* eCSA IE */ + csa_pos = hostapd_eid_ecsa(hapd, pos); + if (csa_pos != pos) + hapd->cs_c_off_ecsa_proberesp = csa_pos - (u8 *) resp - 1; + pos = csa_pos; + + pos = hostapd_eid_supported_op_classes(hapd, pos); + #ifdef CONFIG_IEEE80211N + /* Secondary Channel Offset element */ + /* TODO: The standard doesn't specify a position for this element. */ + pos = hostapd_eid_secondary_channel(hapd, pos); + pos = hostapd_eid_ht_capabilities(hapd, pos); pos = hostapd_eid_ht_operation(hapd, pos); #endif /* CONFIG_IEEE80211N */ @@ -451,9 +476,6 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, pos = hostapd_eid_adv_proto(hapd, pos); pos = hostapd_eid_roaming_consortium(hapd, pos); - pos = hostapd_add_csa_elems(hapd, pos, (u8 *)resp, - &hapd->cs_c_off_proberesp); - #ifdef CONFIG_FST if (hapd->iface->fst_ies) { os_memcpy(pos, wpabuf_head(hapd->iface->fst_ies), @@ -464,8 +486,10 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, #ifdef CONFIG_IEEE80211AC if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) { - pos = hostapd_eid_vht_capabilities(hapd, pos); + pos = hostapd_eid_vht_capabilities(hapd, pos, 0); pos = hostapd_eid_vht_operation(hapd, pos); + pos = hostapd_eid_txpower_envelope(hapd, pos); + pos = hostapd_eid_wb_chsw_wrapper(hapd, pos); } if (hapd->conf->vendor_vht) pos = hostapd_eid_vendor_vht(hapd, pos); @@ -501,6 +525,8 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, pos = hostapd_eid_osen(hapd, pos); #endif /* CONFIG_HS20 */ + pos = hostapd_eid_mbo(hapd, pos, (u8 *) resp + buflen - pos); + if (hapd->conf->vendor_elements) { os_memcpy(pos, wpabuf_head(hapd->conf->vendor_elements), wpabuf_len(hapd->conf->vendor_elements)); @@ -537,8 +563,8 @@ static enum ssid_match_result ssid_match(struct hostapd_data *hapd, pos = ssid_list; end = ssid_list + ssid_list_len; - while (pos + 1 <= end) { - if (pos + 2 + pos[1] > end) + while (end - pos >= 1) { + if (2 + pos[1] > end - pos) break; if (pos[1] == 0) wildcard = 1; @@ -574,7 +600,7 @@ void sta_track_expire(struct hostapd_iface *iface, int force) MAC2STR(info->addr)); dl_list_del(&info->list); iface->num_sta_seen--; - os_free(info); + sta_track_del(info); } } @@ -607,6 +633,8 @@ void sta_track_add(struct hostapd_iface *iface, const u8 *addr) /* Add a new entry */ info = os_zalloc(sizeof(*info)); + if (info == NULL) + return; os_memcpy(info->addr, addr, ETH_ALEN); os_get_reltime(&info->last_seen); @@ -648,6 +676,23 @@ sta_track_seen_on(struct hostapd_iface *iface, const u8 *addr, } +#ifdef CONFIG_TAXONOMY +void sta_track_claim_taxonomy_info(struct hostapd_iface *iface, const u8 *addr, + struct wpabuf **probe_ie_taxonomy) +{ + struct hostapd_sta_info *info; + + info = sta_track_get(iface, addr); + if (!info) + return; + + wpabuf_free(*probe_ie_taxonomy); + *probe_ie_taxonomy = info->probe_ie_taxonomy; + info->probe_ie_taxonomy = NULL; +} +#endif /* CONFIG_TAXONOMY */ + + void handle_probe_req(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, int ssi_signal) @@ -659,13 +704,16 @@ void handle_probe_req(struct hostapd_data *hapd, size_t i, resp_len; int noack; enum ssid_match_result res; + int ret; + u16 csa_offs[2]; + size_t csa_offs_len; - ie = mgmt->u.probe_req.variable; - if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)) + if (len < IEEE80211_HDRLEN) return; + ie = ((const u8 *) mgmt) + IEEE80211_HDRLEN; if (hapd->iconf->track_sta_max_num) sta_track_add(hapd->iface, mgmt->sa); - ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)); + ie_len = len - IEEE80211_HDRLEN; for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx, @@ -711,7 +759,7 @@ void handle_probe_req(struct hostapd_data *hapd, } #ifdef CONFIG_P2P - if (hapd->p2p && elems.wps_ie) { + if (hapd->p2p && hapd->p2p_group && elems.wps_ie) { struct wpabuf *wps; wps = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA); if (wps && !p2p_group_match_dev_type(hapd->p2p_group, wps)) { @@ -724,7 +772,7 @@ void handle_probe_req(struct hostapd_data *hapd, wpabuf_free(wps); } - if (hapd->p2p && elems.p2p) { + if (hapd->p2p && hapd->p2p_group && elems.p2p) { struct wpabuf *p2p; p2p = ieee802_11_vendor_ie_concat(ie, ie_len, P2P_IE_VENDOR_TYPE); if (p2p && !p2p_group_match_dev_id(hapd->p2p_group, p2p)) { @@ -754,6 +802,21 @@ void handle_probe_req(struct hostapd_data *hapd, } #endif /* CONFIG_P2P */ +#ifdef CONFIG_TAXONOMY + { + struct sta_info *sta; + struct hostapd_sta_info *info; + + if ((sta = ap_get_sta(hapd, mgmt->sa)) != NULL) { + taxonomy_sta_info_probe_req(hapd, sta, ie, ie_len); + } else if ((info = sta_track_get(hapd->iface, + mgmt->sa)) != NULL) { + taxonomy_hostapd_sta_info_probe_req(hapd, info, + ie, ie_len); + } + } +#endif /* CONFIG_TAXONOMY */ + res = ssid_match(hapd, elems.ssid, elems.ssid_len, elems.ssid_list, elems.ssid_list_len); if (res == NO_SSID_MATCH) { @@ -825,6 +888,17 @@ void handle_probe_req(struct hostapd_data *hapd, return; } + if (hapd->conf->no_probe_resp_if_max_sta && + is_multicast_ether_addr(mgmt->da) && + is_multicast_ether_addr(mgmt->bssid) && + hapd->num_sta >= hapd->conf->max_num_sta && + !ap_get_sta(hapd, mgmt->sa)) { + wpa_printf(MSG_MSGDUMP, "%s: Ignore Probe Request from " MACSTR + " since no room for additional STA", + hapd->conf->iface, MAC2STR(mgmt->sa)); + return; + } + #ifdef CONFIG_TESTING_OPTIONS if (hapd->iconf->ignore_probe_probability > 0.0 && drand48() < hapd->iconf->ignore_probe_probability) { @@ -847,7 +921,22 @@ void handle_probe_req(struct hostapd_data *hapd, noack = !!(res == WILDCARD_SSID_MATCH && is_broadcast_ether_addr(mgmt->da)); - if (hostapd_drv_send_mlme(hapd, resp, resp_len, noack) < 0) + csa_offs_len = 0; + if (hapd->csa_in_progress) { + if (hapd->cs_c_off_proberesp) + csa_offs[csa_offs_len++] = + hapd->cs_c_off_proberesp; + + if (hapd->cs_c_off_ecsa_proberesp) + csa_offs[csa_offs_len++] = + hapd->cs_c_off_ecsa_proberesp; + } + + ret = hostapd_drv_send_mlme_csa(hapd, resp, resp_len, noack, + csa_offs_len ? csa_offs : NULL, + csa_offs_len); + + if (ret < 0) wpa_printf(MSG_INFO, "handle_probe_req: send failed"); os_free(resp); @@ -896,6 +985,16 @@ static u8 * hostapd_probe_resp_offloads(struct hostapd_data *hapd, #endif /* NEED_AP_MLME */ +void sta_track_del(struct hostapd_sta_info *info) +{ +#ifdef CONFIG_TAXONOMY + wpabuf_free(info->probe_ie_taxonomy); + info->probe_ie_taxonomy = NULL; +#endif /* CONFIG_TAXONOMY */ + os_free(info); +} + + int ieee802_11_build_ap_params(struct hostapd_data *hapd, struct wpa_driver_ap_params *params) { @@ -906,7 +1005,7 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, size_t resp_len = 0; #ifdef NEED_AP_MLME u16 capab_info; - u8 *pos, *tailpos; + u8 *pos, *tailpos, *csa_pos; #define BEACON_HEAD_BUF_SIZE 256 #define BEACON_TAIL_BUF_SIZE 512 @@ -934,6 +1033,8 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, } #endif /* CONFIG_IEEE80211AC */ + tail_len += hostapd_mbo_ie_len(hapd); + tailpos = tail = os_malloc(tail_len); if (head == NULL || tail == NULL) { wpa_printf(MSG_ERROR, "Failed to set beacon data"); @@ -987,6 +1088,12 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, /* Power Constraint element */ tailpos = hostapd_eid_pwr_constraint(hapd, tailpos); + /* CSA IE */ + csa_pos = hostapd_eid_csa(hapd, tailpos); + if (csa_pos != tailpos) + hapd->cs_c_off_beacon = csa_pos - tail - 1; + tailpos = csa_pos; + /* ERP Information element */ tailpos = hostapd_eid_erp_info(hapd, tailpos); @@ -1004,7 +1111,19 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, tailpos = hostapd_eid_bss_load(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE - tailpos); + /* eCSA IE */ + csa_pos = hostapd_eid_ecsa(hapd, tailpos); + if (csa_pos != tailpos) + hapd->cs_c_off_ecsa_beacon = csa_pos - tail - 1; + tailpos = csa_pos; + + tailpos = hostapd_eid_supported_op_classes(hapd, tailpos); + #ifdef CONFIG_IEEE80211N + /* Secondary Channel Offset element */ + /* TODO: The standard doesn't specify a position for this element. */ + tailpos = hostapd_eid_secondary_channel(hapd, tailpos); + tailpos = hostapd_eid_ht_capabilities(hapd, tailpos); tailpos = hostapd_eid_ht_operation(hapd, tailpos); #endif /* CONFIG_IEEE80211N */ @@ -1020,8 +1139,6 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, tailpos = hostapd_eid_interworking(hapd, tailpos); tailpos = hostapd_eid_adv_proto(hapd, tailpos); tailpos = hostapd_eid_roaming_consortium(hapd, tailpos); - tailpos = hostapd_add_csa_elems(hapd, tailpos, tail, - &hapd->cs_c_off_beacon); #ifdef CONFIG_FST if (hapd->iface->fst_ies) { @@ -1033,8 +1150,10 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, #ifdef CONFIG_IEEE80211AC if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) { - tailpos = hostapd_eid_vht_capabilities(hapd, tailpos); + tailpos = hostapd_eid_vht_capabilities(hapd, tailpos, 0); tailpos = hostapd_eid_vht_operation(hapd, tailpos); + tailpos = hostapd_eid_txpower_envelope(hapd, tailpos); + tailpos = hostapd_eid_wb_chsw_wrapper(hapd, tailpos); } if (hapd->conf->vendor_vht) tailpos = hostapd_eid_vendor_vht(hapd, tailpos); @@ -1069,6 +1188,8 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, tailpos = hostapd_eid_osen(hapd, tailpos); #endif /* CONFIG_HS20 */ + tailpos = hostapd_eid_mbo(hapd, tailpos, tail + tail_len - tailpos); + if (hapd->conf->vendor_elements) { os_memcpy(tailpos, wpabuf_head(hapd->conf->vendor_elements), wpabuf_len(hapd->conf->vendor_elements)); @@ -1153,6 +1274,7 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, params->osen = 1; } #endif /* CONFIG_HS20 */ + params->pbss = hapd->conf->pbss; return 0; } diff --git a/contrib/wpa/src/ap/beacon.h b/contrib/wpa/src/ap/beacon.h index d98f42e8157a..fc711815cf65 100644 --- a/contrib/wpa/src/ap/beacon.h +++ b/contrib/wpa/src/ap/beacon.h @@ -22,9 +22,12 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, struct wpa_driver_ap_params *params); void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params); void sta_track_add(struct hostapd_iface *iface, const u8 *addr); +void sta_track_del(struct hostapd_sta_info *info); void sta_track_expire(struct hostapd_iface *iface, int force); struct hostapd_data * sta_track_seen_on(struct hostapd_iface *iface, const u8 *addr, const char *ifname); +void sta_track_claim_taxonomy_info(struct hostapd_iface *iface, const u8 *addr, + struct wpabuf **probe_ie_taxonomy); #endif /* BEACON_H */ diff --git a/contrib/wpa/src/ap/ctrl_iface_ap.c b/contrib/wpa/src/ap/ctrl_iface_ap.c index c98978f33d05..3680fda3153f 100644 --- a/contrib/wpa/src/ap/ctrl_iface_ap.c +++ b/contrib/wpa/src/ap/ctrl_iface_ap.c @@ -22,6 +22,8 @@ #include "p2p_hostapd.h" #include "ctrl_iface_ap.h" #include "ap_drv_ops.h" +#include "mbo_ap.h" +#include "taxonomy.h" static int hostapd_get_sta_tx_rx(struct hostapd_data *hapd, @@ -35,9 +37,9 @@ static int hostapd_get_sta_tx_rx(struct hostapd_data *hapd, return 0; ret = os_snprintf(buf, buflen, "rx_packets=%lu\ntx_packets=%lu\n" - "rx_bytes=%lu\ntx_bytes=%lu\n", + "rx_bytes=%llu\ntx_bytes=%llu\ninactive_msec=%lu\n", data.rx_packets, data.tx_packets, - data.rx_bytes, data.tx_bytes); + data.rx_bytes, data.tx_bytes, data.inactive_msec); if (os_snprintf_error(buflen, ret)) return 0; return ret; @@ -161,6 +163,19 @@ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd, len += res; } + res = mbo_ap_get_info(sta, buf + len, buflen - len); + if (res >= 0) + len += res; + + if (sta->supp_op_classes && + buflen - len > (unsigned) (17 + 2 * sta->supp_op_classes[0])) { + len += os_snprintf(buf + len, buflen - len, "supp_op_classes="); + len += wpa_snprintf_hex(buf + len, buflen - len, + sta->supp_op_classes + 1, + sta->supp_op_classes[0]); + len += os_snprintf(buf + len, buflen - len, "\n"); + } + return len; } @@ -244,7 +259,7 @@ static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype, int ret; u8 *pos; - if (hapd->driver->send_frame == NULL) + if (!hapd->drv_priv || !hapd->driver->send_frame) return -1; mgmt = os_zalloc(sizeof(*mgmt) + 100); @@ -255,7 +270,7 @@ static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype, wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "P2P: Disconnect STA " MACSTR " with minor reason code %u (stype=%u (%s))", MAC2STR(addr), minor_reason_code, stype, - fc2str(mgmt->frame_control)); + fc2str(le_to_host16(mgmt->frame_control))); os_memcpy(mgmt->da, addr, ETH_ALEN); os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); @@ -311,7 +326,7 @@ int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd, if (pos) { struct ieee80211_mgmt mgmt; int encrypt; - if (hapd->driver->send_frame == NULL) + if (!hapd->drv_priv || !hapd->driver->send_frame) return -1; pos += 6; encrypt = atoi(pos); @@ -338,7 +353,10 @@ int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd, } #endif /* CONFIG_P2P_MANAGER */ - hostapd_drv_sta_deauth(hapd, addr, reason); + if (os_strstr(txtaddr, " tx=0")) + hostapd_drv_sta_remove(hapd, addr); + else + hostapd_drv_sta_deauth(hapd, addr, reason); sta = ap_get_sta(hapd, addr); if (sta) ap_sta_deauthenticate(hapd, sta, reason); @@ -371,7 +389,7 @@ int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd, if (pos) { struct ieee80211_mgmt mgmt; int encrypt; - if (hapd->driver->send_frame == NULL) + if (!hapd->drv_priv || !hapd->driver->send_frame) return -1; pos += 6; encrypt = atoi(pos); @@ -398,7 +416,10 @@ int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd, } #endif /* CONFIG_P2P_MANAGER */ - hostapd_drv_sta_disassoc(hapd, addr, reason); + if (os_strstr(txtaddr, " tx=0")) + hostapd_drv_sta_remove(hapd, addr); + else + hostapd_drv_sta_disassoc(hapd, addr, reason); sta = ap_get_sta(hapd, addr); if (sta) ap_sta_disassociate(hapd, sta, reason); @@ -409,6 +430,49 @@ int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd, } +#ifdef CONFIG_TAXONOMY +int hostapd_ctrl_iface_signature(struct hostapd_data *hapd, + const char *txtaddr, + char *buf, size_t buflen) +{ + u8 addr[ETH_ALEN]; + struct sta_info *sta; + + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE SIGNATURE %s", txtaddr); + + if (hwaddr_aton(txtaddr, addr)) + return -1; + + sta = ap_get_sta(hapd, addr); + if (!sta) + return -1; + + return retrieve_sta_taxonomy(hapd, sta, buf, buflen); +} +#endif /* CONFIG_TAXONOMY */ + + +int hostapd_ctrl_iface_poll_sta(struct hostapd_data *hapd, + const char *txtaddr) +{ + u8 addr[ETH_ALEN]; + struct sta_info *sta; + + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE POLL_STA %s", txtaddr); + + if (hwaddr_aton(txtaddr, addr)) + return -1; + + sta = ap_get_sta(hapd, addr); + if (!sta) + return -1; + + hostapd_drv_poll_client(hapd, hapd->own_addr, addr, + sta->flags & WLAN_STA_WMM); + return 0; +} + + int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, size_t buflen) { @@ -473,20 +537,28 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, "channel=%u\n" "secondary_channel=%d\n" "ieee80211n=%d\n" - "ieee80211ac=%d\n" - "vht_oper_chwidth=%d\n" - "vht_oper_centr_freq_seg0_idx=%d\n" - "vht_oper_centr_freq_seg1_idx=%d\n", + "ieee80211ac=%d\n", iface->conf->channel, - iface->conf->secondary_channel, - iface->conf->ieee80211n, - iface->conf->ieee80211ac, - iface->conf->vht_oper_chwidth, - iface->conf->vht_oper_centr_freq_seg0_idx, - iface->conf->vht_oper_centr_freq_seg1_idx); + iface->conf->ieee80211n && !hapd->conf->disable_11n ? + iface->conf->secondary_channel : 0, + iface->conf->ieee80211n && !hapd->conf->disable_11n, + iface->conf->ieee80211ac && + !hapd->conf->disable_11ac); if (os_snprintf_error(buflen - len, ret)) return len; len += ret; + if (iface->conf->ieee80211ac && !hapd->conf->disable_11ac) { + ret = os_snprintf(buf + len, buflen - len, + "vht_oper_chwidth=%d\n" + "vht_oper_centr_freq_seg0_idx=%d\n" + "vht_oper_centr_freq_seg1_idx=%d\n", + iface->conf->vht_oper_chwidth, + iface->conf->vht_oper_centr_freq_seg0_idx, + iface->conf->vht_oper_centr_freq_seg1_idx); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + } for (i = 0; i < iface->num_bss; i++) { struct hostapd_data *bss = iface->bss[i]; @@ -554,3 +626,16 @@ int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd) { return hostapd_drv_stop_ap(hapd); } + + +int hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf, + size_t len) +{ + return wpa_auth_pmksa_list(hapd->wpa_auth, buf, len); +} + + +void hostapd_ctrl_iface_pmksa_flush(struct hostapd_data *hapd) +{ + wpa_auth_pmksa_flush(hapd->wpa_auth); +} diff --git a/contrib/wpa/src/ap/ctrl_iface_ap.h b/contrib/wpa/src/ap/ctrl_iface_ap.h index e5297d03e810..4f996800f132 100644 --- a/contrib/wpa/src/ap/ctrl_iface_ap.h +++ b/contrib/wpa/src/ap/ctrl_iface_ap.h @@ -19,10 +19,18 @@ int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd, const char *txtaddr); int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd, const char *txtaddr); +int hostapd_ctrl_iface_signature(struct hostapd_data *hapd, + const char *txtaddr, + char *buf, size_t buflen); +int hostapd_ctrl_iface_poll_sta(struct hostapd_data *hapd, + const char *txtaddr); int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, size_t buflen); int hostapd_parse_csa_settings(const char *pos, struct csa_settings *settings); int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd); +int hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf, + size_t len); +void hostapd_ctrl_iface_pmksa_flush(struct hostapd_data *hapd); #endif /* CTRL_IFACE_AP_H */ diff --git a/contrib/wpa/src/ap/dfs.c b/contrib/wpa/src/ap/dfs.c index 715f19b6ac7b..47adba7ef726 100644 --- a/contrib/wpa/src/ap/dfs.c +++ b/contrib/wpa/src/ap/dfs.c @@ -450,7 +450,7 @@ dfs_get_valid_channel(struct hostapd_iface *iface, return NULL; if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0) - _rand = os_random(); + return NULL; chan_idx = _rand % num_available_chandefs; dfs_find_channel(iface, &chan, chan_idx, skip_radar); @@ -704,7 +704,8 @@ int hostapd_handle_dfs(struct hostapd_iface *iface) skip_radar); if (!channel) { wpa_printf(MSG_ERROR, "could not get valid channel"); - return -1; + hostapd_set_state(iface, HAPD_IFACE_DFS); + return 0; } iface->freq = channel->freq; @@ -793,7 +794,6 @@ static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface) if (!channel) { wpa_printf(MSG_ERROR, "No valid channel available"); - hostapd_setup_interface_complete(iface, err); return err; } @@ -817,16 +817,6 @@ static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface) } -static int hostapd_csa_in_progress(struct hostapd_iface *iface) -{ - unsigned int i; - for (i = 0; i < iface->num_bss; i++) - if (iface->bss[i]->csa_in_progress) - return 1; - return 0; -} - - static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface) { struct hostapd_channel_data *channel; @@ -868,8 +858,9 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface) &vht_oper_centr_freq_seg1_idx, skip_radar); if (!channel) { - /* FIXME: Wait for channel(s) to become available */ - hostapd_disable_iface(iface); + wpa_printf(MSG_INFO, + "%s: no DFS channels left, waiting for NOP to finish", + __func__); return err; } @@ -992,6 +983,11 @@ int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq, /* TODO add correct implementation here */ set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width, cf1, cf2, HOSTAPD_CHAN_DFS_USABLE); + + /* Handle cases where all channels were initially unavailable */ + if (iface->state == HAPD_IFACE_DFS && !iface->cac_started) + hostapd_handle_dfs(iface); + return 0; } diff --git a/contrib/wpa/src/ap/dhcp_snoop.c b/contrib/wpa/src/ap/dhcp_snoop.c index 3a77225f380e..f0212fb2a984 100644 --- a/contrib/wpa/src/ap/dhcp_snoop.c +++ b/contrib/wpa/src/ap/dhcp_snoop.c @@ -121,7 +121,8 @@ static void handle_dhcp(void *ctx, const u8 *src_addr, const u8 *buf, wpa_printf(MSG_DEBUG, "dhcp_snoop: Found DHCPACK for " MACSTR " @ IPv4 address %s/%d", - MAC2STR(sta->addr), ipaddr_str(ntohl(b->your_ip)), + MAC2STR(sta->addr), + ipaddr_str(be_to_host32(b->your_ip)), prefixlen); if (sta->ipaddr == b->your_ip) diff --git a/contrib/wpa/src/ap/drv_callbacks.c b/contrib/wpa/src/ap/drv_callbacks.c index ca8b75c83906..3552b3e0d53b 100644 --- a/contrib/wpa/src/ap/drv_callbacks.c +++ b/contrib/wpa/src/ap/drv_callbacks.c @@ -22,6 +22,7 @@ #include "wnm_ap.h" #include "hostapd.h" #include "ieee802_11.h" +#include "ieee802_11_auth.h" #include "sta_info.h" #include "accounting.h" #include "tkip_countermeasures.h" @@ -33,6 +34,7 @@ #include "hw_features.h" #include "dfs.h" #include "beacon.h" +#include "mbo_ap.h" int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, @@ -114,6 +116,21 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, } sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2); + /* + * ACL configurations to the drivers (implementing AP SME and ACL + * offload) without hostapd's knowledge, can result in a disconnection + * though the driver accepts the connection. Skip the hostapd check for + * ACL if the driver supports ACL offload to avoid potentially + * conflicting ACL rules. + */ + if (hapd->iface->drv_max_acl_mac_addrs == 0 && + hostapd_check_acl(hapd, addr, NULL) != HOSTAPD_ACL_ACCEPT) { + wpa_printf(MSG_INFO, "STA " MACSTR " not allowed to connect", + MAC2STR(addr)); + reason = WLAN_REASON_UNSPECIFIED; + goto fail; + } + #ifdef CONFIG_P2P if (elems.p2p) { wpabuf_free(sta->p2p_ie); @@ -164,6 +181,11 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, sta->mb_ies = NULL; #endif /* CONFIG_FST */ + mbo_ap_check_sta_assoc(hapd, sta, &elems); + + ap_copy_sta_supp_op_classes(sta, elems.supp_op_classes, + elems.supp_op_classes_len); + if (hapd->conf->wpa) { if (ie == NULL || ielen == 0) { #ifdef CONFIG_WPS @@ -338,6 +360,17 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, return WLAN_STATUS_INVALID_IE; #endif /* CONFIG_HS20 */ } + +#ifdef CONFIG_MBO + if (hapd->conf->mbo_enabled && (hapd->conf->wpa & 2) && + elems.mbo && sta->cell_capa && !(sta->flags & WLAN_STA_MFP) && + hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) { + wpa_printf(MSG_INFO, + "MBO: Reject WPA2 association without PMF"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } +#endif /* CONFIG_MBO */ + #ifdef CONFIG_WPS skip_wpa_check: #endif /* CONFIG_WPS */ @@ -447,7 +480,8 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, int offset, int width, int cf1, int cf2) { #ifdef NEED_AP_MLME - int channel, chwidth, seg0_idx = 0, seg1_idx = 0, is_dfs; + int channel, chwidth, is_dfs; + u8 seg0_idx = 0, seg1_idx = 0; hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, @@ -491,8 +525,8 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, seg1_idx = (cf2 - 5000) / 5; break; default: - seg0_idx = hostapd_hw_get_channel(hapd, cf1); - seg1_idx = hostapd_hw_get_channel(hapd, cf2); + ieee80211_freq_to_chan(cf1, &seg0_idx); + ieee80211_freq_to_chan(cf2, &seg1_idx); break; } @@ -539,10 +573,11 @@ void hostapd_event_connect_failed_reason(struct hostapd_data *hapd, #ifdef CONFIG_ACS -static void hostapd_acs_channel_selected(struct hostapd_data *hapd, - struct acs_selected_channels *acs_res) +void hostapd_acs_channel_selected(struct hostapd_data *hapd, + struct acs_selected_channels *acs_res) { int ret, i; + int err = 0; if (hapd->iconf->channel) { wpa_printf(MSG_INFO, "ACS: Channel was already set to %d", @@ -564,7 +599,8 @@ static void hostapd_acs_channel_selected(struct hostapd_data *hapd, hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_WARNING, "driver selected to bad hw_mode"); - return; + err = 1; + goto out; } } @@ -574,7 +610,8 @@ static void hostapd_acs_channel_selected(struct hostapd_data *hapd, hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_WARNING, "driver switched to bad channel"); - return; + err = 1; + goto out; } hapd->iconf->channel = acs_res->pri_channel; @@ -588,7 +625,8 @@ static void hostapd_acs_channel_selected(struct hostapd_data *hapd, hapd->iconf->secondary_channel = 1; else { wpa_printf(MSG_ERROR, "Invalid secondary channel!"); - return; + err = 1; + goto out; } if (hapd->iface->conf->ieee80211ac) { @@ -617,7 +655,8 @@ static void hostapd_acs_channel_selected(struct hostapd_data *hapd, } } - ret = hostapd_acs_completed(hapd->iface, 0); +out: + ret = hostapd_acs_completed(hapd->iface, err); if (ret) { wpa_printf(MSG_ERROR, "ACS: Possibly channel configuration is invalid"); @@ -884,11 +923,24 @@ static void hostapd_mgmt_tx_cb(struct hostapd_data *hapd, const u8 *buf, size_t len, u16 stype, int ok) { struct ieee80211_hdr *hdr; + struct hostapd_data *orig_hapd = hapd; hdr = (struct ieee80211_hdr *) buf; hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len)); - if (hapd == NULL || hapd == HAPD_BROADCAST) + if (!hapd) return; + if (hapd == HAPD_BROADCAST) { + if (stype != WLAN_FC_STYPE_ACTION || len <= 25 || + buf[24] != WLAN_ACTION_PUBLIC) + return; + hapd = get_hapd_bssid(orig_hapd->iface, hdr->addr2); + if (!hapd || hapd == HAPD_BROADCAST) + return; + /* + * Allow processing of TX status for a Public Action frame that + * used wildcard BBSID. + */ + } ieee802_11_mgmt_cb(hapd, buf, len, stype, ok); } @@ -935,6 +987,8 @@ static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src, ieee802_1x_receive(hapd, src, data, data_len); } +#endif /* HOSTAPD */ + static struct hostapd_channel_data * hostapd_get_mode_channel( struct hostapd_iface *iface, unsigned int freq) @@ -944,8 +998,6 @@ static struct hostapd_channel_data * hostapd_get_mode_channel( for (i = 0; i < iface->current_mode->num_channels; i++) { chan = &iface->current_mode->channels[i]; - if (!chan) - return NULL; if ((unsigned int) chan->freq == freq) return chan; } @@ -1009,10 +1061,9 @@ static void hostapd_single_channel_get_survey(struct hostapd_iface *iface, } -static void hostapd_event_get_survey(struct hostapd_data *hapd, - struct survey_results *survey_results) +void hostapd_event_get_survey(struct hostapd_iface *iface, + struct survey_results *survey_results) { - struct hostapd_iface *iface = hapd->iface; struct freq_survey *survey, *tmp; struct hostapd_channel_data *chan; @@ -1044,6 +1095,7 @@ static void hostapd_event_get_survey(struct hostapd_data *hapd, } +#ifdef HOSTAPD #ifdef NEED_AP_MLME static void hostapd_event_iface_unavailable(struct hostapd_data *hapd) @@ -1251,7 +1303,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, data->connect_failed_reason.code); break; case EVENT_SURVEY: - hostapd_event_get_survey(hapd, &data->survey_results); + hostapd_event_get_survey(hapd->iface, &data->survey_results); break; #ifdef NEED_AP_MLME case EVENT_INTERFACE_UNAVAILABLE: @@ -1321,4 +1373,31 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, } } + +void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event, + union wpa_event_data *data) +{ + struct hapd_interfaces *interfaces = ctx; + struct hostapd_data *hapd; + + if (event != EVENT_INTERFACE_STATUS) + return; + + hapd = hostapd_get_iface(interfaces, data->interface_status.ifname); + if (hapd && hapd->driver && hapd->driver->get_ifindex && + hapd->drv_priv) { + unsigned int ifindex; + + ifindex = hapd->driver->get_ifindex(hapd->drv_priv); + if (ifindex != data->interface_status.ifindex) { + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, + "interface status ifindex %d mismatch (%d)", + ifindex, data->interface_status.ifindex); + return; + } + } + if (hapd) + wpa_supplicant_event(hapd, event, data); +} + #endif /* HOSTAPD */ diff --git a/contrib/wpa/src/ap/gas_serv.c b/contrib/wpa/src/ap/gas_serv.c index 9d19f98d0b7c..6ce178de3b29 100644 --- a/contrib/wpa/src/ap/gas_serv.c +++ b/contrib/wpa/src/ap/gas_serv.c @@ -101,6 +101,7 @@ gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr, if (sta->gas_dialog[i].dialog_token != dialog_token || !sta->gas_dialog[i].valid) continue; + ap_sta_replenish_timeout(hapd, sta, 5); return &sta->gas_dialog[i]; } wpa_printf(MSG_DEBUG, "ANQP: Could not find dialog for " @@ -167,27 +168,107 @@ static void anqp_add_hs_capab_list(struct hostapd_data *hapd, #endif /* CONFIG_HS20 */ +static struct anqp_element * get_anqp_elem(struct hostapd_data *hapd, + u16 infoid) +{ + struct anqp_element *elem; + + dl_list_for_each(elem, &hapd->conf->anqp_elem, struct anqp_element, + list) { + if (elem->infoid == infoid) + return elem; + } + + return NULL; +} + + +static void anqp_add_elem(struct hostapd_data *hapd, struct wpabuf *buf, + u16 infoid) +{ + struct anqp_element *elem; + + elem = get_anqp_elem(hapd, infoid); + if (!elem) + return; + if (wpabuf_tailroom(buf) < 2 + 2 + wpabuf_len(elem->payload)) { + wpa_printf(MSG_DEBUG, "ANQP: No room for InfoID %u payload", + infoid); + return; + } + + wpabuf_put_le16(buf, infoid); + wpabuf_put_le16(buf, wpabuf_len(elem->payload)); + wpabuf_put_buf(buf, elem->payload); +} + + +static int anqp_add_override(struct hostapd_data *hapd, struct wpabuf *buf, + u16 infoid) +{ + if (get_anqp_elem(hapd, infoid)) { + anqp_add_elem(hapd, buf, infoid); + return 1; + } + + return 0; +} + + static void anqp_add_capab_list(struct hostapd_data *hapd, struct wpabuf *buf) { u8 *len; + u16 id; + + if (anqp_add_override(hapd, buf, ANQP_CAPABILITY_LIST)) + return; len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST); wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST); - if (hapd->conf->venue_name) + if (hapd->conf->venue_name || get_anqp_elem(hapd, ANQP_VENUE_NAME)) wpabuf_put_le16(buf, ANQP_VENUE_NAME); - if (hapd->conf->network_auth_type) + if (get_anqp_elem(hapd, ANQP_EMERGENCY_CALL_NUMBER)) + wpabuf_put_le16(buf, ANQP_EMERGENCY_CALL_NUMBER); + if (hapd->conf->network_auth_type || + get_anqp_elem(hapd, ANQP_NETWORK_AUTH_TYPE)) wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE); - if (hapd->conf->roaming_consortium) + if (hapd->conf->roaming_consortium || + get_anqp_elem(hapd, ANQP_ROAMING_CONSORTIUM)) wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM); - if (hapd->conf->ipaddr_type_configured) + if (hapd->conf->ipaddr_type_configured || + get_anqp_elem(hapd, ANQP_IP_ADDR_TYPE_AVAILABILITY)) wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY); - if (hapd->conf->nai_realm_data) + if (hapd->conf->nai_realm_data || + get_anqp_elem(hapd, ANQP_NAI_REALM)) wpabuf_put_le16(buf, ANQP_NAI_REALM); - if (hapd->conf->anqp_3gpp_cell_net) + if (hapd->conf->anqp_3gpp_cell_net || + get_anqp_elem(hapd, ANQP_3GPP_CELLULAR_NETWORK)) wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK); - if (hapd->conf->domain_name) + if (get_anqp_elem(hapd, ANQP_AP_GEOSPATIAL_LOCATION)) + wpabuf_put_le16(buf, ANQP_AP_GEOSPATIAL_LOCATION); + if (get_anqp_elem(hapd, ANQP_AP_CIVIC_LOCATION)) + wpabuf_put_le16(buf, ANQP_AP_CIVIC_LOCATION); + if (get_anqp_elem(hapd, ANQP_AP_LOCATION_PUBLIC_URI)) + wpabuf_put_le16(buf, ANQP_AP_LOCATION_PUBLIC_URI); + if (hapd->conf->domain_name || get_anqp_elem(hapd, ANQP_DOMAIN_NAME)) wpabuf_put_le16(buf, ANQP_DOMAIN_NAME); + if (get_anqp_elem(hapd, ANQP_EMERGENCY_ALERT_URI)) + wpabuf_put_le16(buf, ANQP_EMERGENCY_ALERT_URI); + if (get_anqp_elem(hapd, ANQP_EMERGENCY_NAI)) + wpabuf_put_le16(buf, ANQP_EMERGENCY_NAI); + if (get_anqp_elem(hapd, ANQP_NEIGHBOR_REPORT)) + wpabuf_put_le16(buf, ANQP_NEIGHBOR_REPORT); + for (id = 273; id < 277; id++) { + if (get_anqp_elem(hapd, id)) + wpabuf_put_le16(buf, id); + } + if (get_anqp_elem(hapd, ANQP_VENUE_URL)) + wpabuf_put_le16(buf, ANQP_VENUE_URL); + if (get_anqp_elem(hapd, ANQP_ADVICE_OF_CHARGE)) + wpabuf_put_le16(buf, ANQP_ADVICE_OF_CHARGE); + if (get_anqp_elem(hapd, ANQP_LOCAL_CONTENT)) + wpabuf_put_le16(buf, ANQP_LOCAL_CONTENT); #ifdef CONFIG_HS20 anqp_add_hs_capab_list(hapd, buf); #endif /* CONFIG_HS20 */ @@ -197,6 +278,9 @@ static void anqp_add_capab_list(struct hostapd_data *hapd, static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf) { + if (anqp_add_override(hapd, buf, ANQP_VENUE_NAME)) + return; + if (hapd->conf->venue_name) { u8 *len; unsigned int i; @@ -218,6 +302,9 @@ static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf) static void anqp_add_network_auth_type(struct hostapd_data *hapd, struct wpabuf *buf) { + if (anqp_add_override(hapd, buf, ANQP_NETWORK_AUTH_TYPE)) + return; + if (hapd->conf->network_auth_type) { wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE); wpabuf_put_le16(buf, hapd->conf->network_auth_type_len); @@ -233,6 +320,9 @@ static void anqp_add_roaming_consortium(struct hostapd_data *hapd, unsigned int i; u8 *len; + if (anqp_add_override(hapd, buf, ANQP_ROAMING_CONSORTIUM)) + return; + len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM); for (i = 0; i < hapd->conf->roaming_consortium_count; i++) { struct hostapd_roaming_consortium *rc; @@ -247,6 +337,9 @@ static void anqp_add_roaming_consortium(struct hostapd_data *hapd, static void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd, struct wpabuf *buf) { + if (anqp_add_override(hapd, buf, ANQP_IP_ADDR_TYPE_AVAILABILITY)) + return; + if (hapd->conf->ipaddr_type_configured) { wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY); wpabuf_put_le16(buf, 1); @@ -309,7 +402,7 @@ static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd, pos = home_realm; end = pos + home_realm_len; - if (pos + 1 > end) { + if (end - pos < 1) { wpa_hexdump(MSG_DEBUG, "Too short NAI Home Realm Query", home_realm, home_realm_len); return -1; @@ -317,7 +410,7 @@ static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd, num_realms = *pos++; for (i = 0; i < num_realms && num_matching < 10; i++) { - if (pos + 2 > end) { + if (end - pos < 2) { wpa_hexdump(MSG_DEBUG, "Truncated NAI Home Realm Query", home_realm, home_realm_len); @@ -325,7 +418,7 @@ static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd, } encoding = *pos++; realm_len = *pos++; - if (pos + realm_len > end) { + if (realm_len > end - pos) { wpa_hexdump(MSG_DEBUG, "Truncated NAI Home Realm Query", home_realm, home_realm_len); @@ -391,6 +484,10 @@ static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf, const u8 *home_realm, size_t home_realm_len, int nai_realm, int nai_home_realm) { + if (nai_realm && !nai_home_realm && + anqp_add_override(hapd, buf, ANQP_NAI_REALM)) + return; + if (nai_realm && hapd->conf->nai_realm_data) { u8 *len; unsigned int i, j; @@ -424,6 +521,9 @@ static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf, static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd, struct wpabuf *buf) { + if (anqp_add_override(hapd, buf, ANQP_3GPP_CELLULAR_NETWORK)) + return; + if (hapd->conf->anqp_3gpp_cell_net) { wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK); wpabuf_put_le16(buf, @@ -436,6 +536,9 @@ static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd, static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf) { + if (anqp_add_override(hapd, buf, ANQP_DOMAIN_NAME)) + return; + if (hapd->conf->domain_name) { wpabuf_put_le16(buf, ANQP_DOMAIN_NAME); wpabuf_put_le16(buf, hapd->conf->domain_name_len); @@ -683,20 +786,42 @@ static void anqp_add_icon_binary_file(struct hostapd_data *hapd, #endif /* CONFIG_HS20 */ +static size_t anqp_get_required_len(struct hostapd_data *hapd, + const u16 *infoid, + unsigned int num_infoid) +{ + size_t len = 0; + unsigned int i; + + for (i = 0; i < num_infoid; i++) { + struct anqp_element *elem = get_anqp_elem(hapd, infoid[i]); + + if (elem) + len += 2 + 2 + wpabuf_len(elem->payload); + } + + return len; +} + + static struct wpabuf * gas_serv_build_gas_resp_payload(struct hostapd_data *hapd, unsigned int request, const u8 *home_realm, size_t home_realm_len, - const u8 *icon_name, size_t icon_name_len) + const u8 *icon_name, size_t icon_name_len, + const u16 *extra_req, + unsigned int num_extra_req) { struct wpabuf *buf; size_t len; + unsigned int i; len = 1400; if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM)) len += 1000; if (request & ANQP_REQ_ICON_REQUEST) len += 65536; + len += anqp_get_required_len(hapd, extra_req, num_extra_req); buf = wpabuf_alloc(len); if (buf == NULL) @@ -706,6 +831,8 @@ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd, anqp_add_capab_list(hapd, buf); if (request & ANQP_REQ_VENUE_NAME) anqp_add_venue_name(hapd, buf); + if (request & ANQP_REQ_EMERGENCY_CALL_NUMBER) + anqp_add_elem(hapd, buf, ANQP_EMERGENCY_CALL_NUMBER); if (request & ANQP_REQ_NETWORK_AUTH_TYPE) anqp_add_network_auth_type(hapd, buf); if (request & ANQP_REQ_ROAMING_CONSORTIUM) @@ -718,8 +845,23 @@ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd, request & ANQP_REQ_NAI_HOME_REALM); if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK) anqp_add_3gpp_cellular_network(hapd, buf); + if (request & ANQP_REQ_AP_GEOSPATIAL_LOCATION) + anqp_add_elem(hapd, buf, ANQP_AP_GEOSPATIAL_LOCATION); + if (request & ANQP_REQ_AP_CIVIC_LOCATION) + anqp_add_elem(hapd, buf, ANQP_AP_CIVIC_LOCATION); + if (request & ANQP_REQ_AP_LOCATION_PUBLIC_URI) + anqp_add_elem(hapd, buf, ANQP_AP_LOCATION_PUBLIC_URI); if (request & ANQP_REQ_DOMAIN_NAME) anqp_add_domain_name(hapd, buf); + if (request & ANQP_REQ_EMERGENCY_ALERT_URI) + anqp_add_elem(hapd, buf, ANQP_EMERGENCY_ALERT_URI); + if (request & ANQP_REQ_TDLS_CAPABILITY) + anqp_add_elem(hapd, buf, ANQP_TDLS_CAPABILITY); + if (request & ANQP_REQ_EMERGENCY_NAI) + anqp_add_elem(hapd, buf, ANQP_EMERGENCY_NAI); + + for (i = 0; i < num_extra_req; i++) + anqp_add_elem(hapd, buf, extra_req[i]); #ifdef CONFIG_HS20 if (request & ANQP_REQ_HS_CAPABILITY_LIST) @@ -742,6 +884,8 @@ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd, } +#define ANQP_MAX_EXTRA_REQ 20 + struct anqp_query_info { unsigned int request; const u8 *home_realm_query; @@ -749,6 +893,8 @@ struct anqp_query_info { const u8 *icon_name; size_t icon_name_len; int p2p_sd; + u16 extra_req[ANQP_MAX_EXTRA_REQ]; + unsigned int num_extra_req; }; @@ -776,6 +922,11 @@ static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id, set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name", hapd->conf->venue_name != NULL, qi); break; + case ANQP_EMERGENCY_CALL_NUMBER: + set_anqp_req(ANQP_REQ_EMERGENCY_CALL_NUMBER, + "Emergency Call Number", + get_anqp_elem(hapd, info_id) != NULL, qi); + break; case ANQP_NETWORK_AUTH_TYPE: set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type", hapd->conf->network_auth_type != NULL, qi); @@ -798,13 +949,55 @@ static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id, "3GPP Cellular Network", hapd->conf->anqp_3gpp_cell_net != NULL, qi); break; + case ANQP_AP_GEOSPATIAL_LOCATION: + set_anqp_req(ANQP_REQ_AP_GEOSPATIAL_LOCATION, + "AP Geospatial Location", + get_anqp_elem(hapd, info_id) != NULL, qi); + break; + case ANQP_AP_CIVIC_LOCATION: + set_anqp_req(ANQP_REQ_AP_CIVIC_LOCATION, + "AP Civic Location", + get_anqp_elem(hapd, info_id) != NULL, qi); + break; + case ANQP_AP_LOCATION_PUBLIC_URI: + set_anqp_req(ANQP_REQ_AP_LOCATION_PUBLIC_URI, + "AP Location Public URI", + get_anqp_elem(hapd, info_id) != NULL, qi); + break; case ANQP_DOMAIN_NAME: set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name", hapd->conf->domain_name != NULL, qi); break; + case ANQP_EMERGENCY_ALERT_URI: + set_anqp_req(ANQP_REQ_EMERGENCY_ALERT_URI, + "Emergency Alert URI", + get_anqp_elem(hapd, info_id) != NULL, qi); + break; + case ANQP_TDLS_CAPABILITY: + set_anqp_req(ANQP_REQ_TDLS_CAPABILITY, + "TDLS Capability", + get_anqp_elem(hapd, info_id) != NULL, qi); + break; + case ANQP_EMERGENCY_NAI: + set_anqp_req(ANQP_REQ_EMERGENCY_NAI, + "Emergency NAI", + get_anqp_elem(hapd, info_id) != NULL, qi); + break; default: - wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u", - info_id); + if (!get_anqp_elem(hapd, info_id)) { + wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u", + info_id); + break; + } + if (qi->num_extra_req == ANQP_MAX_EXTRA_REQ) { + wpa_printf(MSG_DEBUG, + "ANQP: No more room for extra requests - ignore Info Id %u", + info_id); + break; + } + wpa_printf(MSG_DEBUG, "ANQP: Info Id %u (local)", info_id); + qi->extra_req[qi->num_extra_req] = info_id; + qi->num_extra_req++; break; } } @@ -817,7 +1010,7 @@ static void rx_anqp_query_list(struct hostapd_data *hapd, wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list", (unsigned int) (end - pos) / 2); - while (pos + 2 <= end) { + while (end - pos >= 2) { rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi); pos += 2; } @@ -906,7 +1099,7 @@ static void rx_anqp_vendor_specific(struct hostapd_data *hapd, u32 oui; u8 subtype; - if (pos + 4 > end) { + if (end - pos < 4) { wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP " "Query element"); return; @@ -942,7 +1135,7 @@ static void rx_anqp_vendor_specific(struct hostapd_data *hapd, } pos++; - if (pos + 1 >= end) + if (end - pos <= 1) return; subtype = *pos++; @@ -973,14 +1166,16 @@ static void rx_anqp_vendor_specific(struct hostapd_data *hapd, static void gas_serv_req_local_processing(struct hostapd_data *hapd, const u8 *sa, u8 dialog_token, - struct anqp_query_info *qi, int prot) + struct anqp_query_info *qi, int prot, + int std_addr3) { struct wpabuf *buf, *tx_buf; buf = gas_serv_build_gas_resp_payload(hapd, qi->request, qi->home_realm_query, qi->home_realm_query_len, - qi->icon_name, qi->icon_name_len); + qi->icon_name, qi->icon_name_len, + qi->extra_req, qi->num_extra_req); wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses", buf); if (!buf) @@ -1033,15 +1228,22 @@ static void gas_serv_req_local_processing(struct hostapd_data *hapd, return; if (prot) convert_to_protected_dual(tx_buf); - hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, - wpabuf_head(tx_buf), wpabuf_len(tx_buf)); + if (std_addr3) + hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, + wpabuf_head(tx_buf), + wpabuf_len(tx_buf)); + else + hostapd_drv_send_action_addr3_ap(hapd, hapd->iface->freq, 0, sa, + wpabuf_head(tx_buf), + wpabuf_len(tx_buf)); wpabuf_free(tx_buf); } static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, const u8 *sa, - const u8 *data, size_t len, int prot) + const u8 *data, size_t len, int prot, + int std_addr3) { const u8 *pos = data; const u8 *end = data + len; @@ -1069,12 +1271,12 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, adv_proto = pos++; slen = *pos++; - next = pos + slen; - if (next > end || slen < 2) { + if (slen > end - pos || slen < 2) { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Invalid IE in GAS Initial Request"); return; } + next = pos + slen; pos++; /* skip QueryRespLenLimit and PAME-BI */ if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { @@ -1093,19 +1295,26 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, wpabuf_put_le16(buf, 0); /* Query Response Length */ if (prot) convert_to_protected_dual(buf); - hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, - wpabuf_head(buf), wpabuf_len(buf)); + if (std_addr3) + hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, + wpabuf_head(buf), + wpabuf_len(buf)); + else + hostapd_drv_send_action_addr3_ap(hapd, + hapd->iface->freq, 0, + sa, wpabuf_head(buf), + wpabuf_len(buf)); wpabuf_free(buf); return; } pos = next; /* Query Request */ - if (pos + 2 > end) + if (end - pos < 2) return; slen = WPA_GET_LE16(pos); pos += 2; - if (pos + slen > end) + if (slen > end - pos) return; end = pos + slen; @@ -1113,7 +1322,7 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, while (pos < end) { u16 info_id, elen; - if (pos + 4 > end) + if (end - pos < 4) return; info_id = WPA_GET_LE16(pos); @@ -1121,7 +1330,7 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, elen = WPA_GET_LE16(pos); pos += 2; - if (pos + elen > end) { + if (elen > end - pos) { wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request"); return; } @@ -1144,13 +1353,15 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, pos += elen; } - gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot); + gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot, + std_addr3); } static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd, const u8 *sa, - const u8 *data, size_t len, int prot) + const u8 *data, size_t len, int prot, + int std_addr3) { struct gas_dialog_info *dialog; struct wpabuf *buf, *tx_buf; @@ -1226,8 +1437,14 @@ static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd, send_resp: if (prot) convert_to_protected_dual(tx_buf); - hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, - wpabuf_head(tx_buf), wpabuf_len(tx_buf)); + if (std_addr3) + hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, + wpabuf_head(tx_buf), + wpabuf_len(tx_buf)); + else + hostapd_drv_send_action_addr3_ap(hapd, hapd->iface->freq, 0, sa, + wpabuf_head(tx_buf), + wpabuf_len(tx_buf)); wpabuf_free(tx_buf); } @@ -1238,7 +1455,7 @@ static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len, struct hostapd_data *hapd = ctx; const struct ieee80211_mgmt *mgmt; const u8 *sa, *data; - int prot; + int prot, std_addr3; mgmt = (const struct ieee80211_mgmt *) buf; if (len < IEEE80211_HDRLEN + 2) @@ -1253,14 +1470,22 @@ static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len, */ prot = mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL; sa = mgmt->sa; + if (hapd->conf->gas_address3 == 1) + std_addr3 = 1; + else if (hapd->conf->gas_address3 == 2) + std_addr3 = 0; + else + std_addr3 = is_broadcast_ether_addr(mgmt->bssid); len -= IEEE80211_HDRLEN + 1; data = buf + IEEE80211_HDRLEN + 1; switch (data[0]) { case WLAN_PA_GAS_INITIAL_REQ: - gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot); + gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot, + std_addr3); break; case WLAN_PA_GAS_COMEBACK_REQ: - gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot); + gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot, + std_addr3); break; } } diff --git a/contrib/wpa/src/ap/gas_serv.h b/contrib/wpa/src/ap/gas_serv.h index 4ec3201967c0..9051e4f90513 100644 --- a/contrib/wpa/src/ap/gas_serv.h +++ b/contrib/wpa/src/ap/gas_serv.h @@ -9,10 +9,13 @@ #ifndef GAS_SERV_H #define GAS_SERV_H +/* First 16 ANQP InfoIDs can be included in the optimized bitmap */ #define ANQP_REQ_CAPABILITY_LIST \ (1 << (ANQP_CAPABILITY_LIST - ANQP_QUERY_LIST)) #define ANQP_REQ_VENUE_NAME \ (1 << (ANQP_VENUE_NAME - ANQP_QUERY_LIST)) +#define ANQP_REQ_EMERGENCY_CALL_NUMBER \ + (1 << (ANQP_EMERGENCY_CALL_NUMBER - ANQP_QUERY_LIST)) #define ANQP_REQ_NETWORK_AUTH_TYPE \ (1 << (ANQP_NETWORK_AUTH_TYPE - ANQP_QUERY_LIST)) #define ANQP_REQ_ROAMING_CONSORTIUM \ @@ -23,8 +26,24 @@ (1 << (ANQP_NAI_REALM - ANQP_QUERY_LIST)) #define ANQP_REQ_3GPP_CELLULAR_NETWORK \ (1 << (ANQP_3GPP_CELLULAR_NETWORK - ANQP_QUERY_LIST)) +#define ANQP_REQ_AP_GEOSPATIAL_LOCATION \ + (1 << (ANQP_AP_GEOSPATIAL_LOCATION - ANQP_QUERY_LIST)) +#define ANQP_REQ_AP_CIVIC_LOCATION \ + (1 << (ANQP_AP_CIVIC_LOCATION - ANQP_QUERY_LIST)) +#define ANQP_REQ_AP_LOCATION_PUBLIC_URI \ + (1 << (ANQP_AP_LOCATION_PUBLIC_URI - ANQP_QUERY_LIST)) #define ANQP_REQ_DOMAIN_NAME \ (1 << (ANQP_DOMAIN_NAME - ANQP_QUERY_LIST)) +#define ANQP_REQ_EMERGENCY_ALERT_URI \ + (1 << (ANQP_EMERGENCY_ALERT_URI - ANQP_QUERY_LIST)) +#define ANQP_REQ_TDLS_CAPABILITY \ + (1 << (ANQP_TDLS_CAPABILITY - ANQP_QUERY_LIST)) +#define ANQP_REQ_EMERGENCY_NAI \ + (1 << (ANQP_EMERGENCY_NAI - ANQP_QUERY_LIST)) +/* + * First 16 Hotspot 2.0 vendor specific ANQP-elements can be included in the + * optimized bitmap. + */ #define ANQP_REQ_HS_CAPABILITY_LIST \ (0x10000 << HS20_STYPE_CAPABILITY_LIST) #define ANQP_REQ_OPERATOR_FRIENDLY_NAME \ diff --git a/contrib/wpa/src/ap/hostapd.c b/contrib/wpa/src/ap/hostapd.c index c09c17a44696..9fafc7f457bb 100644 --- a/contrib/wpa/src/ap/hostapd.c +++ b/contrib/wpa/src/ap/hostapd.c @@ -12,6 +12,7 @@ #include "utils/eloop.h" #include "common/ieee802_11_defs.h" #include "common/wpa_ctrl.h" +#include "common/hw_features_common.h" #include "radius/radius_client.h" #include "radius/radius_das.h" #include "eap_server/tncs.h" @@ -42,6 +43,8 @@ #include "x_snoop.h" #include "dhcp_snoop.h" #include "ndisc_snoop.h" +#include "neighbor_db.h" +#include "rrm.h" static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason); @@ -203,10 +206,12 @@ int hostapd_reload_config(struct hostapd_iface *iface) static void hostapd_broadcast_key_clear_iface(struct hostapd_data *hapd, - char *ifname) + const char *ifname) { int i; + if (!ifname) + return; for (i = 0; i < NUM_WEP_KEYS; i++) { if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE, NULL, i, 0, NULL, 0, NULL, 0)) { @@ -334,6 +339,8 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd) wpabuf_free(hapd->mesh_pending_auth); hapd->mesh_pending_auth = NULL; #endif /* CONFIG_MESH */ + + hostapd_clean_rrm(hapd); } @@ -367,7 +374,7 @@ static void sta_track_deinit(struct hostapd_iface *iface) list))) { dl_list_del(&info->list); iface->num_sta_seen--; - os_free(info); + sta_track_del(info); } } @@ -511,6 +518,9 @@ static int hostapd_validate_bssid_configuration(struct hostapd_iface *iface) if (hostapd_drv_none(hapd)) return 0; + if (iface->conf->use_driver_iface_addr) + return 0; + /* Generate BSSID mask that is large enough to cover the BSSIDs. */ /* Determine the bits necessary to cover the number of BSSIDs. */ @@ -520,7 +530,7 @@ static int hostapd_validate_bssid_configuration(struct hostapd_iface *iface) /* Determine the bits necessary to any configured BSSIDs, if they are higher than the number of BSSIDs. */ for (j = 0; j < iface->conf->num_bss; j++) { - if (hostapd_mac_comp_empty(iface->conf->bss[j]->bssid) == 0) { + if (is_zero_ether_addr(iface->conf->bss[j]->bssid)) { if (j) auto_addr++; continue; @@ -672,7 +682,7 @@ static struct sta_info * hostapd_das_find_sta(struct hostapd_data *hapd, if (attr->acct_session_id) { num_attr++; - if (attr->acct_session_id_len != 17) { + if (attr->acct_session_id_len != 16) { wpa_printf(MSG_DEBUG, "RADIUS DAS: Acct-Session-Id cannot match"); return NULL; @@ -682,10 +692,9 @@ static struct sta_info * hostapd_das_find_sta(struct hostapd_data *hapd, for (sta = hapd->sta_list; sta; sta = sta->next) { if (!sta->radius_das_match) continue; - os_snprintf(buf, sizeof(buf), "%08X-%08X", - sta->acct_session_id_hi, - sta->acct_session_id_lo); - if (os_memcmp(attr->acct_session_id, buf, 17) != 0) + os_snprintf(buf, sizeof(buf), "%016llX", + (unsigned long long) sta->acct_session_id); + if (os_memcmp(attr->acct_session_id, buf, 16) != 0) sta->radius_das_match = 0; else count++; @@ -701,7 +710,7 @@ static struct sta_info * hostapd_das_find_sta(struct hostapd_data *hapd, if (attr->acct_multi_session_id) { num_attr++; - if (attr->acct_multi_session_id_len != 17) { + if (attr->acct_multi_session_id_len != 16) { wpa_printf(MSG_DEBUG, "RADIUS DAS: Acct-Multi-Session-Id cannot match"); return NULL; @@ -712,14 +721,14 @@ static struct sta_info * hostapd_das_find_sta(struct hostapd_data *hapd, if (!sta->radius_das_match) continue; if (!sta->eapol_sm || - !sta->eapol_sm->acct_multi_session_id_hi) { + !sta->eapol_sm->acct_multi_session_id) { sta->radius_das_match = 0; continue; } - os_snprintf(buf, sizeof(buf), "%08X+%08X", - sta->eapol_sm->acct_multi_session_id_hi, - sta->eapol_sm->acct_multi_session_id_lo); - if (os_memcmp(attr->acct_multi_session_id, buf, 17) != + os_snprintf(buf, sizeof(buf), "%016llX", + (unsigned long long) + sta->eapol_sm->acct_multi_session_id); + if (os_memcmp(attr->acct_multi_session_id, buf, 16) != 0) sta->radius_das_match = 0; else @@ -905,12 +914,9 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) hapd->started = 1; if (!first || first == -1) { - if (hostapd_mac_comp_empty(conf->bssid) == 0) { - /* Allocate the next available BSSID. */ - do { - inc_byte_array(hapd->own_addr, ETH_ALEN); - } while (mac_in_conf(hapd->iconf, hapd->own_addr)); - } else { + u8 *addr = hapd->own_addr; + + if (!is_zero_ether_addr(conf->bssid)) { /* Allocate the configured BSSID. */ os_memcpy(hapd->own_addr, conf->bssid, ETH_ALEN); @@ -922,11 +928,18 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) "the radio", conf->iface); return -1; } + } else if (hapd->iconf->use_driver_iface_addr) { + addr = NULL; + } else { + /* Allocate the next available BSSID. */ + do { + inc_byte_array(hapd->own_addr, ETH_ALEN); + } while (mac_in_conf(hapd->iconf, hapd->own_addr)); } hapd->interface_added = 1; if (hostapd_if_add(hapd->iface->bss[0], WPA_IF_AP_BSS, - conf->iface, hapd->own_addr, hapd, + conf->iface, addr, hapd, &hapd->drv_priv, force_ifname, if_addr, conf->bridge[0] ? conf->bridge : NULL, first == -1)) { @@ -935,11 +948,19 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) hapd->interface_added = 0; return -1; } + + if (!addr) + os_memcpy(hapd->own_addr, if_addr, ETH_ALEN); } if (conf->wmm_enabled < 0) conf->wmm_enabled = hapd->iconf->ieee80211n; +#ifdef CONFIG_IEEE80211R + if (is_zero_ether_addr(conf->r1_key_holder)) + os_memcpy(conf->r1_key_holder, hapd->own_addr, ETH_ALEN); +#endif /* CONFIG_IEEE80211R */ + #ifdef CONFIG_MESH if (hapd->iface->mconf == NULL) flush_old_stations = 0; @@ -1022,6 +1043,8 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) das_conf.time_window = conf->radius_das_time_window; das_conf.require_event_timestamp = conf->radius_das_require_event_timestamp; + das_conf.require_message_authenticator = + conf->radius_das_require_message_authenticator; das_conf.ctx = hapd; das_conf.disconnect = hostapd_das_disconnect; hapd->radius_das = radius_das_init(&das_conf); @@ -1509,15 +1532,128 @@ void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd, #endif /* CONFIG_FST */ -/** - * hostapd_setup_interface_complete - Complete interface setup - * - * This function is called when previous steps in the interface setup has been - * completed. This can also start operations, e.g., DFS, that will require - * additional processing before interface is ready to be enabled. Such - * operations will call this function from eloop callbacks when finished. - */ -int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err) +#ifdef NEED_AP_MLME +static enum nr_chan_width hostapd_get_nr_chan_width(struct hostapd_data *hapd, + int ht, int vht) +{ + if (!ht && !vht) + return NR_CHAN_WIDTH_20; + if (!hapd->iconf->secondary_channel) + return NR_CHAN_WIDTH_20; + if (!vht || hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_USE_HT) + return NR_CHAN_WIDTH_40; + if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_80MHZ) + return NR_CHAN_WIDTH_80; + if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_160MHZ) + return NR_CHAN_WIDTH_160; + if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_80P80MHZ) + return NR_CHAN_WIDTH_80P80; + return NR_CHAN_WIDTH_20; +} +#endif /* NEED_AP_MLME */ + + +static void hostapd_set_own_neighbor_report(struct hostapd_data *hapd) +{ +#ifdef NEED_AP_MLME + u16 capab = hostapd_own_capab_info(hapd); + int ht = hapd->iconf->ieee80211n && !hapd->conf->disable_11n; + int vht = hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac; + struct wpa_ssid_value ssid; + u8 channel, op_class; + int center_freq1 = 0, center_freq2 = 0; + enum nr_chan_width width; + u32 bssid_info; + struct wpabuf *nr; + + if (!(hapd->conf->radio_measurements[0] & + WLAN_RRM_CAPS_NEIGHBOR_REPORT)) + return; + + bssid_info = 3; /* AP is reachable */ + bssid_info |= NEI_REP_BSSID_INFO_SECURITY; /* "same as the AP" */ + bssid_info |= NEI_REP_BSSID_INFO_KEY_SCOPE; /* "same as the AP" */ + + if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT) + bssid_info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT; + + bssid_info |= NEI_REP_BSSID_INFO_RM; /* RRM is supported */ + + if (hapd->conf->wmm_enabled) { + bssid_info |= NEI_REP_BSSID_INFO_QOS; + + if (hapd->conf->wmm_uapsd && + (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD)) + bssid_info |= NEI_REP_BSSID_INFO_APSD; + } + + if (ht) { + bssid_info |= NEI_REP_BSSID_INFO_HT | + NEI_REP_BSSID_INFO_DELAYED_BA; + + /* VHT bit added in IEEE P802.11-REVmc/D4.3 */ + if (vht) + bssid_info |= NEI_REP_BSSID_INFO_VHT; + } + + /* TODO: Set NEI_REP_BSSID_INFO_MOBILITY_DOMAIN if MDE is set */ + + ieee80211_freq_to_channel_ext(hapd->iface->freq, + hapd->iconf->secondary_channel, + hapd->iconf->vht_oper_chwidth, + &op_class, &channel); + width = hostapd_get_nr_chan_width(hapd, ht, vht); + if (vht) { + center_freq1 = ieee80211_chan_to_freq( + NULL, op_class, + hapd->iconf->vht_oper_centr_freq_seg0_idx); + if (width == NR_CHAN_WIDTH_80P80) + center_freq2 = ieee80211_chan_to_freq( + NULL, op_class, + hapd->iconf->vht_oper_centr_freq_seg1_idx); + } else if (ht) { + center_freq1 = hapd->iface->freq + + 10 * hapd->iconf->secondary_channel; + } + + ssid.ssid_len = hapd->conf->ssid.ssid_len; + os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len); + + /* + * Neighbor Report element size = BSSID + BSSID info + op_class + chan + + * phy type + wide bandwidth channel subelement. + */ + nr = wpabuf_alloc(ETH_ALEN + 4 + 1 + 1 + 1 + 5); + if (!nr) + return; + + wpabuf_put_data(nr, hapd->own_addr, ETH_ALEN); + wpabuf_put_le32(nr, bssid_info); + wpabuf_put_u8(nr, op_class); + wpabuf_put_u8(nr, channel); + wpabuf_put_u8(nr, ieee80211_get_phy_type(hapd->iface->freq, ht, vht)); + + /* + * Wide Bandwidth Channel subelement may be needed to allow the + * receiving STA to send packets to the AP. See IEEE P802.11-REVmc/D5.0 + * Figure 9-301. + */ + wpabuf_put_u8(nr, WNM_NEIGHBOR_WIDE_BW_CHAN); + wpabuf_put_u8(nr, 3); + wpabuf_put_u8(nr, width); + wpabuf_put_u8(nr, center_freq1); + wpabuf_put_u8(nr, center_freq2); + + hostapd_neighbor_set(hapd, hapd->own_addr, &ssid, nr, hapd->iconf->lci, + hapd->iconf->civic); + + wpabuf_free(nr); +#endif /* NEED_AP_MLME */ +} + + +static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface, + int err) { struct hostapd_data *hapd = iface->bss[0]; size_t j; @@ -1633,7 +1769,7 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err) } while (j-- > 0); goto fail; } - if (hostapd_mac_comp_empty(hapd->conf->bssid) == 0) + if (is_zero_ether_addr(hapd->conf->bssid)) prev_addr = hapd->own_addr; } hapd = iface->bss[0]; @@ -1641,7 +1777,6 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err) hostapd_tx_queue_params(iface); ap_list_init(iface); - dl_list_init(&iface->sta_seen); hostapd_set_acl(hapd); @@ -1701,6 +1836,9 @@ dfs_offload: if (iface->interfaces && iface->interfaces->terminate_on_error > 0) iface->interfaces->terminate_on_error--; + for (j = 0; j < iface->num_bss; j++) + hostapd_set_own_neighbor_report(iface->bss[j]); + return 0; fail: @@ -1719,6 +1857,89 @@ fail: } +/** + * hostapd_setup_interface_complete - Complete interface setup + * + * This function is called when previous steps in the interface setup has been + * completed. This can also start operations, e.g., DFS, that will require + * additional processing before interface is ready to be enabled. Such + * operations will call this function from eloop callbacks when finished. + */ +int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err) +{ + struct hapd_interfaces *interfaces = iface->interfaces; + struct hostapd_data *hapd = iface->bss[0]; + unsigned int i; + int not_ready_in_sync_ifaces = 0; + + if (!iface->need_to_start_in_sync) + return hostapd_setup_interface_complete_sync(iface, err); + + if (err) { + wpa_printf(MSG_ERROR, "Interface initialization failed"); + hostapd_set_state(iface, HAPD_IFACE_DISABLED); + iface->need_to_start_in_sync = 0; + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED); + if (interfaces && interfaces->terminate_on_error) + eloop_terminate(); + return -1; + } + + if (iface->ready_to_start_in_sync) { + /* Already in ready and waiting. should never happpen */ + return 0; + } + + for (i = 0; i < interfaces->count; i++) { + if (interfaces->iface[i]->need_to_start_in_sync && + !interfaces->iface[i]->ready_to_start_in_sync) + not_ready_in_sync_ifaces++; + } + + /* + * Check if this is the last interface, if yes then start all the other + * waiting interfaces. If not, add this interface to the waiting list. + */ + if (not_ready_in_sync_ifaces > 1 && iface->state == HAPD_IFACE_DFS) { + /* + * If this interface went through CAC, do not synchronize, just + * start immediately. + */ + iface->need_to_start_in_sync = 0; + wpa_printf(MSG_INFO, + "%s: Finished CAC - bypass sync and start interface", + iface->bss[0]->conf->iface); + return hostapd_setup_interface_complete_sync(iface, err); + } + + if (not_ready_in_sync_ifaces > 1) { + /* need to wait as there are other interfaces still coming up */ + iface->ready_to_start_in_sync = 1; + wpa_printf(MSG_INFO, + "%s: Interface waiting to sync with other interfaces", + iface->bss[0]->conf->iface); + return 0; + } + + wpa_printf(MSG_INFO, + "%s: Last interface to sync - starting all interfaces", + iface->bss[0]->conf->iface); + iface->need_to_start_in_sync = 0; + hostapd_setup_interface_complete_sync(iface, err); + for (i = 0; i < interfaces->count; i++) { + if (interfaces->iface[i]->need_to_start_in_sync && + interfaces->iface[i]->ready_to_start_in_sync) { + hostapd_setup_interface_complete_sync( + interfaces->iface[i], 0); + /* Only once the interfaces are sync started */ + interfaces->iface[i]->need_to_start_in_sync = 0; + } + } + + return 0; +} + + /** * hostapd_setup_interface - Setup of an interface * @iface: Pointer to interface data. @@ -1778,6 +1999,8 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface, hapd->iface = hapd_iface; hapd->driver = hapd->iconf->driver; hapd->ctrl_sock = -1; + dl_list_init(&hapd->ctrl_dst); + dl_list_init(&hapd->nr_db); return hapd; } @@ -1785,6 +2008,8 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface, static void hostapd_bss_deinit(struct hostapd_data *hapd) { + if (!hapd) + return; wpa_printf(MSG_DEBUG, "%s: deinit bss %s", __func__, hapd->conf->iface); hostapd_bss_deinit_no_free(hapd); @@ -1819,8 +2044,11 @@ void hostapd_interface_deinit(struct hostapd_iface *iface) } #endif /* CONFIG_FST */ - for (j = iface->num_bss - 1; j >= 0; j--) + for (j = iface->num_bss - 1; j >= 0; j--) { + if (!iface->bss) + break; hostapd_bss_deinit(iface->bss[j]); + } } @@ -1829,6 +2057,8 @@ void hostapd_interface_free(struct hostapd_iface *iface) size_t j; wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface); for (j = 0; j < iface->num_bss; j++) { + if (!iface->bss) + break; wpa_printf(MSG_DEBUG, "%s: free hapd %p", __func__, iface->bss[j]); os_free(iface->bss[j]); @@ -1837,6 +2067,20 @@ void hostapd_interface_free(struct hostapd_iface *iface) } +struct hostapd_iface * hostapd_alloc_iface(void) +{ + struct hostapd_iface *hapd_iface; + + hapd_iface = os_zalloc(sizeof(*hapd_iface)); + if (!hapd_iface) + return NULL; + + dl_list_init(&hapd_iface->sta_seen); + + return hapd_iface; +} + + /** * hostapd_init - Allocate and initialize per-interface data * @config_file: Path to the configuration file @@ -1854,7 +2098,7 @@ struct hostapd_iface * hostapd_init(struct hapd_interfaces *interfaces, struct hostapd_data *hapd; size_t i; - hapd_iface = os_zalloc(sizeof(*hapd_iface)); + hapd_iface = hostapd_alloc_iface(); if (hapd_iface == NULL) goto fail; @@ -2190,7 +2434,7 @@ hostapd_iface_alloc(struct hapd_interfaces *interfaces) return NULL; interfaces->iface = iface; hapd_iface = interfaces->iface[interfaces->count] = - os_zalloc(sizeof(*hapd_iface)); + hostapd_alloc_iface(); if (hapd_iface == NULL) { wpa_printf(MSG_ERROR, "%s: Failed to allocate memory for " "the interface", __func__); @@ -2557,6 +2801,7 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, } hostapd_prune_associations(hapd, sta->addr); + ap_sta_clear_disconnect_timeouts(hapd, sta); /* IEEE 802.11F (IAPP) */ if (hapd->conf->ieee802_11f) @@ -2590,9 +2835,10 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, wpa_auth_sta_associated(hapd->wpa_auth, sta->wpa_sm); if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) { - wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout " - "for " MACSTR " (%d seconds - ap_max_inactivity)", - __func__, MAC2STR(sta->addr), + wpa_printf(MSG_DEBUG, + "%s: %s: reschedule ap_handle_timer timeout for " + MACSTR " (%d seconds - ap_max_inactivity)", + hapd->conf->iface, __func__, MAC2STR(sta->addr), hapd->conf->ap_max_inactivity); eloop_cancel_timeout(ap_handle_timer, hapd, sta); eloop_register_timeout(hapd->conf->ap_max_inactivity, 0, @@ -2627,12 +2873,23 @@ const char * hostapd_state_text(enum hostapd_iface_state s) void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s) { wpa_printf(MSG_INFO, "%s: interface state %s->%s", - iface->conf->bss[0]->iface, hostapd_state_text(iface->state), - hostapd_state_text(s)); + iface->conf ? iface->conf->bss[0]->iface : "N/A", + hostapd_state_text(iface->state), hostapd_state_text(s)); iface->state = s; } +int hostapd_csa_in_progress(struct hostapd_iface *iface) +{ + unsigned int i; + + for (i = 0; i < iface->num_bss; i++) + if (iface->bss[i]->csa_in_progress) + return 1; + return 0; +} + + #ifdef NEED_AP_MLME static void free_beacon_data(struct beacon_data *beacon) @@ -2744,9 +3001,9 @@ free_ap_params: /* - * TODO: This flow currently supports only changing frequency within the - * same hw_mode. Any other changes to MAC parameters or provided settings (even - * width) are not supported. + * TODO: This flow currently supports only changing channel and width within + * the same hw_mode. Any other changes to MAC parameters or provided settings + * are not supported. */ static int hostapd_change_config_freq(struct hostapd_data *hapd, struct hostapd_config *conf, @@ -2765,15 +3022,44 @@ static int hostapd_change_config_freq(struct hostapd_data *hapd, return -1; /* if a pointer to old_params is provided we save previous state */ - if (old_params) { - old_params->channel = conf->channel; - old_params->ht_enabled = conf->ieee80211n; - old_params->sec_channel_offset = conf->secondary_channel; + if (old_params && + hostapd_set_freq_params(old_params, conf->hw_mode, + hostapd_hw_get_freq(hapd, conf->channel), + conf->channel, conf->ieee80211n, + conf->ieee80211ac, + conf->secondary_channel, + conf->vht_oper_chwidth, + conf->vht_oper_centr_freq_seg0_idx, + conf->vht_oper_centr_freq_seg1_idx, + conf->vht_capab)) + return -1; + + switch (params->bandwidth) { + case 0: + case 20: + case 40: + conf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT; + break; + case 80: + if (params->center_freq2) + conf->vht_oper_chwidth = VHT_CHANWIDTH_80P80MHZ; + else + conf->vht_oper_chwidth = VHT_CHANWIDTH_80MHZ; + break; + case 160: + conf->vht_oper_chwidth = VHT_CHANWIDTH_160MHZ; + break; + default: + return -1; } conf->channel = channel; conf->ieee80211n = params->ht_enabled; conf->secondary_channel = params->sec_channel_offset; + ieee80211_freq_to_chan(params->center_freq1, + &conf->vht_oper_centr_freq_seg0_idx); + ieee80211_freq_to_chan(params->center_freq2, + &conf->vht_oper_centr_freq_seg1_idx); /* TODO: maybe call here hostapd_config_check here? */ @@ -2787,11 +3073,43 @@ static int hostapd_fill_csa_settings(struct hostapd_data *hapd, struct hostapd_iface *iface = hapd->iface; struct hostapd_freq_params old_freq; int ret; + u8 chan, vht_bandwidth; os_memset(&old_freq, 0, sizeof(old_freq)); if (!iface || !iface->freq || hapd->csa_in_progress) return -1; + switch (settings->freq_params.bandwidth) { + case 80: + if (settings->freq_params.center_freq2) + vht_bandwidth = VHT_CHANWIDTH_80P80MHZ; + else + vht_bandwidth = VHT_CHANWIDTH_80MHZ; + break; + case 160: + vht_bandwidth = VHT_CHANWIDTH_160MHZ; + break; + default: + vht_bandwidth = VHT_CHANWIDTH_USE_HT; + break; + } + + if (ieee80211_freq_to_channel_ext( + settings->freq_params.freq, + settings->freq_params.sec_channel_offset, + vht_bandwidth, + &hapd->iface->cs_oper_class, + &chan) == NUM_HOSTAPD_MODES) { + wpa_printf(MSG_DEBUG, + "invalid frequency for channel switch (freq=%d, sec_channel_offset=%d, vht_enabled=%d)", + settings->freq_params.freq, + settings->freq_params.sec_channel_offset, + settings->freq_params.vht_enabled); + return -1; + } + + settings->freq_params.channel = chan; + ret = hostapd_change_config_freq(iface->bss[0], iface->conf, &settings->freq_params, &old_freq); @@ -2818,8 +3136,10 @@ static int hostapd_fill_csa_settings(struct hostapd_data *hapd, return ret; } - settings->counter_offset_beacon = hapd->cs_c_off_beacon; - settings->counter_offset_presp = hapd->cs_c_off_proberesp; + settings->counter_offset_beacon[0] = hapd->cs_c_off_beacon; + settings->counter_offset_presp[0] = hapd->cs_c_off_proberesp; + settings->counter_offset_beacon[1] = hapd->cs_c_off_ecsa_beacon; + settings->counter_offset_presp[1] = hapd->cs_c_off_ecsa_proberesp; return 0; } @@ -2833,6 +3153,8 @@ void hostapd_cleanup_cs_params(struct hostapd_data *hapd) hapd->cs_c_off_beacon = 0; hapd->cs_c_off_proberesp = 0; hapd->csa_in_progress = 0; + hapd->cs_c_off_ecsa_beacon = 0; + hapd->cs_c_off_ecsa_proberesp = 0; } @@ -2920,6 +3242,8 @@ hostapd_switch_channel_fallback(struct hostapd_iface *iface, hostapd_enable_iface(iface); } +#endif /* NEED_AP_MLME */ + struct hostapd_data * hostapd_get_iface(struct hapd_interfaces *interfaces, const char *ifname) @@ -2940,8 +3264,6 @@ struct hostapd_data * hostapd_get_iface(struct hapd_interfaces *interfaces, return NULL; } -#endif /* NEED_AP_MLME */ - void hostapd_periodic_iface(struct hostapd_iface *iface) { diff --git a/contrib/wpa/src/ap/hostapd.h b/contrib/wpa/src/ap/hostapd.h index dcf51f00f78d..dec46f692206 100644 --- a/contrib/wpa/src/ap/hostapd.h +++ b/contrib/wpa/src/ap/hostapd.h @@ -41,7 +41,7 @@ struct hapd_interfaces { size_t count; int global_ctrl_sock; - struct wpa_ctrl_dst *global_ctrl_dst; + struct dl_list global_ctrl_dst; char *global_iface_path; char *global_iface_name; #ifndef CONFIG_NATIVE_WINDOWS @@ -53,6 +53,7 @@ struct hapd_interfaces { #ifndef CONFIG_NO_VLAN struct dynamic_iface *vlan_priv; #endif /* CONFIG_NO_VLAN */ + int eloop_initialized; }; enum hostapd_chan_status { @@ -99,6 +100,16 @@ struct wps_stat { u8 peer_addr[ETH_ALEN]; }; +struct hostapd_neighbor_entry { + struct dl_list list; + u8 bssid[ETH_ALEN]; + struct wpa_ssid_value ssid; + struct wpabuf *nr; + struct wpabuf *lci; + struct wpabuf *civic; + /* LCI update time */ + struct os_time lci_date; +}; /** * struct hostapd_data - hostapd per-BSS data structure @@ -138,7 +149,7 @@ struct hostapd_data { void *msg_ctx_parent; /* parent interface ctx for wpa_msg() calls */ struct radius_client_data *radius; - u32 acct_session_id_hi, acct_session_id_lo; + u64 acct_session_id; struct radius_das_data *radius_das; struct iapp_data *iapp; @@ -155,7 +166,7 @@ struct hostapd_data { int tkip_countermeasures; int ctrl_sock; - struct wpa_ctrl_dst *ctrl_dst; + struct dl_list ctrl_dst; void *ssl_ctx; void *eap_sim_db_priv; @@ -228,6 +239,8 @@ struct hostapd_data { unsigned int cs_c_off_beacon; unsigned int cs_c_off_proberesp; int csa_in_progress; + unsigned int cs_c_off_ecsa_beacon; + unsigned int cs_c_off_ecsa_proberesp; /* BSS Load */ unsigned int bss_load_update_timeout; @@ -256,9 +269,11 @@ struct hostapd_data { #ifdef CONFIG_MESH int num_plinks; int max_plinks; - void (*mesh_sta_free_cb)(struct sta_info *sta); + void (*mesh_sta_free_cb)(struct hostapd_data *hapd, + struct sta_info *sta); struct wpabuf *mesh_pending_auth; struct os_reltime mesh_pending_auth_time; + u8 mesh_required_peer[ETH_ALEN]; #endif /* CONFIG_MESH */ #ifdef CONFIG_SQLITE @@ -278,6 +293,17 @@ struct hostapd_data { struct l2_packet_data *l2_test; #endif /* CONFIG_TESTING_OPTIONS */ + +#ifdef CONFIG_MBO + unsigned int mbo_assoc_disallow; +#endif /* CONFIG_MBO */ + + struct dl_list nr_db; + + u8 lci_req_token; + u8 range_req_token; + unsigned int lci_req_active:1; + unsigned int range_req_active:1; }; @@ -285,6 +311,9 @@ struct hostapd_sta_info { struct dl_list list; u8 addr[ETH_ALEN]; struct os_reltime last_seen; +#ifdef CONFIG_TAXONOMY + struct wpabuf *probe_ie_taxonomy; +#endif /* CONFIG_TAXONOMY */ }; /** @@ -327,6 +356,15 @@ struct hostapd_iface { */ unsigned int driver_ap_teardown:1; + /* + * When set, indicates that this interface is part of list of + * interfaces that need to be started together (synchronously). + */ + unsigned int need_to_start_in_sync:1; + + /* Ready to start but waiting for other interfaces to become ready. */ + unsigned int ready_to_start_in_sync:1; + int num_ap; /* number of entries in ap_list */ struct ap_info *ap_list; /* AP info list head */ struct ap_info *ap_hash[STA_HASH_SIZE]; @@ -402,6 +440,9 @@ struct hostapd_iface { u64 last_channel_time_busy; u8 channel_utilization; + /* eCSA IE will be added only if operating class is specified */ + u8 cs_oper_class; + unsigned int dfs_cac_ms; struct os_reltime dfs_cac_start; @@ -433,6 +474,7 @@ int hostapd_setup_interface(struct hostapd_iface *iface); int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err); void hostapd_interface_deinit(struct hostapd_iface *iface); void hostapd_interface_free(struct hostapd_iface *iface); +struct hostapd_iface * hostapd_alloc_iface(void); struct hostapd_iface * hostapd_init(struct hapd_interfaces *interfaces, const char *config_file); struct hostapd_iface * @@ -449,6 +491,7 @@ int hostapd_remove_iface(struct hapd_interfaces *ifaces, char *buf); void hostapd_channel_list_updated(struct hostapd_iface *iface, int initiator); void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s); const char * hostapd_state_text(enum hostapd_iface_state s); +int hostapd_csa_in_progress(struct hostapd_iface *iface); int hostapd_switch_channel(struct hostapd_data *hapd, struct csa_settings *settings); void @@ -478,6 +521,11 @@ int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da, int ssi_signal); void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, int offset, int width, int cf1, int cf2); +struct survey_results; +void hostapd_event_get_survey(struct hostapd_iface *iface, + struct survey_results *survey_results); +void hostapd_acs_channel_selected(struct hostapd_data *hapd, + struct acs_selected_channels *acs_res); const struct hostapd_eap_user * hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity, diff --git a/contrib/wpa/src/ap/hw_features.c b/contrib/wpa/src/ap/hw_features.c index fc8786dc311c..16887acdfef4 100644 --- a/contrib/wpa/src/ap/hw_features.c +++ b/contrib/wpa/src/ap/hw_features.c @@ -329,6 +329,7 @@ static void ieee80211n_check_scan(struct hostapd_iface *iface) res = ieee80211n_allowed_ht40_channel_pair(iface); if (!res) { iface->conf->secondary_channel = 0; + res = 1; wpa_printf(MSG_INFO, "Fallback to 20 MHz"); } @@ -472,8 +473,9 @@ static int ieee80211n_check_40mhz(struct hostapd_iface *iface) struct wpa_driver_scan_params params; int ret; - if (!iface->conf->secondary_channel) - return 0; /* HT40 not used */ + /* Check that HT40 is used and PRI / SEC switch is allowed */ + if (!iface->conf->secondary_channel || iface->conf->no_pri_sec_switch) + return 0; hostapd_set_state(iface, HAPD_IFACE_HT_SCAN); wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling " diff --git a/contrib/wpa/src/ap/iapp.c b/contrib/wpa/src/ap/iapp.c index 99aa04dc3dd9..2556da30c82f 100644 --- a/contrib/wpa/src/ap/iapp.c +++ b/contrib/wpa/src/ap/iapp.c @@ -34,11 +34,7 @@ #include "utils/includes.h" #include #include -#ifdef USE_KERNEL_HEADERS -#include -#else /* USE_KERNEL_HEADERS */ #include -#endif /* USE_KERNEL_HEADERS */ #include "utils/common.h" #include "utils/eloop.h" @@ -385,6 +381,7 @@ struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface) struct sockaddr_in *paddr, uaddr; struct iapp_data *iapp; struct ip_mreqn mreq; + int reuseaddr = 1; iapp = os_zalloc(sizeof(*iapp)); if (iapp == NULL) @@ -447,6 +444,18 @@ struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface) os_memset(&uaddr, 0, sizeof(uaddr)); uaddr.sin_family = AF_INET; uaddr.sin_port = htons(IAPP_UDP_PORT); + + if (setsockopt(iapp->udp_sock, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, + sizeof(reuseaddr)) < 0) { + wpa_printf(MSG_INFO, + "iapp_init - setsockopt[UDP,SO_REUSEADDR]: %s", + strerror(errno)); + /* + * Ignore this and try to continue. This is fine for single + * BSS cases, but may fail if multiple BSSes enable IAPP. + */ + } + if (bind(iapp->udp_sock, (struct sockaddr *) &uaddr, sizeof(uaddr)) < 0) { wpa_printf(MSG_INFO, "iapp_init - bind[UDP]: %s", diff --git a/contrib/wpa/src/ap/ieee802_11.c b/contrib/wpa/src/ap/ieee802_11.c index 7bb18c01d1a1..4e04169c73e6 100644 --- a/contrib/wpa/src/ap/ieee802_11.c +++ b/contrib/wpa/src/ap/ieee802_11.c @@ -42,6 +42,9 @@ #include "hw_features.h" #include "ieee802_11.h" #include "dfs.h" +#include "mbo_ap.h" +#include "rrm.h" +#include "taxonomy.h" u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid) @@ -139,6 +142,7 @@ u16 hostapd_own_capab_info(struct hostapd_data *hapd) int capab = WLAN_CAPABILITY_ESS; int privacy; int dfs; + int i; /* Check if any of configured channels require DFS */ dfs = hostapd_is_dfs_required(hapd->iface); @@ -186,8 +190,12 @@ u16 hostapd_own_capab_info(struct hostapd_data *hapd) (hapd->iconf->spectrum_mgmt_required || dfs)) capab |= WLAN_CAPABILITY_SPECTRUM_MGMT; - if (hapd->conf->radio_measurements) - capab |= IEEE80211_CAP_RRM; + for (i = 0; i < RRM_CAPABILITIES_IE_LEN; i++) { + if (hapd->conf->radio_measurements[i]) { + capab |= IEEE80211_CAP_RRM; + break; + } + } return capab; } @@ -207,16 +215,17 @@ static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta, if (!sta->challenge) { /* Generate a pseudo-random challenge */ u8 key[8]; - struct os_time now; - int r; + sta->challenge = os_zalloc(WLAN_AUTH_CHALLENGE_LEN); if (sta->challenge == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; - os_get_time(&now); - r = os_random(); - os_memcpy(key, &now.sec, 4); - os_memcpy(key + 4, &r, 4); + if (os_get_random(key, sizeof(key)) < 0) { + os_free(sta->challenge); + sta->challenge = NULL; + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + rc4_skip(key, sizeof(key), 0, sta->challenge, WLAN_AUTH_CHALLENGE_LEN); } @@ -250,19 +259,20 @@ static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta, #endif /* CONFIG_NO_RC4 */ -static void send_auth_reply(struct hostapd_data *hapd, - const u8 *dst, const u8 *bssid, - u16 auth_alg, u16 auth_transaction, u16 resp, - const u8 *ies, size_t ies_len) +static int send_auth_reply(struct hostapd_data *hapd, + const u8 *dst, const u8 *bssid, + u16 auth_alg, u16 auth_transaction, u16 resp, + const u8 *ies, size_t ies_len) { struct ieee80211_mgmt *reply; u8 *buf; size_t rlen; + int reply_res = WLAN_STATUS_UNSPECIFIED_FAILURE; rlen = IEEE80211_HDRLEN + sizeof(reply->u.auth) + ies_len; buf = os_zalloc(rlen); if (buf == NULL) - return; + return -1; reply = (struct ieee80211_mgmt *) buf; reply->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, @@ -283,9 +293,13 @@ static void send_auth_reply(struct hostapd_data *hapd, MAC2STR(dst), auth_alg, auth_transaction, resp, (unsigned long) ies_len); if (hostapd_drv_send_mlme(hapd, reply, rlen, 0) < 0) - wpa_printf(MSG_INFO, "send_auth_reply: send"); + wpa_printf(MSG_INFO, "send_auth_reply: send failed"); + else + reply_res = WLAN_STATUS_SUCCESS; os_free(buf); + + return reply_res; } @@ -296,17 +310,25 @@ static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid, { struct hostapd_data *hapd = ctx; struct sta_info *sta; + int reply_res; - send_auth_reply(hapd, dst, bssid, WLAN_AUTH_FT, auth_transaction, - status, ies, ies_len); - - if (status != WLAN_STATUS_SUCCESS) - return; + reply_res = send_auth_reply(hapd, dst, bssid, WLAN_AUTH_FT, + auth_transaction, status, ies, ies_len); sta = ap_get_sta(hapd, dst); if (sta == NULL) return; + if (sta->added_unassoc && (reply_res != WLAN_STATUS_SUCCESS || + status != WLAN_STATUS_SUCCESS)) { + hostapd_drv_sta_remove(hapd, sta->addr); + sta->added_unassoc = 0; + return; + } + + if (status != WLAN_STATUS_SUCCESS) + return; + hostapd_logger(hapd, dst, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "authentication OK (FT)"); sta->flags |= WLAN_STA_AUTH; @@ -369,18 +391,19 @@ static int auth_sae_send_commit(struct hostapd_data *hapd, const u8 *bssid, int update) { struct wpabuf *data; + int reply_res; data = auth_build_sae_commit(hapd, sta, update); if (data == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; - send_auth_reply(hapd, sta->addr, bssid, - WLAN_AUTH_SAE, 1, WLAN_STATUS_SUCCESS, - wpabuf_head(data), wpabuf_len(data)); + reply_res = send_auth_reply(hapd, sta->addr, bssid, WLAN_AUTH_SAE, 1, + WLAN_STATUS_SUCCESS, wpabuf_head(data), + wpabuf_len(data)); wpabuf_free(data); - return WLAN_STATUS_SUCCESS; + return reply_res; } @@ -389,18 +412,19 @@ static int auth_sae_send_confirm(struct hostapd_data *hapd, const u8 *bssid) { struct wpabuf *data; + int reply_res; data = auth_build_sae_confirm(hapd, sta); if (data == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; - send_auth_reply(hapd, sta->addr, bssid, - WLAN_AUTH_SAE, 2, WLAN_STATUS_SUCCESS, - wpabuf_head(data), wpabuf_len(data)); + reply_res = send_auth_reply(hapd, sta->addr, bssid, WLAN_AUTH_SAE, 2, + WLAN_STATUS_SUCCESS, wpabuf_head(data), + wpabuf_len(data)); wpabuf_free(data); - return WLAN_STATUS_SUCCESS; + return reply_res; } @@ -495,6 +519,9 @@ static void auth_sae_retransmit_timer(void *eloop_ctx, void *eloop_data) if (sae_check_big_sync(sta)) return; sta->sae->sync++; + wpa_printf(MSG_DEBUG, "SAE: Auth SAE retransmit timer for " MACSTR + " (sync=%d state=%d)", + MAC2STR(sta->addr), sta->sae->sync, sta->sae->state); switch (sta->sae->state) { case SAE_COMMITTED: @@ -537,6 +564,18 @@ static void sae_set_retransmit_timer(struct hostapd_data *hapd, } +void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta) +{ + sta->flags |= WLAN_STA_AUTH; + sta->auth_alg = WLAN_AUTH_SAE; + mlme_authenticate_indication(hapd, sta); + wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); + sta->sae->state = SAE_ACCEPTED; + wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr, + sta->sae->pmk, sta->sae->pmkid); +} + + static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, const u8 *bssid, u8 auth_transaction) { @@ -580,7 +619,7 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, * message now to get alternating sequence of * Authentication frames between the AP and STA. * Confirm will be sent in - * Commited -> Confirmed/Accepted transition + * Committed -> Confirmed/Accepted transition * when receiving Confirm from STA. */ } @@ -659,13 +698,7 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, sae_set_retransmit_timer(hapd, sta); } else { - sta->flags |= WLAN_STA_AUTH; - sta->auth_alg = WLAN_AUTH_SAE; - mlme_authenticate_indication(hapd, sta); - wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); - sta->sae->state = SAE_ACCEPTED; - wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr, - sta->sae->pmk); + sae_accept_sta(hapd, sta); } break; case SAE_ACCEPTED: @@ -674,6 +707,7 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, ") doing reauthentication", MAC2STR(sta->addr)); ap_free_sta(hapd, sta); + wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr); } else { if (sae_check_big_sync(sta)) return WLAN_STATUS_SUCCESS; @@ -694,23 +728,73 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, } +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; + + 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 (sae->group == groups[i]) + break; + } + + if (!groups || groups[i] <= 0) { + wpa_printf(MSG_DEBUG, + "SAE: Previously selected group not found from the current configuration"); + return; + } + + for (;;) { + i++; + if (groups[i] <= 0) { + wpa_printf(MSG_DEBUG, + "SAE: No alternative group enabled"); + return; + } + + if (sae_set_group(sae, groups[i]) < 0) + continue; + + break; + } + wpa_printf(MSG_DEBUG, "SAE: Selected new group: %d", groups[i]); +} + + static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, const struct ieee80211_mgmt *mgmt, size_t len, u16 auth_transaction, u16 status_code) { - u16 resp = WLAN_STATUS_SUCCESS; + int resp = WLAN_STATUS_SUCCESS; struct wpabuf *data = NULL; if (!sta->sae) { - if (auth_transaction != 1 || status_code != WLAN_STATUS_SUCCESS) - return; + if (auth_transaction != 1 || + status_code != WLAN_STATUS_SUCCESS) { + resp = -1; + goto remove_sta; + } sta->sae = os_zalloc(sizeof(*sta->sae)); - if (sta->sae == NULL) - return; + if (!sta->sae) { + resp = -1; + goto remove_sta; + } sta->sae->state = SAE_NOTHING; sta->sae->sync = 0; } + if (sta->mesh_sae_pmksa_caching) { + wpa_printf(MSG_DEBUG, + "SAE: Cancel use of mesh PMKSA caching because peer starts SAE authentication"); + wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr); + sta->mesh_sae_pmksa_caching = 0; + } + if (auth_transaction == 1) { const u8 *token = NULL, *pos, *end; size_t token_len = 0; @@ -746,7 +830,8 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, if (sta->sae->tmp->anti_clogging_token == NULL) { wpa_printf(MSG_ERROR, "SAE: Failed to alloc for anti-clogging token"); - return; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto remove_sta; } /* @@ -756,10 +841,11 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, * Authentication frame, and the commit-scalar and * COMMIT-ELEMENT previously sent. */ - if (auth_sae_send_commit(hapd, sta, mgmt->bssid, 0)) { + resp = auth_sae_send_commit(hapd, sta, mgmt->bssid, 0); + if (resp != WLAN_STATUS_SUCCESS) { wpa_printf(MSG_ERROR, "SAE: Failed to send commit message"); - return; + goto remove_sta; } sta->sae->state = SAE_COMMITTED; sta->sae->sync = 0; @@ -767,8 +853,18 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, return; } + if ((hapd->conf->mesh & MESH_ENABLED) && + status_code == + WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED && + sta->sae->tmp) { + wpa_printf(MSG_DEBUG, + "SAE: Peer did not accept our SAE group"); + sae_pick_next_group(hapd, sta); + goto remove_sta; + } + if (status_code != WLAN_STATUS_SUCCESS) - return; + goto remove_sta; resp = sae_parse_commit(sta->sae, mgmt->u.auth.variable, ((const u8 *) mgmt) + len - @@ -778,14 +874,15 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, wpa_printf(MSG_DEBUG, "SAE: Drop commit message from " MACSTR " due to reflection attack", MAC2STR(sta->addr)); - return; + goto remove_sta; } if (token && check_sae_token(hapd, sta->addr, token, token_len) < 0) { wpa_printf(MSG_DEBUG, "SAE: Drop commit message with " "incorrect token from " MACSTR, MAC2STR(sta->addr)); - return; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto remove_sta; } if (resp != WLAN_STATUS_SUCCESS) @@ -810,7 +907,7 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, "SAE authentication (RX confirm, status=%u)", status_code); if (status_code != WLAN_STATUS_SUCCESS) - return; + goto remove_sta; if (sta->sae->state >= SAE_CONFIRMED || !(hapd->conf->mesh & MESH_ENABLED)) { if (sae_check_confirm(sta->sae, mgmt->u.auth.variable, @@ -827,7 +924,7 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, "unexpected SAE authentication transaction %u (status=%u)", auth_transaction, status_code); if (status_code != WLAN_STATUS_SUCCESS) - return; + goto remove_sta; resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; } @@ -838,6 +935,13 @@ reply: data ? wpabuf_head(data) : (u8 *) "", data ? wpabuf_len(data) : 0); } + +remove_sta: + if (sta->added_unassoc && (resp != WLAN_STATUS_SUCCESS || + status_code != WLAN_STATUS_SUCCESS)) { + hostapd_drv_sta_remove(hapd, sta->addr); + sta->added_unassoc = 0; + } wpabuf_free(data); } @@ -882,11 +986,11 @@ static void handle_auth(struct hostapd_data *hapd, u16 auth_alg, auth_transaction, status_code; u16 resp = WLAN_STATUS_SUCCESS; struct sta_info *sta = NULL; - int res; + int res, reply_res; u16 fc; const u8 *challenge = NULL; u32 session_timeout, acct_interim_interval; - int vlan_id = 0; + struct vlan_description vlan_id; struct hostapd_sta_wpa_psk_short *psk = NULL; u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN]; size_t resp_ies_len = 0; @@ -894,6 +998,8 @@ static void handle_auth(struct hostapd_data *hapd, char *radius_cui = NULL; u16 seq_ctrl; + os_memset(&vlan_id, 0, sizeof(vlan_id)); + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)", (unsigned long) len); @@ -1067,13 +1173,22 @@ static void handle_auth(struct hostapd_data *hapd, seq_ctrl); return; } +#ifdef CONFIG_MESH + if ((hapd->conf->mesh & MESH_ENABLED) && + sta->plink_state == PLINK_BLOCKED) { + wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR + " is blocked - drop Authentication frame", + MAC2STR(mgmt->sa)); + return; + } +#endif /* CONFIG_MESH */ } else { #ifdef CONFIG_MESH if (hapd->conf->mesh & MESH_ENABLED) { /* if the mesh peer is not available, we don't do auth. */ wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR - " not yet known - drop Authentiation frame", + " not yet known - drop Authentication frame", MAC2STR(mgmt->sa)); /* * Save a copy of the frame so that it can be processed @@ -1095,19 +1210,23 @@ static void handle_auth(struct hostapd_data *hapd, sta->last_seq_ctrl = seq_ctrl; sta->last_subtype = WLAN_FC_STYPE_AUTH; - if (vlan_id > 0) { - if (!hostapd_vlan_id_valid(hapd->conf->vlan, vlan_id)) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_INFO, "Invalid VLAN ID " - "%d received from RADIUS server", - vlan_id); - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto fail; - } - sta->vlan_id = vlan_id; + if (vlan_id.notempty && + !hostapd_vlan_valid(hapd->conf->vlan, &vlan_id)) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, + "Invalid VLAN %d%s received from RADIUS server", + vlan_id.untagged, + vlan_id.tagged[0] ? "+" : ""); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + if (ap_sta_set_vlan(hapd, sta, &vlan_id) < 0) { + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + if (sta->vlan_id) hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id); - } hostapd_free_psk_list(sta->psk); if (hapd->conf->wpa_psk_radius != PSK_RADIUS_IGNORED) { @@ -1132,6 +1251,46 @@ static void handle_auth(struct hostapd_data *hapd, else ap_sta_no_session_timeout(hapd, sta); + /* + * If the driver supports full AP client state, add a station to the + * driver before sending authentication reply to make sure the driver + * has resources, and not to go through the entire authentication and + * association handshake, and fail it at the end. + * + * If this is not the first transaction, in a multi-step authentication + * algorithm, the station already exists in the driver + * (sta->added_unassoc = 1) so skip it. + * + * In mesh mode, the station was already added to the driver when the + * NEW_PEER_CANDIDATE event is received. + */ + if (FULL_AP_CLIENT_STATE_SUPP(hapd->iface->drv_flags) && + !(hapd->conf->mesh & MESH_ENABLED) && + !(sta->added_unassoc)) { + /* + * If a station that is already associated to the AP, is trying + * to authenticate again, remove the STA entry, in order to make + * sure the STA PS state gets cleared and configuration gets + * updated. To handle this, station's added_unassoc flag is + * cleared once the station has completed association. + */ + hostapd_drv_sta_remove(hapd, sta->addr); + sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_AUTH | + WLAN_STA_AUTHORIZED); + + if (hostapd_sta_add(hapd, sta->addr, 0, 0, NULL, 0, 0, + NULL, NULL, sta->flags, 0, 0, 0, 0)) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_NOTICE, + "Could not add STA to kernel driver"); + resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; + goto fail; + } + + sta->added_unassoc = 1; + } + switch (auth_alg) { case WLAN_AUTH_OPEN: hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, @@ -1205,12 +1364,19 @@ static void handle_auth(struct hostapd_data *hapd, os_free(radius_cui); hostapd_free_psk_list(psk); - send_auth_reply(hapd, mgmt->sa, mgmt->bssid, auth_alg, - auth_transaction + 1, resp, resp_ies, resp_ies_len); + reply_res = send_auth_reply(hapd, mgmt->sa, mgmt->bssid, auth_alg, + auth_transaction + 1, resp, resp_ies, + resp_ies_len); + + if (sta && sta->added_unassoc && (resp != WLAN_STATUS_SUCCESS || + reply_res != WLAN_STATUS_SUCCESS)) { + hostapd_drv_sta_remove(hapd, sta->addr); + sta->added_unassoc = 0; + } } -static int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta) +int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta) { int i, j = 32, aid; @@ -1220,6 +1386,9 @@ static int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta) return 0; } + if (TEST_FAIL()) + return -1; + for (i = 0; i < AID_WORDS; i++) { if (hapd->sta_aid[i] == (u32) -1) continue; @@ -1327,6 +1496,9 @@ static u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta, } #endif /* CONFIG_INTERWORKING */ + if (ext_capab_ie_len > 0) + sta->ecsa_supported = !!(ext_capab_ie[0] & BIT(2)); + return WLAN_STATUS_SUCCESS; } @@ -1617,6 +1789,27 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, sta->mb_ies = NULL; #endif /* CONFIG_FST */ +#ifdef CONFIG_MBO + mbo_ap_check_sta_assoc(hapd, sta, &elems); + + if (hapd->conf->mbo_enabled && (hapd->conf->wpa & 2) && + elems.mbo && sta->cell_capa && !(sta->flags & WLAN_STA_MFP) && + hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) { + wpa_printf(MSG_INFO, + "MBO: Reject WPA2 association without PMF"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } +#endif /* CONFIG_MBO */ + + ap_copy_sta_supp_op_classes(sta, elems.supp_op_classes, + elems.supp_op_classes_len); + + if ((sta->capability & WLAN_CAPABILITY_RADIO_MEASUREMENT) && + elems.rrm_enabled && + elems.rrm_enabled_len >= sizeof(sta->rrm_enabled_capa)) + os_memcpy(sta->rrm_enabled_capa, elems.rrm_enabled, + sizeof(sta->rrm_enabled_capa)); + return WLAN_STATUS_SUCCESS; } @@ -1643,9 +1836,66 @@ static void send_deauth(struct hostapd_data *hapd, const u8 *addr, } -static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, - u16 status_code, int reassoc, const u8 *ies, - size_t ies_len) +static int add_associated_sta(struct hostapd_data *hapd, + struct sta_info *sta) +{ + struct ieee80211_ht_capabilities ht_cap; + struct ieee80211_vht_capabilities vht_cap; + + /* + * Remove the STA entry to ensure the STA PS state gets cleared and + * configuration gets updated. This is relevant for cases, such as + * FT-over-the-DS, where a station re-associates back to the same AP but + * skips the authentication flow, or if working with a driver that + * does not support full AP client state. + */ + if (!sta->added_unassoc) + hostapd_drv_sta_remove(hapd, sta->addr); + +#ifdef CONFIG_IEEE80211N + if (sta->flags & WLAN_STA_HT) + hostapd_get_ht_capab(hapd, sta->ht_capabilities, &ht_cap); +#endif /* CONFIG_IEEE80211N */ +#ifdef CONFIG_IEEE80211AC + if (sta->flags & WLAN_STA_VHT) + hostapd_get_vht_capab(hapd, sta->vht_capabilities, &vht_cap); +#endif /* CONFIG_IEEE80211AC */ + + /* + * Add the station with forced WLAN_STA_ASSOC flag. The sta->flags + * will be set when the ACK frame for the (Re)Association Response frame + * is processed (TX status driver event). + */ + if (hostapd_sta_add(hapd, sta->addr, sta->aid, sta->capability, + sta->supported_rates, sta->supported_rates_len, + sta->listen_interval, + sta->flags & WLAN_STA_HT ? &ht_cap : NULL, + sta->flags & WLAN_STA_VHT ? &vht_cap : NULL, + sta->flags | WLAN_STA_ASSOC, sta->qosinfo, + sta->vht_opmode, sta->p2p_ie ? 1 : 0, + sta->added_unassoc)) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE, + "Could not %s STA to kernel driver", + sta->added_unassoc ? "set" : "add"); + + if (sta->added_unassoc) { + hostapd_drv_sta_remove(hapd, sta->addr); + sta->added_unassoc = 0; + } + + return -1; + } + + sta->added_unassoc = 0; + + return 0; +} + + +static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, + u16 status_code, int reassoc, const u8 *ies, + size_t ies_len) { int send_len; u8 buf[sizeof(struct ieee80211_mgmt) + 1024]; @@ -1695,7 +1945,23 @@ static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, #ifdef CONFIG_IEEE80211AC if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) { - p = hostapd_eid_vht_capabilities(hapd, p); + u32 nsts = 0, sta_nsts; + + if (hapd->conf->use_sta_nsts && sta->vht_capabilities) { + struct ieee80211_vht_capabilities *capa; + + nsts = (hapd->iface->conf->vht_capab >> + VHT_CAP_BEAMFORMEE_STS_OFFSET) & 7; + capa = sta->vht_capabilities; + sta_nsts = (le_to_host32(capa->vht_capabilities_info) >> + VHT_CAP_BEAMFORMEE_STS_OFFSET) & 7; + + if (nsts < sta_nsts) + nsts = 0; + else + nsts = sta_nsts; + } + p = hostapd_eid_vht_capabilities(hapd, p, nsts); p = hostapd_eid_vht_operation(hapd, p); } #endif /* CONFIG_IEEE80211AC */ @@ -1734,7 +2000,7 @@ static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P - if (sta->p2p_ie) { + if (sta->p2p_ie && hapd->p2p_group) { struct wpabuf *p2p_resp_ie; enum p2p_status_code status; switch (status_code) { @@ -1763,11 +2029,25 @@ static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, p = hostapd_eid_p2p_manage(hapd, p); #endif /* CONFIG_P2P_MANAGER */ + p = hostapd_eid_mbo(hapd, p, buf + sizeof(buf) - p); + + if (hapd->conf->assocresp_elements && + (size_t) (buf + sizeof(buf) - p) >= + wpabuf_len(hapd->conf->assocresp_elements)) { + os_memcpy(p, wpabuf_head(hapd->conf->assocresp_elements), + wpabuf_len(hapd->conf->assocresp_elements)); + p += wpabuf_len(hapd->conf->assocresp_elements); + } + send_len += p - reply->u.assoc_resp.variable; - if (hostapd_drv_send_mlme(hapd, reply, send_len, 0) < 0) + if (hostapd_drv_send_mlme(hapd, reply, send_len, 0) < 0) { wpa_printf(MSG_INFO, "Failed to send assoc resp: %s", strerror(errno)); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + return WLAN_STATUS_SUCCESS; } @@ -1776,7 +2056,7 @@ static void handle_assoc(struct hostapd_data *hapd, int reassoc) { u16 capab_info, listen_interval, seq_ctrl, fc; - u16 resp = WLAN_STATUS_SUCCESS; + u16 resp = WLAN_STATUS_SUCCESS, reply_res; const u8 *pos; int left, i; struct sta_info *sta; @@ -1843,6 +2123,12 @@ static void handle_assoc(struct hostapd_data *hapd, wpa_printf(MSG_DEBUG, "FT: Allow STA " MACSTR " to associate " "prior to authentication since it is using " "over-the-DS FT", MAC2STR(mgmt->sa)); + + /* + * Mark station as authenticated, to avoid adding station + * entry in the driver as associated and not authenticated + */ + sta->flags |= WLAN_STA_AUTH; } else #endif /* CONFIG_IEEE80211R */ if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) { @@ -1886,6 +2172,19 @@ static void handle_assoc(struct hostapd_data *hapd, goto fail; } +#ifdef CONFIG_MBO + if (hapd->conf->mbo_enabled && hapd->mbo_assoc_disallow) { + resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; + goto fail; + } +#endif /* CONFIG_MBO */ + + /* + * sta->capability is used in check_assoc_ies() for RRM enabled + * capability element. + */ + sta->capability = capab_info; + /* followed by SSID and Supported rates; and HT capabilities if 802.11n * is used */ resp = check_assoc_ies(hapd, sta, pos, left, reassoc); @@ -1899,7 +2198,6 @@ static void handle_assoc(struct hostapd_data *hapd, goto fail; } - sta->capability = capab_info; sta->listen_interval = listen_interval; if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) @@ -1969,8 +2267,44 @@ static void handle_assoc(struct hostapd_data *hapd, * remove the STA immediately. */ sta->timeout_next = STA_NULLFUNC; +#ifdef CONFIG_TAXONOMY + taxonomy_sta_info_assoc_req(hapd, sta, pos, left); +#endif /* CONFIG_TAXONOMY */ + fail: - send_assoc_resp(hapd, sta, resp, reassoc, pos, left); + /* + * In case of a successful response, add the station to the driver. + * Otherwise, the kernel may ignore Data frames before we process the + * ACK frame (TX status). In case of a failure, this station will be + * removed. + * + * Note that this is not compliant with the IEEE 802.11 standard that + * states that a non-AP station should transition into the + * authenticated/associated state only after the station acknowledges + * the (Re)Association Response frame. However, still do this as: + * + * 1. In case the station does not acknowledge the (Re)Association + * Response frame, it will be removed. + * 2. Data frames will be dropped in the kernel until the station is + * set into authorized state, and there are no significant known + * issues with processing other non-Data Class 3 frames during this + * window. + */ + if (resp == WLAN_STATUS_SUCCESS && add_associated_sta(hapd, sta)) + resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; + + reply_res = send_assoc_resp(hapd, sta, resp, reassoc, pos, left); + + /* + * Remove the station in case tranmission of a success response fails + * (the STA was added associated to the driver) or if the station was + * previously added unassociated. + */ + if ((reply_res != WLAN_STATUS_SUCCESS && + resp == WLAN_STATUS_SUCCESS) || sta->added_unassoc) { + hostapd_drv_sta_remove(hapd, sta->addr); + sta->added_unassoc = 0; + } } @@ -2007,11 +2341,12 @@ static void handle_disassoc(struct hostapd_data *hapd, /* Stop Accounting and IEEE 802.1X sessions, but leave the STA * authenticated. */ accounting_sta_stop(hapd, sta); - ieee802_1x_free_station(sta); + ieee802_1x_free_station(hapd, sta); if (sta->ipaddr) hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr); ap_sta_ip6addr_del(hapd, sta); hostapd_drv_sta_remove(hapd, sta->addr); + sta->added_unassoc = 0; if (sta->timeout_next == STA_NULLFUNC || sta->timeout_next == STA_DISASSOC) { @@ -2233,6 +2568,9 @@ static int handle_action(struct hostapd_data *hapd, return 1; } break; + case WLAN_ACTION_RADIO_MEASUREMENT: + hostapd_handle_radio_measurement(hapd, (const u8 *) mgmt, len); + return 1; } hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, @@ -2240,8 +2578,9 @@ static int handle_action(struct hostapd_data *hapd, "handle_action - unknown action category %d or invalid " "frame", mgmt->u.action.category); - if (!(mgmt->da[0] & 0x01) && !(mgmt->u.action.category & 0x80) && - !(mgmt->sa[0] & 0x01)) { + if (!is_multicast_ether_addr(mgmt->da) && + !(mgmt->u.action.category & 0x80) && + !is_multicast_ether_addr(mgmt->sa)) { struct ieee80211_mgmt *resp; /* @@ -2288,7 +2627,6 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, struct hostapd_frame_info *fi) { struct ieee80211_mgmt *mgmt; - int broadcast; u16 fc, stype; int ret = 0; @@ -2304,11 +2642,7 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, return 1; } - broadcast = mgmt->bssid[0] == 0xff && mgmt->bssid[1] == 0xff && - mgmt->bssid[2] == 0xff && mgmt->bssid[3] == 0xff && - mgmt->bssid[4] == 0xff && mgmt->bssid[5] == 0xff; - - if (!broadcast && + if (!is_broadcast_ether_addr(mgmt->bssid) && #ifdef CONFIG_P2P /* Invitation responses can be sent with the peer MAC as BSSID */ !((hapd->conf->p2p & P2P_GROUP_OWNER) && @@ -2388,16 +2722,10 @@ static void handle_auth_cb(struct hostapd_data *hapd, u16 auth_alg, auth_transaction, status_code; struct sta_info *sta; - if (!ok) { - hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_NOTICE, - "did not acknowledge authentication response"); - return; - } - - if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { - wpa_printf(MSG_INFO, "handle_auth_cb - too short payload (len=%lu)", - (unsigned long) len); + sta = ap_get_sta(hapd, mgmt->da); + if (!sta) { + wpa_printf(MSG_INFO, "handle_auth_cb: STA " MACSTR " not found", + MAC2STR(mgmt->da)); return; } @@ -2405,11 +2733,17 @@ static void handle_auth_cb(struct hostapd_data *hapd, auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); status_code = le_to_host16(mgmt->u.auth.status_code); - sta = ap_get_sta(hapd, mgmt->da); - if (!sta) { - wpa_printf(MSG_INFO, "handle_auth_cb: STA " MACSTR " not found", - MAC2STR(mgmt->da)); - return; + if (!ok) { + hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_NOTICE, + "did not acknowledge authentication response"); + goto fail; + } + + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { + wpa_printf(MSG_INFO, "handle_auth_cb - too short payload (len=%lu)", + (unsigned long) len); + goto fail; } if (status_code == WLAN_STATUS_SUCCESS && @@ -2418,6 +2752,15 @@ static void handle_auth_cb(struct hostapd_data *hapd, hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "authenticated"); sta->flags |= WLAN_STA_AUTH; + if (sta->added_unassoc) + hostapd_set_sta_flags(hapd, sta); + return; + } + +fail: + if (status_code != WLAN_STATUS_SUCCESS && sta->added_unassoc) { + hostapd_drv_sta_remove(hapd, sta->addr); + sta->added_unassoc = 0; } } @@ -2453,15 +2796,6 @@ static void handle_assoc_cb(struct hostapd_data *hapd, u16 status; struct sta_info *sta; int new_assoc = 1; - struct ieee80211_ht_capabilities ht_cap; - struct ieee80211_vht_capabilities vht_cap; - - if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) : - sizeof(mgmt->u.assoc_resp))) { - wpa_printf(MSG_INFO, "handle_assoc_cb(reassoc=%d) - too short payload (len=%lu)", - reassoc, (unsigned long) len); - return; - } sta = ap_get_sta(hapd, mgmt->da); if (!sta) { @@ -2470,11 +2804,12 @@ static void handle_assoc_cb(struct hostapd_data *hapd, return; } - if (!ok) { - hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, - "did not acknowledge association response"); - sta->flags &= ~WLAN_STA_ASSOC_REQ_OK; + if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) : + sizeof(mgmt->u.assoc_resp))) { + wpa_printf(MSG_INFO, + "handle_assoc_cb(reassoc=%d) - too short payload (len=%lu)", + reassoc, (unsigned long) len); + hostapd_drv_sta_remove(hapd, sta->addr); return; } @@ -2483,6 +2818,18 @@ static void handle_assoc_cb(struct hostapd_data *hapd, else status = le_to_host16(mgmt->u.assoc_resp.status_code); + if (!ok) { + hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "did not acknowledge association response"); + sta->flags &= ~WLAN_STA_ASSOC_REQ_OK; + /* The STA is added only in case of SUCCESS */ + if (status == WLAN_STATUS_SUCCESS) + hostapd_drv_sta_remove(hapd, sta->addr); + + return; + } + if (status != WLAN_STATUS_SUCCESS) return; @@ -2517,38 +2864,6 @@ static void handle_assoc_cb(struct hostapd_data *hapd, sta->sa_query_timed_out = 0; #endif /* CONFIG_IEEE80211W */ - /* - * Remove the STA entry in order to make sure the STA PS state gets - * cleared and configuration gets updated in case of reassociation back - * to the same AP. - */ - hostapd_drv_sta_remove(hapd, sta->addr); - -#ifdef CONFIG_IEEE80211N - if (sta->flags & WLAN_STA_HT) - hostapd_get_ht_capab(hapd, sta->ht_capabilities, &ht_cap); -#endif /* CONFIG_IEEE80211N */ -#ifdef CONFIG_IEEE80211AC - if (sta->flags & WLAN_STA_VHT) - hostapd_get_vht_capab(hapd, sta->vht_capabilities, &vht_cap); -#endif /* CONFIG_IEEE80211AC */ - - if (hostapd_sta_add(hapd, sta->addr, sta->aid, sta->capability, - sta->supported_rates, sta->supported_rates_len, - sta->listen_interval, - sta->flags & WLAN_STA_HT ? &ht_cap : NULL, - sta->flags & WLAN_STA_VHT ? &vht_cap : NULL, - sta->flags, sta->qosinfo, sta->vht_opmode)) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_NOTICE, - "Could not add STA to kernel driver"); - - ap_sta_disconnect(hapd, sta, sta->addr, - WLAN_REASON_DISASSOC_AP_BUSY); - - return; - } - if (sta->flags & WLAN_STA_WDS) { int ret; char ifname_wds[IFNAMSIZ + 1]; @@ -2580,8 +2895,26 @@ static void handle_assoc_cb(struct hostapd_data *hapd, else wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC); hapd->new_assoc_sta_cb(hapd, sta, !new_assoc); - ieee802_1x_notify_port_enabled(sta->eapol_sm, 1); + + if (sta->pending_eapol_rx) { + struct os_reltime now, age; + + os_get_reltime(&now); + os_reltime_sub(&now, &sta->pending_eapol_rx->rx_time, &age); + if (age.sec == 0 && age.usec < 200000) { + wpa_printf(MSG_DEBUG, + "Process pending EAPOL frame that was received from " MACSTR " just before association notification", + MAC2STR(sta->addr)); + ieee802_1x_receive( + hapd, mgmt->da, + wpabuf_head(sta->pending_eapol_rx->buf), + wpabuf_len(sta->pending_eapol_rx->buf)); + } + wpabuf_free(sta->pending_eapol_rx->buf); + os_free(sta->pending_eapol_rx); + sta->pending_eapol_rx = NULL; + } } @@ -2590,7 +2923,7 @@ static void handle_deauth_cb(struct hostapd_data *hapd, size_t len, int ok) { struct sta_info *sta; - if (mgmt->da[0] & 0x01) + if (is_multicast_ether_addr(mgmt->da)) return; sta = ap_get_sta(hapd, mgmt->da); if (!sta) { @@ -2614,7 +2947,7 @@ static void handle_disassoc_cb(struct hostapd_data *hapd, size_t len, int ok) { struct sta_info *sta; - if (mgmt->da[0] & 0x01) + if (is_multicast_ether_addr(mgmt->da)) return; sta = ap_get_sta(hapd, mgmt->da); if (!sta) { @@ -2670,7 +3003,7 @@ void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len, handle_assoc_cb(hapd, mgmt, len, 1, ok); break; case WLAN_FC_STYPE_PROBE_RESP: - wpa_printf(MSG_EXCESSIVE, "mgmt::proberesp cb"); + wpa_printf(MSG_EXCESSIVE, "mgmt::proberesp cb ok=%d", ok); break; case WLAN_FC_STYPE_DEAUTH: wpa_printf(MSG_DEBUG, "mgmt::deauth cb"); @@ -2681,7 +3014,7 @@ void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len, handle_disassoc_cb(hapd, mgmt, len, ok); break; case WLAN_FC_STYPE_ACTION: - wpa_printf(MSG_DEBUG, "mgmt::action cb"); + wpa_printf(MSG_DEBUG, "mgmt::action cb ok=%d", ok); break; default: wpa_printf(MSG_INFO, "unknown mgmt cb frame subtype %d", stype); @@ -2779,6 +3112,8 @@ void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr) } if (sta == NULL) return; + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_POLL_OK MACSTR, + MAC2STR(sta->addr)); if (!(sta->flags & WLAN_STA_PENDING_POLL)) return; @@ -2817,7 +3152,7 @@ void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src, wpa_printf(MSG_DEBUG, "Data/PS-poll frame from not associated STA " MACSTR, MAC2STR(src)); - if (src[0] & 0x01) { + if (is_multicast_ether_addr(src)) { /* Broadcast bit set in SA?! Ignore the frame silently. */ return; } diff --git a/contrib/wpa/src/ap/ieee802_11.h b/contrib/wpa/src/ap/ieee802_11.h index 44c1bff364ac..0327dec2a2bc 100644 --- a/contrib/wpa/src/ap/ieee802_11.h +++ b/contrib/wpa/src/ap/ieee802_11.h @@ -49,9 +49,13 @@ u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid); -u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_secondary_channel(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts); u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid); + int hostapd_ht_operation_update(struct hostapd_iface *iface); void ieee802_11_send_sa_query_req(struct hostapd_data *hapd, const u8 *addr, const u8 *trans_id); @@ -61,6 +65,7 @@ void hostapd_get_ht_capab(struct hostapd_data *hapd, void hostapd_get_vht_capab(struct hostapd_data *hapd, struct ieee80211_vht_capabilities *vht_cap, struct ieee80211_vht_capabilities *neg_vht_cap); +int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta); u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ht_capab); u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta, @@ -97,6 +102,7 @@ int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta); #ifdef CONFIG_SAE void sae_clear_retransmit_timer(struct hostapd_data *hapd, struct sta_info *sta); +void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta); #else /* CONFIG_SAE */ static inline void sae_clear_retransmit_timer(struct hostapd_data *hapd, struct sta_info *sta) @@ -104,4 +110,29 @@ static inline void sae_clear_retransmit_timer(struct hostapd_data *hapd, } #endif /* CONFIG_SAE */ +#ifdef CONFIG_MBO + +u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len); + +u8 hostapd_mbo_ie_len(struct hostapd_data *hapd); + +#else /* CONFIG_MBO */ + +static inline u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, + size_t len) +{ + return eid; +} + +static inline u8 hostapd_mbo_ie_len(struct hostapd_data *hapd) +{ + return 0; +} + +#endif /* CONFIG_MBO */ + +void ap_copy_sta_supp_op_classes(struct sta_info *sta, + const u8 *supp_op_classes, + size_t supp_op_classes_len); + #endif /* IEEE802_11_H */ diff --git a/contrib/wpa/src/ap/ieee802_11_auth.c b/contrib/wpa/src/ap/ieee802_11_auth.c index 531a67da412c..b8905373618d 100644 --- a/contrib/wpa/src/ap/ieee802_11_auth.c +++ b/contrib/wpa/src/ap/ieee802_11_auth.c @@ -15,7 +15,6 @@ #include "utils/common.h" #include "utils/eloop.h" -#include "crypto/sha1.h" #include "radius/radius.h" #include "radius/radius_client.h" #include "hostapd.h" @@ -35,7 +34,7 @@ struct hostapd_cached_radius_acl { struct hostapd_cached_radius_acl *next; u32 session_timeout; u32 acct_interim_interval; - int vlan_id; + struct vlan_description vlan_id; struct hostapd_sta_wpa_psk_short *psk; char *identity; char *radius_cui; @@ -77,29 +76,20 @@ static void hostapd_acl_cache_free(struct hostapd_cached_radius_acl *acl_cache) static void copy_psk_list(struct hostapd_sta_wpa_psk_short **psk, struct hostapd_sta_wpa_psk_short *src) { - struct hostapd_sta_wpa_psk_short **copy_to; - struct hostapd_sta_wpa_psk_short *copy_from; + if (!psk) + return; - /* Copy PSK linked list */ - copy_to = psk; - copy_from = src; - while (copy_from && copy_to) { - *copy_to = os_zalloc(sizeof(struct hostapd_sta_wpa_psk_short)); - if (*copy_to == NULL) - break; - os_memcpy(*copy_to, copy_from, - sizeof(struct hostapd_sta_wpa_psk_short)); - copy_from = copy_from->next; - copy_to = &((*copy_to)->next); - } - if (copy_to) - *copy_to = NULL; + if (src) + src->ref++; + + *psk = src; } static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr, u32 *session_timeout, - u32 *acct_interim_interval, int *vlan_id, + u32 *acct_interim_interval, + struct vlan_description *vlan_id, struct hostapd_sta_wpa_psk_short **psk, char **identity, char **radius_cui) { @@ -165,7 +155,10 @@ static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr, if (msg == NULL) return -1; - radius_msg_make_authenticator(msg, addr, ETH_ALEN); + if (radius_msg_make_authenticator(msg) < 0) { + wpa_printf(MSG_INFO, "Could not make Request Authenticator"); + goto fail; + } os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, MAC2STR(addr)); if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, (u8 *) buf, @@ -212,6 +205,33 @@ static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr, #endif /* CONFIG_NO_RADIUS */ +/** + * hostapd_check_acl - Check a specified STA against accept/deny ACLs + * @hapd: hostapd BSS data + * @addr: MAC address of the STA + * @vlan_id: Buffer for returning VLAN ID + * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING + */ +int hostapd_check_acl(struct hostapd_data *hapd, const u8 *addr, + struct vlan_description *vlan_id) +{ + if (hostapd_maclist_found(hapd->conf->accept_mac, + hapd->conf->num_accept_mac, addr, vlan_id)) + return HOSTAPD_ACL_ACCEPT; + + if (hostapd_maclist_found(hapd->conf->deny_mac, + hapd->conf->num_deny_mac, addr, vlan_id)) + return HOSTAPD_ACL_REJECT; + + if (hapd->conf->macaddr_acl == ACCEPT_UNLESS_DENIED) + return HOSTAPD_ACL_ACCEPT; + if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED) + return HOSTAPD_ACL_REJECT; + + return HOSTAPD_ACL_PENDING; +} + + /** * hostapd_allowed_address - Check whether a specified STA can be authenticated * @hapd: hostapd BSS data @@ -231,16 +251,19 @@ static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr, */ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, const u8 *msg, size_t len, u32 *session_timeout, - u32 *acct_interim_interval, int *vlan_id, + u32 *acct_interim_interval, + struct vlan_description *vlan_id, struct hostapd_sta_wpa_psk_short **psk, char **identity, char **radius_cui) { + int res; + if (session_timeout) *session_timeout = 0; if (acct_interim_interval) *acct_interim_interval = 0; if (vlan_id) - *vlan_id = 0; + os_memset(vlan_id, 0, sizeof(*vlan_id)); if (psk) *psk = NULL; if (identity) @@ -248,18 +271,9 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, if (radius_cui) *radius_cui = NULL; - if (hostapd_maclist_found(hapd->conf->accept_mac, - hapd->conf->num_accept_mac, addr, vlan_id)) - return HOSTAPD_ACL_ACCEPT; - - if (hostapd_maclist_found(hapd->conf->deny_mac, - hapd->conf->num_deny_mac, addr, vlan_id)) - return HOSTAPD_ACL_REJECT; - - if (hapd->conf->macaddr_acl == ACCEPT_UNLESS_DENIED) - return HOSTAPD_ACL_ACCEPT; - if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED) - return HOSTAPD_ACL_REJECT; + res = hostapd_check_acl(hapd, addr, vlan_id); + if (res != HOSTAPD_ACL_PENDING) + return res; if (hapd->conf->macaddr_acl == USE_EXTERNAL_RADIUS_AUTH) { #ifdef CONFIG_NO_RADIUS @@ -268,10 +282,9 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, struct hostapd_acl_query_data *query; /* Check whether ACL cache has an entry for this station */ - int res = hostapd_acl_cache_get(hapd, addr, session_timeout, - acct_interim_interval, - vlan_id, psk, - identity, radius_cui); + res = hostapd_acl_cache_get(hapd, addr, session_timeout, + acct_interim_interval, vlan_id, psk, + identity, radius_cui); if (res == HOSTAPD_ACL_ACCEPT || res == HOSTAPD_ACL_ACCEPT_TIMEOUT) return res; @@ -419,7 +432,7 @@ static void decode_tunnel_passwords(struct hostapd_data *hapd, struct hostapd_cached_radius_acl *cache) { int passphraselen; - char *passphrase, *strpassphrase; + char *passphrase; size_t i; struct hostapd_sta_wpa_psk_short *psk; @@ -436,24 +449,42 @@ static void decode_tunnel_passwords(struct hostapd_data *hapd, */ if (passphrase == NULL) break; + + /* + * Passphase should be 8..63 chars (to be hashed with SSID) + * or 64 chars hex string (no separate hashing with SSID). + */ + + if (passphraselen < MIN_PASSPHRASE_LEN || + passphraselen > MAX_PASSPHRASE_LEN + 1) + goto free_pass; + /* * passphrase does not contain the NULL termination. * Add it here as pbkdf2_sha1() requires it. */ - strpassphrase = os_zalloc(passphraselen + 1); psk = os_zalloc(sizeof(struct hostapd_sta_wpa_psk_short)); - if (strpassphrase && psk) { - os_memcpy(strpassphrase, passphrase, passphraselen); - pbkdf2_sha1(strpassphrase, - hapd->conf->ssid.ssid, - hapd->conf->ssid.ssid_len, 4096, - psk->psk, PMK_LEN); + if (psk) { + if ((passphraselen == MAX_PASSPHRASE_LEN + 1) && + (hexstr2bin(passphrase, psk->psk, PMK_LEN) < 0)) { + hostapd_logger(hapd, cache->addr, + HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_WARNING, + "invalid hex string (%d chars) in Tunnel-Password", + passphraselen); + goto skip; + } else if (passphraselen <= MAX_PASSPHRASE_LEN) { + os_memcpy(psk->passphrase, passphrase, + passphraselen); + psk->is_passphrase = 1; + } psk->next = cache->psk; cache->psk = psk; psk = NULL; } - os_free(strpassphrase); +skip: os_free(psk); +free_pass: os_free(passphrase); } } @@ -478,6 +509,7 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, struct hostapd_acl_query_data *query, *prev; struct hostapd_cached_radius_acl *cache; struct radius_hdr *hdr = radius_msg_get_hdr(msg); + int *untagged, *tagged, *notempty; query = hapd->acl_queries; prev = NULL; @@ -535,7 +567,12 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, cache->acct_interim_interval = 0; } - cache->vlan_id = radius_msg_get_vlanid(msg); + notempty = &cache->vlan_id.notempty; + untagged = &cache->vlan_id.untagged; + tagged = cache->vlan_id.tagged; + *notempty = !!radius_msg_get_vlanid(msg, untagged, + MAX_NUM_TAGGED_VLAN, + tagged); decode_tunnel_passwords(hapd, shared_secret, shared_secret_len, msg, req, cache); @@ -558,17 +595,18 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, !cache->psk) cache->accepted = HOSTAPD_ACL_REJECT; - if (cache->vlan_id && - !hostapd_vlan_id_valid(hapd->conf->vlan, cache->vlan_id)) { + if (cache->vlan_id.notempty && + !hostapd_vlan_valid(hapd->conf->vlan, &cache->vlan_id)) { hostapd_logger(hapd, query->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, - "Invalid VLAN ID %d received from RADIUS server", - cache->vlan_id); - cache->vlan_id = 0; + "Invalid VLAN %d%s received from RADIUS server", + cache->vlan_id.untagged, + cache->vlan_id.tagged[0] ? "+" : ""); + os_memset(&cache->vlan_id, 0, sizeof(cache->vlan_id)); } if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_REQUIRED && - !cache->vlan_id) + !cache->vlan_id.notempty) cache->accepted = HOSTAPD_ACL_REJECT; } else cache->accepted = HOSTAPD_ACL_REJECT; @@ -640,6 +678,12 @@ void hostapd_acl_deinit(struct hostapd_data *hapd) void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk) { + if (psk && psk->ref) { + /* This will be freed when the last reference is dropped. */ + psk->ref--; + return; + } + while (psk) { struct hostapd_sta_wpa_psk_short *prev = psk; psk = psk->next; diff --git a/contrib/wpa/src/ap/ieee802_11_auth.h b/contrib/wpa/src/ap/ieee802_11_auth.h index b66f244b3ebc..71f53b9612fa 100644 --- a/contrib/wpa/src/ap/ieee802_11_auth.h +++ b/contrib/wpa/src/ap/ieee802_11_auth.h @@ -16,9 +16,12 @@ enum { HOSTAPD_ACL_ACCEPT_TIMEOUT = 3 }; +int hostapd_check_acl(struct hostapd_data *hapd, const u8 *addr, + struct vlan_description *vlan_id); int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, const u8 *msg, size_t len, u32 *session_timeout, - u32 *acct_interim_interval, int *vlan_id, + u32 *acct_interim_interval, + struct vlan_description *vlan_id, struct hostapd_sta_wpa_psk_short **psk, char **identity, char **radius_cui); int hostapd_acl_init(struct hostapd_data *hapd); diff --git a/contrib/wpa/src/ap/ieee802_11_ht.c b/contrib/wpa/src/ap/ieee802_11_ht.c index 11fde2a26394..5eb1060a2965 100644 --- a/contrib/wpa/src/ap/ieee802_11_ht.c +++ b/contrib/wpa/src/ap/ieee802_11_ht.c @@ -108,6 +108,29 @@ u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid) } +u8 * hostapd_eid_secondary_channel(struct hostapd_data *hapd, u8 *eid) +{ + u8 sec_ch; + + if (!hapd->cs_freq_params.channel || + !hapd->cs_freq_params.sec_channel_offset) + return eid; + + if (hapd->cs_freq_params.sec_channel_offset == -1) + sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW; + else if (hapd->cs_freq_params.sec_channel_offset == 1) + sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE; + else + return eid; + + *eid++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET; + *eid++ = 1; + *eid++ = sec_ch; + + return eid; +} + + /* op_mode Set to 0 (HT pure) under the followign conditions diff --git a/contrib/wpa/src/ap/ieee802_11_shared.c b/contrib/wpa/src/ap/ieee802_11_shared.c index d462ac8bf9cd..259413bd12ff 100644 --- a/contrib/wpa/src/ap/ieee802_11_shared.c +++ b/contrib/wpa/src/ap/ieee802_11_shared.c @@ -172,6 +172,8 @@ static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx) case 0: /* Bits 0-7 */ if (hapd->iconf->obss_interval) *pos |= 0x01; /* Bit 0 - Coexistence management */ + if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) + *pos |= 0x04; /* Bit 2 - Extended Channel Switching */ break; case 1: /* Bits 8-15 */ if (hapd->conf->proxy_arp) @@ -207,11 +209,21 @@ static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx) if (hapd->conf->hs20) *pos |= 0x40; /* Bit 46 - WNM-Notification */ #endif /* CONFIG_HS20 */ +#ifdef CONFIG_MBO + if (hapd->conf->mbo_enabled) + *pos |= 0x40; /* Bit 46 - WNM-Notification */ +#endif /* CONFIG_MBO */ break; case 6: /* Bits 48-55 */ if (hapd->conf->ssid.utf8_ssid) *pos |= 0x01; /* Bit 48 - UTF-8 SSID */ break; + case 8: /* Bits 64-71 */ + if (hapd->conf->ftm_responder) + *pos |= 0x40; /* Bit 70 - FTM responder */ + if (hapd->conf->ftm_initiator) + *pos |= 0x80; /* Bit 71 - FTM initiator */ + break; } } @@ -231,6 +243,9 @@ u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid) len = 1; if (len < 7 && hapd->conf->ssid.utf8_ssid) len = 7; + if (len < 9 && + (hapd->conf->ftm_initiator || hapd->conf->ftm_responder)) + len = 9; #ifdef CONFIG_WNM if (len < 4) len = 4; @@ -239,6 +254,10 @@ u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid) if (hapd->conf->hs20 && len < 6) len = 6; #endif /* CONFIG_HS20 */ +#ifdef CONFIG_MBO + if (hapd->conf->mbo_enabled && len < 6) + len = 6; +#endif /* CONFIG_MBO */ if (len < hapd->iface->extended_capa_len) len = hapd->iface->extended_capa_len; if (len == 0) @@ -506,3 +525,62 @@ u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid) return pos; } + + +#ifdef CONFIG_MBO + +u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len) +{ + u8 mbo[6], *mbo_pos = mbo; + u8 *pos = eid; + + if (!hapd->conf->mbo_enabled) + return eid; + + *mbo_pos++ = MBO_ATTR_ID_AP_CAPA_IND; + *mbo_pos++ = 1; + /* Not Cellular aware */ + *mbo_pos++ = 0; + + if (hapd->mbo_assoc_disallow) { + *mbo_pos++ = MBO_ATTR_ID_ASSOC_DISALLOW; + *mbo_pos++ = 1; + *mbo_pos++ = hapd->mbo_assoc_disallow; + } + + pos += mbo_add_ie(pos, len, mbo, mbo_pos - mbo); + + return pos; +} + + +u8 hostapd_mbo_ie_len(struct hostapd_data *hapd) +{ + if (!hapd->conf->mbo_enabled) + return 0; + + /* + * MBO IE header (6) + Capability Indication attribute (3) + + * Association Disallowed attribute (3) = 12 + */ + return 6 + 3 + (hapd->mbo_assoc_disallow ? 3 : 0); +} + +#endif /* CONFIG_MBO */ + + +void ap_copy_sta_supp_op_classes(struct sta_info *sta, + const u8 *supp_op_classes, + size_t supp_op_classes_len) +{ + if (!supp_op_classes) + return; + os_free(sta->supp_op_classes); + sta->supp_op_classes = os_malloc(1 + supp_op_classes_len); + if (!sta->supp_op_classes) + return; + + sta->supp_op_classes[0] = supp_op_classes_len; + os_memcpy(sta->supp_op_classes + 1, supp_op_classes, + supp_op_classes_len); +} diff --git a/contrib/wpa/src/ap/ieee802_11_vht.c b/contrib/wpa/src/ap/ieee802_11_vht.c index 5bf1b5d72002..f30f63bc5709 100644 --- a/contrib/wpa/src/ap/ieee802_11_vht.c +++ b/contrib/wpa/src/ap/ieee802_11_vht.c @@ -17,9 +17,10 @@ #include "sta_info.h" #include "beacon.h" #include "ieee802_11.h" +#include "dfs.h" -u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid) +u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts) { struct ieee80211_vht_capabilities *cap; struct hostapd_hw_modes *mode = hapd->iface->current_mode; @@ -49,6 +50,18 @@ u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid) cap->vht_capabilities_info = host_to_le32( hapd->iface->conf->vht_capab); + if (nsts != 0) { + u32 hapd_nsts; + + hapd_nsts = le_to_host32(cap->vht_capabilities_info); + hapd_nsts = (hapd_nsts >> VHT_CAP_BEAMFORMEE_STS_OFFSET) & 7; + cap->vht_capabilities_info &= + ~(host_to_le32(hapd_nsts << + VHT_CAP_BEAMFORMEE_STS_OFFSET)); + cap->vht_capabilities_info |= + host_to_le32(nsts << VHT_CAP_BEAMFORMEE_STS_OFFSET); + } + /* Supported MCS set comes from hw */ os_memcpy(&cap->vht_supported_mcs_set, mode->vht_mcs_set, 8); @@ -80,6 +93,26 @@ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid) hapd->iconf->vht_oper_centr_freq_seg1_idx; oper->vht_op_info_chwidth = hapd->iconf->vht_oper_chwidth; + if (hapd->iconf->vht_oper_chwidth == 2) { + /* + * Convert 160 MHz channel width to new style as interop + * workaround. + */ + oper->vht_op_info_chwidth = 1; + oper->vht_op_info_chan_center_freq_seg1_idx = + oper->vht_op_info_chan_center_freq_seg0_idx; + if (hapd->iconf->channel < + hapd->iconf->vht_oper_centr_freq_seg0_idx) + oper->vht_op_info_chan_center_freq_seg0_idx -= 8; + else + oper->vht_op_info_chan_center_freq_seg0_idx += 8; + } else if (hapd->iconf->vht_oper_chwidth == 3) { + /* + * Convert 80+80 MHz channel width to new style as interop + * workaround. + */ + oper->vht_op_info_chwidth = 1; + } /* VHT Basic MCS set comes from hw */ /* Hard code 1 stream, MCS0-7 is a min Basic VHT MCS rates */ @@ -131,6 +164,171 @@ static int check_valid_vht_mcs(struct hostapd_hw_modes *mode, } +u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid) +{ + u8 bw, chan1, chan2 = 0; + int freq1; + + if (!hapd->cs_freq_params.channel || + !hapd->cs_freq_params.vht_enabled) + return eid; + + /* bandwidth: 0: 40, 1: 80, 2: 160, 3: 80+80 */ + switch (hapd->cs_freq_params.bandwidth) { + case 40: + bw = 0; + break; + case 80: + /* check if it's 80+80 */ + if (!hapd->cs_freq_params.center_freq2) + bw = 1; + else + bw = 3; + break; + case 160: + bw = 2; + break; + default: + /* not valid VHT bandwidth or not in CSA */ + return eid; + } + + freq1 = hapd->cs_freq_params.center_freq1 ? + hapd->cs_freq_params.center_freq1 : + hapd->cs_freq_params.freq; + if (ieee80211_freq_to_chan(freq1, &chan1) != + HOSTAPD_MODE_IEEE80211A) + return eid; + + if (hapd->cs_freq_params.center_freq2 && + ieee80211_freq_to_chan(hapd->cs_freq_params.center_freq2, + &chan2) != HOSTAPD_MODE_IEEE80211A) + return eid; + + *eid++ = WLAN_EID_VHT_CHANNEL_SWITCH_WRAPPER; + *eid++ = 5; /* Length of Channel Switch Wrapper */ + *eid++ = WLAN_EID_VHT_WIDE_BW_CHSWITCH; + *eid++ = 3; /* Length of Wide Bandwidth Channel Switch element */ + *eid++ = bw; /* New Channel Width */ + *eid++ = chan1; /* New Channel Center Frequency Segment 0 */ + *eid++ = chan2; /* New Channel Center Frequency Segment 1 */ + + return eid; +} + + +u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid) +{ + struct hostapd_iface *iface = hapd->iface; + struct hostapd_config *iconf = iface->conf; + struct hostapd_hw_modes *mode = iface->current_mode; + struct hostapd_channel_data *chan; + int dfs, i; + u8 channel, tx_pwr_count, local_pwr_constraint; + int max_tx_power; + u8 tx_pwr; + + if (!mode) + return eid; + + if (ieee80211_freq_to_chan(iface->freq, &channel) == NUM_HOSTAPD_MODES) + return eid; + + for (i = 0; i < mode->num_channels; i++) { + if (mode->channels[i].freq == iface->freq) + break; + } + if (i == mode->num_channels) + return eid; + + switch (iface->conf->vht_oper_chwidth) { + case VHT_CHANWIDTH_USE_HT: + if (iconf->secondary_channel == 0) { + /* Max Transmit Power count = 0 (20 MHz) */ + tx_pwr_count = 0; + } else { + /* Max Transmit Power count = 1 (20, 40 MHz) */ + tx_pwr_count = 1; + } + break; + case VHT_CHANWIDTH_80MHZ: + /* Max Transmit Power count = 2 (20, 40, and 80 MHz) */ + tx_pwr_count = 2; + break; + case VHT_CHANWIDTH_80P80MHZ: + case VHT_CHANWIDTH_160MHZ: + /* Max Transmit Power count = 3 (20, 40, 80, 160/80+80 MHz) */ + tx_pwr_count = 3; + break; + default: + return eid; + } + + /* + * Below local_pwr_constraint logic is referred from + * hostapd_eid_pwr_constraint. + * + * Check if DFS is required by regulatory. + */ + dfs = hostapd_is_dfs_required(hapd->iface); + if (dfs < 0) + dfs = 0; + + /* + * In order to meet regulations when TPC is not implemented using + * a transmit power that is below the legal maximum (including any + * mitigation factor) should help. In this case, indicate 3 dB below + * maximum allowed transmit power. + */ + if (hapd->iconf->local_pwr_constraint == -1) + local_pwr_constraint = (dfs == 0) ? 0 : 3; + else + local_pwr_constraint = hapd->iconf->local_pwr_constraint; + + /* + * A STA that is not an AP shall use a transmit power less than or + * equal to the local maximum transmit power level for the channel. + * The local maximum transmit power can be calculated from the formula: + * local max TX pwr = max TX pwr - local pwr constraint + * Where max TX pwr is maximum transmit power level specified for + * channel in Country element and local pwr constraint is specified + * for channel in this Power Constraint element. + */ + chan = &mode->channels[i]; + max_tx_power = chan->max_tx_power - local_pwr_constraint; + + /* + * Local Maximum Transmit power is encoded as two's complement + * with a 0.5 dB step. + */ + max_tx_power *= 2; /* in 0.5 dB steps */ + if (max_tx_power > 127) { + /* 63.5 has special meaning of 63.5 dBm or higher */ + max_tx_power = 127; + } + if (max_tx_power < -128) + max_tx_power = -128; + if (max_tx_power < 0) + tx_pwr = 0x80 + max_tx_power + 128; + else + tx_pwr = max_tx_power; + + *eid++ = WLAN_EID_VHT_TRANSMIT_POWER_ENVELOPE; + *eid++ = 2 + tx_pwr_count; + + /* + * Max Transmit Power count and + * Max Transmit Power units = 0 (EIRP) + */ + *eid++ = tx_pwr_count; + + for (i = 0; i <= tx_pwr_count; i++) + *eid++ = tx_pwr; + + return eid; +} + + u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta, const u8 *vht_capab) { @@ -212,7 +410,7 @@ u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid) WPA_PUT_BE32(pos, (OUI_BROADCOM << 8) | VENDOR_VHT_TYPE); pos += 4; *pos++ = VENDOR_VHT_SUBTYPE; - pos = hostapd_eid_vht_capabilities(hapd, pos); + pos = hostapd_eid_vht_capabilities(hapd, pos, 0); pos = hostapd_eid_vht_operation(hapd, pos); return pos; diff --git a/contrib/wpa/src/ap/ieee802_1x.c b/contrib/wpa/src/ap/ieee802_1x.c index 0f2d428cf752..80ff996948f9 100644 --- a/contrib/wpa/src/ap/ieee802_1x.c +++ b/contrib/wpa/src/ap/ieee802_1x.c @@ -34,6 +34,9 @@ #include "ieee802_1x.h" +#ifdef CONFIG_HS20 +static void ieee802_1x_wnm_notif_send(void *eloop_ctx, void *timeout_ctx); +#endif /* CONFIG_HS20 */ static void ieee802_1x_finished(struct hostapd_data *hapd, struct sta_info *sta, int success, int remediation); @@ -219,7 +222,7 @@ static void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) MAC2STR(sta->addr)); #ifndef CONFIG_NO_VLAN - if (sta->vlan_id > 0 && sta->vlan_id <= MAX_VLAN_ID) { + if (sta->vlan_id > 0) { wpa_printf(MSG_ERROR, "Using WEP with vlans is not supported."); return; } @@ -401,8 +404,17 @@ static int add_common_radius_sta_attr(struct hostapd_data *hapd, { char buf[128]; + if (!hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_SERVICE_TYPE) && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_SERVICE_TYPE, + RADIUS_SERVICE_TYPE_FRAMED)) { + wpa_printf(MSG_ERROR, "Could not add Service-Type"); + return -1; + } + if (!hostapd_config_get_radius_attr(req_attr, RADIUS_ATTR_NAS_PORT) && + sta->aid > 0 && !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) { wpa_printf(MSG_ERROR, "Could not add NAS-Port"); return -1; @@ -435,9 +447,9 @@ static int add_common_radius_sta_attr(struct hostapd_data *hapd, return -1; } - if (sta->acct_session_id_hi || sta->acct_session_id_lo) { - os_snprintf(buf, sizeof(buf), "%08X-%08X", - sta->acct_session_id_hi, sta->acct_session_id_lo); + if (sta->acct_session_id) { + os_snprintf(buf, sizeof(buf), "%016llX", + (unsigned long long) sta->acct_session_id); if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID, (u8 *) buf, os_strlen(buf))) { wpa_printf(MSG_ERROR, "Could not add Acct-Session-Id"); @@ -445,6 +457,21 @@ static int add_common_radius_sta_attr(struct hostapd_data *hapd, } } + if ((hapd->conf->wpa & 2) && + !hapd->conf->disable_pmksa_caching && + sta->eapol_sm && sta->eapol_sm->acct_multi_session_id) { + os_snprintf(buf, sizeof(buf), "%016llX", + (unsigned long long) + sta->eapol_sm->acct_multi_session_id); + if (!radius_msg_add_attr( + msg, RADIUS_ATTR_ACCT_MULTI_SESSION_ID, + (u8 *) buf, os_strlen(buf))) { + wpa_printf(MSG_INFO, + "Could not add Acct-Multi-Session-Id"); + return -1; + } + } + #ifdef CONFIG_IEEE80211R if (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) && sta->wpa_sm && @@ -475,6 +502,7 @@ int add_common_radius_attr(struct hostapd_data *hapd, { char buf[128]; struct hostapd_radius_attr *attr; + int len; if (!hostapd_config_get_radius_attr(req_attr, RADIUS_ATTR_NAS_IP_ADDRESS) && @@ -506,15 +534,15 @@ int add_common_radius_attr(struct hostapd_data *hapd, return -1; } - os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", - MAC2STR(hapd->own_addr), - wpa_ssid_txt(hapd->conf->ssid.ssid, - hapd->conf->ssid.ssid_len)); - buf[sizeof(buf) - 1] = '\0'; + len = os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":", + MAC2STR(hapd->own_addr)); + os_memcpy(&buf[len], hapd->conf->ssid.ssid, + hapd->conf->ssid.ssid_len); + len += hapd->conf->ssid.ssid_len; if (!hostapd_config_get_radius_attr(req_attr, RADIUS_ATTR_CALLED_STATION_ID) && !radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID, - (u8 *) buf, os_strlen(buf))) { + (u8 *) buf, len)) { wpa_printf(MSG_ERROR, "Could not add Called-Station-Id"); return -1; } @@ -583,7 +611,10 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, return; } - radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta)); + if (radius_msg_make_authenticator(msg) < 0) { + wpa_printf(MSG_INFO, "Could not make Request Authenticator"); + goto fail; + } if (sm->identity && !radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, @@ -831,6 +862,29 @@ ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta) } +static void ieee802_1x_save_eapol(struct sta_info *sta, const u8 *buf, + size_t len) +{ + if (sta->pending_eapol_rx) { + wpabuf_free(sta->pending_eapol_rx->buf); + } else { + sta->pending_eapol_rx = + os_malloc(sizeof(*sta->pending_eapol_rx)); + if (!sta->pending_eapol_rx) + return; + } + + sta->pending_eapol_rx->buf = wpabuf_alloc_copy(buf, len); + if (!sta->pending_eapol_rx->buf) { + os_free(sta->pending_eapol_rx); + sta->pending_eapol_rx = NULL; + return; + } + + os_get_reltime(&sta->pending_eapol_rx->rx_time); +} + + /** * ieee802_1x_receive - Process the EAPOL frames from the Supplicant * @hapd: hostapd BSS data @@ -861,6 +915,13 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED))) { wpa_printf(MSG_DEBUG, "IEEE 802.1X data frame from not " "associated/Pre-authenticating STA"); + + if (sta && (sta->flags & WLAN_STA_AUTH)) { + wpa_printf(MSG_DEBUG, "Saving EAPOL frame from " MACSTR + " for later use", MAC2STR(sta->addr)); + ieee802_1x_save_eapol(sta, buf, len); + } + return; } @@ -1047,7 +1108,7 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) * Clear any possible EAPOL authenticator state to support * reassociation change from WPS to PSK. */ - ieee802_1x_free_station(sta); + ieee802_1x_free_station(hapd, sta); return; } @@ -1058,7 +1119,7 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) * Clear any possible EAPOL authenticator state to support * reassociation change from WPA-EAP to PSK. */ - ieee802_1x_free_station(sta); + ieee802_1x_free_station(hapd, sta); return; } @@ -1106,6 +1167,7 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS; sta->eapol_sm->authSuccess = TRUE; sta->eapol_sm->authFail = FALSE; + sta->eapol_sm->portValid = TRUE; if (sta->eapol_sm->eap) eap_sm_notify_cached(sta->eapol_sm->eap); /* TODO: get vlan_id from R0KH using RRB message */ @@ -1128,7 +1190,7 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) sta->eapol_sm->authFail = FALSE; if (sta->eapol_sm->eap) eap_sm_notify_cached(sta->eapol_sm->eap); - pmksa_cache_to_eapol_data(pmksa, sta->eapol_sm); + pmksa_cache_to_eapol_data(hapd, pmksa, sta->eapol_sm); ap_sta_bind_vlan(hapd, sta); } else { if (reassoc) { @@ -1144,10 +1206,20 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) } -void ieee802_1x_free_station(struct sta_info *sta) +void ieee802_1x_free_station(struct hostapd_data *hapd, struct sta_info *sta) { struct eapol_state_machine *sm = sta->eapol_sm; +#ifdef CONFIG_HS20 + eloop_cancel_timeout(ieee802_1x_wnm_notif_send, hapd, sta); +#endif /* CONFIG_HS20 */ + + if (sta->pending_eapol_rx) { + wpabuf_free(sta->pending_eapol_rx->buf); + os_free(sta->pending_eapol_rx); + sta->pending_eapol_rx = NULL; + } + if (sm == NULL) return; @@ -1156,10 +1228,8 @@ void ieee802_1x_free_station(struct sta_info *sta) #ifndef CONFIG_NO_RADIUS radius_msg_free(sm->last_recv_radius); radius_free_class(&sm->radius_class); - wpabuf_free(sm->radius_cui); #endif /* CONFIG_NO_RADIUS */ - os_free(sm->identity); eapol_auth_free(sm); } @@ -1592,10 +1662,16 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, struct hostapd_data *hapd = data; struct sta_info *sta; u32 session_timeout = 0, termination_action, acct_interim_interval; - int session_timeout_set, vlan_id = 0; + int session_timeout_set; struct eapol_state_machine *sm; int override_eapReq = 0; struct radius_hdr *hdr = radius_msg_get_hdr(msg); + struct vlan_description vlan_desc; +#ifndef CONFIG_NO_VLAN + int *untagged, *tagged, *notempty; +#endif /* CONFIG_NO_VLAN */ + + os_memset(&vlan_desc, 0, sizeof(vlan_desc)); sm = ieee802_1x_search_radius_identifier(hapd, hdr->identifier); if (sm == NULL) { @@ -1659,27 +1735,32 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, switch (hdr->code) { case RADIUS_CODE_ACCESS_ACCEPT: - if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED) - vlan_id = 0; #ifndef CONFIG_NO_VLAN - else - vlan_id = radius_msg_get_vlanid(msg); - if (vlan_id > 0 && - hostapd_vlan_id_valid(hapd->conf->vlan, vlan_id)) { - hostapd_logger(hapd, sta->addr, - HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_INFO, - "VLAN ID %d", vlan_id); - } else if (vlan_id > 0) { + if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED) { + notempty = &vlan_desc.notempty; + untagged = &vlan_desc.untagged; + tagged = vlan_desc.tagged; + *notempty = !!radius_msg_get_vlanid(msg, untagged, + MAX_NUM_TAGGED_VLAN, + tagged); + } + + if (vlan_desc.notempty && + !hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) { sta->eapol_sm->authFail = TRUE; hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, - "Invalid VLAN ID %d received from RADIUS server", - vlan_id); + "Invalid VLAN %d%s received from RADIUS server", + vlan_desc.untagged, + vlan_desc.tagged[0] ? "+" : ""); + os_memset(&vlan_desc, 0, sizeof(vlan_desc)); + ap_sta_set_vlan(hapd, sta, &vlan_desc); break; - } else if (hapd->conf->ssid.dynamic_vlan == - DYNAMIC_VLAN_REQUIRED) { + } + + if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_REQUIRED && + !vlan_desc.notempty) { sta->eapol_sm->authFail = TRUE; hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, @@ -1690,7 +1771,18 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, } #endif /* CONFIG_NO_VLAN */ - sta->vlan_id = vlan_id; + if (ap_sta_set_vlan(hapd, sta, &vlan_desc) < 0) + break; + +#ifndef CONFIG_NO_VLAN + if (sta->vlan_id > 0) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, + "VLAN ID %d", sta->vlan_id); + } +#endif /* CONFIG_NO_VLAN */ + if ((sta->flags & WLAN_STA_ASSOC) && ap_sta_bind_vlan(hapd, sta) < 0) break; @@ -1715,15 +1807,6 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, ieee802_1x_check_hs20(hapd, sta, msg, session_timeout_set ? (int) session_timeout : -1); - if (sm->eap_if->eapKeyAvailable && !sta->remediation && - !sta->hs20_deauth_requested && - wpa_auth_pmksa_add(sta->wpa_sm, sm->eapol_key_crypt, - session_timeout_set ? - (int) session_timeout : -1, sm) == 0) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, - "Added PMKSA cache entry"); - } break; case RADIUS_CODE_ACCESS_REJECT: sm->eap_if->aaaFail = TRUE; @@ -2190,7 +2273,7 @@ void ieee802_1x_deinit(struct hostapd_data *hapd) { eloop_cancel_timeout(ieee802_1x_rekey, hapd, NULL); - if (hapd->driver != NULL && + if (hapd->driver && hapd->drv_priv && (hapd->conf->ieee802_1x || hapd->conf->wpa)) hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0); @@ -2495,12 +2578,12 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, /* TODO: dot1xAuthSessionOctetsTx */ /* TODO: dot1xAuthSessionFramesRx */ /* TODO: dot1xAuthSessionFramesTx */ - "dot1xAuthSessionId=%08X-%08X\n" + "dot1xAuthSessionId=%016llX\n" "dot1xAuthSessionAuthenticMethod=%d\n" "dot1xAuthSessionTime=%u\n" "dot1xAuthSessionTerminateCause=999\n" "dot1xAuthSessionUserName=%s\n", - sta->acct_session_id_hi, sta->acct_session_id_lo, + (unsigned long long) sta->acct_session_id, (wpa_key_mgmt_wpa_ieee8021x( wpa_auth_sta_key_mgmt(sta->wpa_sm))) ? 1 : 2, @@ -2510,11 +2593,11 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, return len; len += ret; - if (sm->acct_multi_session_id_hi) { + if (sm->acct_multi_session_id) { ret = os_snprintf(buf + len, buflen - len, - "authMultiSessionId=%08X+%08X\n", - sm->acct_multi_session_id_hi, - sm->acct_multi_session_id_lo); + "authMultiSessionId=%016llX\n", + (unsigned long long) + sm->acct_multi_session_id); if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -2535,6 +2618,34 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, } +#ifdef CONFIG_HS20 +static void ieee802_1x_wnm_notif_send(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + + if (sta->remediation) { + wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to " + MACSTR " to indicate Subscription Remediation", + MAC2STR(sta->addr)); + hs20_send_wnm_notification(hapd, sta->addr, + sta->remediation_method, + sta->remediation_url); + os_free(sta->remediation_url); + sta->remediation_url = NULL; + } + + if (sta->hs20_deauth_req) { + wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to " + MACSTR " to indicate imminent deauthentication", + MAC2STR(sta->addr)); + hs20_send_wnm_notification_deauth_req(hapd, sta->addr, + sta->hs20_deauth_req); + } +} +#endif /* CONFIG_HS20 */ + + static void ieee802_1x_finished(struct hostapd_data *hapd, struct sta_info *sta, int success, int remediation) @@ -2554,26 +2665,12 @@ static void ieee802_1x_finished(struct hostapd_data *hapd, sta->remediation_method = 1; /* SOAP-XML SPP */ } - if (success) { - if (sta->remediation) { - wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification " - "to " MACSTR " to indicate Subscription " - "Remediation", - MAC2STR(sta->addr)); - hs20_send_wnm_notification(hapd, sta->addr, - sta->remediation_method, - sta->remediation_url); - os_free(sta->remediation_url); - sta->remediation_url = NULL; - } - - if (sta->hs20_deauth_req) { - wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification " - "to " MACSTR " to indicate imminent " - "deauthentication", MAC2STR(sta->addr)); - hs20_send_wnm_notification_deauth_req( - hapd, sta->addr, sta->hs20_deauth_req); - } + if (success && (sta->remediation || sta->hs20_deauth_req)) { + wpa_printf(MSG_DEBUG, "HS 2.0: Schedule WNM-Notification to " + MACSTR " in 100 ms", MAC2STR(sta->addr)); + eloop_cancel_timeout(ieee802_1x_wnm_notif_send, hapd, sta); + eloop_register_timeout(0, 100000, ieee802_1x_wnm_notif_send, + hapd, sta); } #endif /* CONFIG_HS20 */ @@ -2584,7 +2681,7 @@ static void ieee802_1x_finished(struct hostapd_data *hapd, session_timeout = dot11RSNAConfigPMKLifetime; if (success && key && len >= PMK_LEN && !sta->remediation && !sta->hs20_deauth_requested && - wpa_auth_pmksa_add(sta->wpa_sm, key, session_timeout, + wpa_auth_pmksa_add(sta->wpa_sm, key, len, session_timeout, sta->eapol_sm) == 0) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, HOSTAPD_LEVEL_DEBUG, diff --git a/contrib/wpa/src/ap/ieee802_1x.h b/contrib/wpa/src/ap/ieee802_1x.h index 14d69556993c..ec80199007b6 100644 --- a/contrib/wpa/src/ap/ieee802_1x.h +++ b/contrib/wpa/src/ap/ieee802_1x.h @@ -21,7 +21,7 @@ struct radius_msg; void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, size_t len); void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta); -void ieee802_1x_free_station(struct sta_info *sta); +void ieee802_1x_free_station(struct hostapd_data *hapd, struct sta_info *sta); void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta); void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd, diff --git a/contrib/wpa/src/ap/mbo_ap.c b/contrib/wpa/src/ap/mbo_ap.c new file mode 100644 index 000000000000..43b0bf16934e --- /dev/null +++ b/contrib/wpa/src/ap/mbo_ap.c @@ -0,0 +1,244 @@ +/* + * hostapd - MBO + * Copyright (c) 2016, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "hostapd.h" +#include "sta_info.h" +#include "mbo_ap.h" + + +void mbo_ap_sta_free(struct sta_info *sta) +{ + struct mbo_non_pref_chan_info *info, *prev; + + info = sta->non_pref_chan; + sta->non_pref_chan = NULL; + while (info) { + prev = info; + info = info->next; + os_free(prev); + } +} + + +static void mbo_ap_parse_non_pref_chan(struct sta_info *sta, + const u8 *buf, size_t len) +{ + struct mbo_non_pref_chan_info *info, *tmp; + char channels[200], *pos, *end; + size_t num_chan, i; + int ret; + + if (len <= 3) + return; /* Not enough room for any channels */ + + num_chan = len - 3; + info = os_zalloc(sizeof(*info) + num_chan); + if (!info) + return; + info->op_class = buf[0]; + info->pref = buf[len - 2]; + info->reason_code = buf[len - 1]; + info->num_channels = num_chan; + buf++; + os_memcpy(info->channels, buf, num_chan); + if (!sta->non_pref_chan) { + sta->non_pref_chan = info; + } else { + tmp = sta->non_pref_chan; + while (tmp->next) + tmp = tmp->next; + tmp->next = info; + } + + pos = channels; + end = pos + sizeof(channels); + *pos = '\0'; + for (i = 0; i < num_chan; i++) { + ret = os_snprintf(pos, end - pos, "%s%u", + i == 0 ? "" : " ", buf[i]); + if (os_snprintf_error(end - pos, ret)) { + *pos = '\0'; + break; + } + pos += ret; + } + + wpa_printf(MSG_DEBUG, "MBO: STA " MACSTR + " non-preferred channel list (op class %u, pref %u, reason code %u, channels %s)", + MAC2STR(sta->addr), info->op_class, info->pref, + info->reason_code, channels); +} + + +void mbo_ap_check_sta_assoc(struct hostapd_data *hapd, struct sta_info *sta, + struct ieee802_11_elems *elems) +{ + const u8 *pos, *attr, *end; + size_t len; + + if (!hapd->conf->mbo_enabled || !elems->mbo) + return; + + pos = elems->mbo + 4; + len = elems->mbo_len - 4; + wpa_hexdump(MSG_DEBUG, "MBO: Association Request attributes", pos, len); + + attr = get_ie(pos, len, MBO_ATTR_ID_CELL_DATA_CAPA); + if (attr && attr[1] >= 1) + sta->cell_capa = attr[2]; + + mbo_ap_sta_free(sta); + end = pos + len; + while (end - pos > 1) { + u8 ie_len = pos[1]; + + if (2 + ie_len > end - pos) + break; + + if (pos[0] == MBO_ATTR_ID_NON_PREF_CHAN_REPORT) + mbo_ap_parse_non_pref_chan(sta, pos + 2, ie_len); + pos += 2 + pos[1]; + } +} + + +int mbo_ap_get_info(struct sta_info *sta, char *buf, size_t buflen) +{ + char *pos = buf, *end = buf + buflen; + int ret; + struct mbo_non_pref_chan_info *info; + u8 i; + unsigned int count = 0; + + if (!sta->cell_capa) + return 0; + + ret = os_snprintf(pos, end - pos, "mbo_cell_capa=%u\n", sta->cell_capa); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + + for (info = sta->non_pref_chan; info; info = info->next) { + char *pos2 = pos; + + ret = os_snprintf(pos2, end - pos2, + "non_pref_chan[%u]=%u:%u:%u:", + count, info->op_class, info->pref, + info->reason_code); + count++; + if (os_snprintf_error(end - pos2, ret)) + break; + pos2 += ret; + + for (i = 0; i < info->num_channels; i++) { + ret = os_snprintf(pos2, end - pos2, "%u%s", + info->channels[i], + i + 1 < info->num_channels ? + "," : ""); + if (os_snprintf_error(end - pos2, ret)) { + pos2 = NULL; + break; + } + pos2 += ret; + } + + if (!pos2) + break; + ret = os_snprintf(pos2, end - pos2, "\n"); + if (os_snprintf_error(end - pos2, ret)) + break; + pos2 += ret; + pos = pos2; + } + + return pos - buf; +} + + +static void mbo_ap_wnm_notif_req_cell_capa(struct sta_info *sta, + const u8 *buf, size_t len) +{ + if (len < 1) + return; + wpa_printf(MSG_DEBUG, "MBO: STA " MACSTR + " updated cellular data capability: %u", + MAC2STR(sta->addr), buf[0]); + sta->cell_capa = buf[0]; +} + + +static void mbo_ap_wnm_notif_req_elem(struct sta_info *sta, u8 type, + const u8 *buf, size_t len, + int *first_non_pref_chan) +{ + switch (type) { + case WFA_WNM_NOTIF_SUBELEM_NON_PREF_CHAN_REPORT: + if (*first_non_pref_chan) { + /* + * Need to free the previously stored entries now to + * allow the update to replace all entries. + */ + *first_non_pref_chan = 0; + mbo_ap_sta_free(sta); + } + mbo_ap_parse_non_pref_chan(sta, buf, len); + break; + case WFA_WNM_NOTIF_SUBELEM_CELL_DATA_CAPA: + mbo_ap_wnm_notif_req_cell_capa(sta, buf, len); + break; + default: + wpa_printf(MSG_DEBUG, + "MBO: Ignore unknown WNM Notification WFA subelement %u", + type); + break; + } +} + + +void mbo_ap_wnm_notification_req(struct hostapd_data *hapd, const u8 *addr, + const u8 *buf, size_t len) +{ + const u8 *pos, *end; + u8 ie_len; + struct sta_info *sta; + int first_non_pref_chan = 1; + + if (!hapd->conf->mbo_enabled) + return; + + sta = ap_get_sta(hapd, addr); + if (!sta) + return; + + pos = buf; + end = buf + len; + + while (end - pos > 1) { + ie_len = pos[1]; + + if (2 + ie_len > end - pos) + break; + + if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && + ie_len >= 4 && WPA_GET_BE24(pos + 2) == OUI_WFA) + mbo_ap_wnm_notif_req_elem(sta, pos[5], + pos + 6, ie_len - 4, + &first_non_pref_chan); + else + wpa_printf(MSG_DEBUG, + "MBO: Ignore unknown WNM Notification element %u (len=%u)", + pos[0], pos[1]); + + pos += 2 + pos[1]; + } +} diff --git a/contrib/wpa/src/ap/mbo_ap.h b/contrib/wpa/src/ap/mbo_ap.h new file mode 100644 index 000000000000..9f37f2802f3b --- /dev/null +++ b/contrib/wpa/src/ap/mbo_ap.h @@ -0,0 +1,51 @@ +/* + * MBO related functions and structures + * Copyright (c) 2016, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef MBO_AP_H +#define MBO_AP_H + +struct hostapd_data; +struct sta_info; +struct ieee802_11_elems; + +#ifdef CONFIG_MBO + +void mbo_ap_check_sta_assoc(struct hostapd_data *hapd, struct sta_info *sta, + struct ieee802_11_elems *elems); +int mbo_ap_get_info(struct sta_info *sta, char *buf, size_t buflen); +void mbo_ap_wnm_notification_req(struct hostapd_data *hapd, const u8 *addr, + const u8 *buf, size_t len); +void mbo_ap_sta_free(struct sta_info *sta); + +#else /* CONFIG_MBO */ + +static inline void mbo_ap_check_sta_assoc(struct hostapd_data *hapd, + struct sta_info *sta, + struct ieee802_11_elems *elems) +{ +} + +static inline int mbo_ap_get_info(struct sta_info *sta, char *buf, + size_t buflen) +{ + return 0; +} + +static inline void mbo_ap_wnm_notification_req(struct hostapd_data *hapd, + const u8 *addr, + const u8 *buf, size_t len) +{ +} + +static inline void mbo_ap_sta_free(struct sta_info *sta) +{ +} + +#endif /* CONFIG_MBO */ + +#endif /* MBO_AP_H */ diff --git a/contrib/wpa/src/ap/ndisc_snoop.c b/contrib/wpa/src/ap/ndisc_snoop.c index 4a87721e2ecf..3c086bfc7131 100644 --- a/contrib/wpa/src/ap/ndisc_snoop.c +++ b/contrib/wpa/src/ap/ndisc_snoop.c @@ -17,6 +17,7 @@ #include "ap_drv_ops.h" #include "list.h" #include "x_snoop.h" +#include "ndisc_snoop.h" struct ip6addr { struct in6_addr addr; diff --git a/contrib/wpa/src/ap/neighbor_db.c b/contrib/wpa/src/ap/neighbor_db.c new file mode 100644 index 000000000000..a2efff618286 --- /dev/null +++ b/contrib/wpa/src/ap/neighbor_db.c @@ -0,0 +1,133 @@ +/* + * hostapd / Neighboring APs DB + * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH. + * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "hostapd.h" +#include "neighbor_db.h" + + +struct hostapd_neighbor_entry * +hostapd_neighbor_get(struct hostapd_data *hapd, const u8 *bssid, + const struct wpa_ssid_value *ssid) +{ + struct hostapd_neighbor_entry *nr; + + dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry, + list) { + if (os_memcmp(bssid, nr->bssid, ETH_ALEN) == 0 && + (!ssid || + (ssid->ssid_len == nr->ssid.ssid_len && + os_memcmp(ssid->ssid, nr->ssid.ssid, + ssid->ssid_len) == 0))) + return nr; + } + return NULL; +} + + +static void hostapd_neighbor_clear_entry(struct hostapd_neighbor_entry *nr) +{ + wpabuf_free(nr->nr); + nr->nr = NULL; + wpabuf_free(nr->lci); + nr->lci = NULL; + wpabuf_free(nr->civic); + nr->civic = NULL; + os_memset(nr->bssid, 0, sizeof(nr->bssid)); + os_memset(&nr->ssid, 0, sizeof(nr->ssid)); +} + + +static struct hostapd_neighbor_entry * +hostapd_neighbor_add(struct hostapd_data *hapd) +{ + struct hostapd_neighbor_entry *nr; + + nr = os_zalloc(sizeof(struct hostapd_neighbor_entry)); + if (!nr) + return NULL; + + dl_list_add(&hapd->nr_db, &nr->list); + + return nr; +} + + +int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid, + const struct wpa_ssid_value *ssid, + const struct wpabuf *nr, const struct wpabuf *lci, + const struct wpabuf *civic) +{ + struct hostapd_neighbor_entry *entry; + + entry = hostapd_neighbor_get(hapd, bssid, ssid); + if (!entry) + entry = hostapd_neighbor_add(hapd); + if (!entry) + return -1; + + hostapd_neighbor_clear_entry(entry); + + os_memcpy(entry->bssid, bssid, ETH_ALEN); + os_memcpy(&entry->ssid, ssid, sizeof(entry->ssid)); + + entry->nr = wpabuf_dup(nr); + if (!entry->nr) + goto fail; + + if (lci) { + entry->lci = wpabuf_dup(lci); + if (!entry->lci || os_get_time(&entry->lci_date)) + goto fail; + } + + if (civic) { + entry->civic = wpabuf_dup(civic); + if (!entry->civic) + goto fail; + } + + return 0; + +fail: + hostapd_neighbor_remove(hapd, bssid, ssid); + return -1; +} + + +int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid, + const struct wpa_ssid_value *ssid) +{ + struct hostapd_neighbor_entry *nr; + + nr = hostapd_neighbor_get(hapd, bssid, ssid); + if (!nr) + return -1; + + hostapd_neighbor_clear_entry(nr); + dl_list_del(&nr->list); + os_free(nr); + + return 0; +} + + +void hostpad_free_neighbor_db(struct hostapd_data *hapd) +{ + struct hostapd_neighbor_entry *nr, *prev; + + dl_list_for_each_safe(nr, prev, &hapd->nr_db, + struct hostapd_neighbor_entry, list) { + hostapd_neighbor_clear_entry(nr); + dl_list_del(&nr->list); + os_free(nr); + } +} diff --git a/contrib/wpa/src/ap/neighbor_db.h b/contrib/wpa/src/ap/neighbor_db.h new file mode 100644 index 000000000000..c22e043c120e --- /dev/null +++ b/contrib/wpa/src/ap/neighbor_db.h @@ -0,0 +1,24 @@ +/* + * hostapd / Neighboring APs DB + * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH. + * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef NEIGHBOR_DB_H +#define NEIGHBOR_DB_H + +struct hostapd_neighbor_entry * +hostapd_neighbor_get(struct hostapd_data *hapd, const u8 *bssid, + const struct wpa_ssid_value *ssid); +int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid, + const struct wpa_ssid_value *ssid, + const struct wpabuf *nr, const struct wpabuf *lci, + const struct wpabuf *civic); +int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid, + const struct wpa_ssid_value *ssid); +void hostpad_free_neighbor_db(struct hostapd_data *hapd); + +#endif /* NEIGHBOR_DB_H */ diff --git a/contrib/wpa/src/ap/pmksa_cache_auth.c b/contrib/wpa/src/ap/pmksa_cache_auth.c index 877affe4eadc..d610e7e5b005 100644 --- a/contrib/wpa/src/ap/pmksa_cache_auth.c +++ b/contrib/wpa/src/ap/pmksa_cache_auth.c @@ -38,6 +38,7 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa); static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry) { + os_free(entry->vlan_desc); os_free(entry->identity); wpabuf_free(entry->cui); #ifndef CONFIG_NO_RADIUS @@ -91,6 +92,20 @@ void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, } +/** + * pmksa_cache_auth_flush - Flush all PMKSA cache entries + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() + */ +void pmksa_cache_auth_flush(struct rsn_pmksa_cache *pmksa) +{ + while (pmksa->pmksa) { + wpa_printf(MSG_DEBUG, "RSN: Flush PMKSA cache entry for " + MACSTR, MAC2STR(pmksa->pmksa->spa)); + pmksa_cache_free_entry(pmksa, pmksa->pmksa); + } +} + + static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx) { struct rsn_pmksa_cache *pmksa = eloop_ctx; @@ -126,6 +141,8 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry, struct eapol_state_machine *eapol) { + struct vlan_description *vlan_desc; + if (eapol == NULL) return; @@ -146,14 +163,22 @@ static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry, #endif /* CONFIG_NO_RADIUS */ entry->eap_type_authsrv = eapol->eap_type_authsrv; - entry->vlan_id = ((struct sta_info *) eapol->sta)->vlan_id; - entry->acct_multi_session_id_hi = eapol->acct_multi_session_id_hi; - entry->acct_multi_session_id_lo = eapol->acct_multi_session_id_lo; + vlan_desc = ((struct sta_info *) eapol->sta)->vlan_desc; + if (vlan_desc && vlan_desc->notempty) { + entry->vlan_desc = os_zalloc(sizeof(struct vlan_description)); + if (entry->vlan_desc) + *entry->vlan_desc = *vlan_desc; + } else { + entry->vlan_desc = NULL; + } + + entry->acct_multi_session_id = eapol->acct_multi_session_id; } -void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry, +void pmksa_cache_to_eapol_data(struct hostapd_data *hapd, + struct rsn_pmksa_cache_entry *entry, struct eapol_state_machine *eapol) { if (entry == NULL || eapol == NULL) @@ -186,10 +211,11 @@ void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry, } eapol->eap_type_authsrv = entry->eap_type_authsrv; - ((struct sta_info *) eapol->sta)->vlan_id = entry->vlan_id; +#ifndef CONFIG_NO_VLAN + ap_sta_set_vlan(hapd, eapol->sta, entry->vlan_desc); +#endif /* CONFIG_NO_VLAN */ - eapol->acct_multi_session_id_hi = entry->acct_multi_session_id_hi; - eapol->acct_multi_session_id_lo = entry->acct_multi_session_id_lo; + eapol->acct_multi_session_id = entry->acct_multi_session_id; } @@ -234,6 +260,7 @@ static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa, * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() * @pmk: The new pairwise master key * @pmk_len: PMK length in bytes, usually PMK_LEN (32) + * @pmkid: Calculated PMKID * @kck: Key confirmation key or %NULL if not yet derived * @kck_len: KCK length in bytes * @aa: Authenticator address @@ -250,7 +277,7 @@ static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa, */ struct rsn_pmksa_cache_entry * pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa, - const u8 *pmk, size_t pmk_len, + const u8 *pmk, size_t pmk_len, const u8 *pmkid, const u8 *kck, size_t kck_len, const u8 *aa, const u8 *spa, int session_timeout, struct eapol_state_machine *eapol, int akmp) @@ -258,7 +285,7 @@ pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa, struct rsn_pmksa_cache_entry *entry, *pos; struct os_reltime now; - if (pmk_len > PMK_LEN) + if (pmk_len > PMK_LEN_MAX) return NULL; if (wpa_key_mgmt_suite_b(akmp) && !kck) @@ -269,7 +296,9 @@ pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa, return NULL; os_memcpy(entry->pmk, pmk, pmk_len); entry->pmk_len = pmk_len; - if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) + if (pmkid) + os_memcpy(entry->pmkid, pmkid, PMKID_LEN); + else if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) rsn_pmkid_suite_b_192(kck, kck_len, aa, spa, entry->pmkid); else if (wpa_key_mgmt_suite_b(akmp)) rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid); @@ -337,7 +366,13 @@ pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa, radius_copy_class(&entry->radius_class, &old_entry->radius_class); #endif /* CONFIG_NO_RADIUS */ entry->eap_type_authsrv = old_entry->eap_type_authsrv; - entry->vlan_id = old_entry->vlan_id; + if (old_entry->vlan_desc) { + entry->vlan_desc = os_zalloc(sizeof(struct vlan_description)); + if (entry->vlan_desc) + *entry->vlan_desc = *old_entry->vlan_desc; + } else { + entry->vlan_desc = NULL; + } entry->opportunistic = 1; pmksa_cache_link_entry(pmksa, entry); @@ -471,12 +506,11 @@ static int das_attr_match(struct rsn_pmksa_cache_entry *entry, if (attr->acct_multi_session_id) { char buf[20]; - if (attr->acct_multi_session_id_len != 17) + if (attr->acct_multi_session_id_len != 16) return 0; - os_snprintf(buf, sizeof(buf), "%08X+%08X", - entry->acct_multi_session_id_hi, - entry->acct_multi_session_id_lo); - if (os_memcmp(attr->acct_multi_session_id, buf, 17) != 0) + os_snprintf(buf, sizeof(buf), "%016llX", + (unsigned long long) entry->acct_multi_session_id); + if (os_memcmp(attr->acct_multi_session_id, buf, 16) != 0) return 0; match++; } @@ -526,3 +560,48 @@ int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa, return found ? 0 : -1; } + + +/** + * pmksa_cache_auth_list - Dump text list of entries in PMKSA cache + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() + * @buf: Buffer for the list + * @len: Length of the buffer + * Returns: Number of bytes written to buffer + * + * This function is used to generate a text format representation of the + * current PMKSA cache contents for the ctrl_iface PMKSA command. + */ +int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len) +{ + int i, ret; + char *pos = buf; + struct rsn_pmksa_cache_entry *entry; + struct os_reltime now; + + os_get_reltime(&now); + ret = os_snprintf(pos, buf + len - pos, + "Index / SPA / PMKID / expiration (in seconds) / opportunistic\n"); + if (os_snprintf_error(buf + len - pos, ret)) + return pos - buf; + pos += ret; + i = 0; + entry = pmksa->pmksa; + while (entry) { + ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ", + i, MAC2STR(entry->spa)); + if (os_snprintf_error(buf + len - pos, ret)) + return pos - buf; + pos += ret; + pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid, + PMKID_LEN); + ret = os_snprintf(pos, buf + len - pos, " %d %d\n", + (int) (entry->expiration - now.sec), + entry->opportunistic); + if (os_snprintf_error(buf + len - pos, ret)) + return pos - buf; + pos += ret; + entry = entry->next; + } + return pos - buf; +} diff --git a/contrib/wpa/src/ap/pmksa_cache_auth.h b/contrib/wpa/src/ap/pmksa_cache_auth.h index 8b7be1291b53..d8d9c5a25c0e 100644 --- a/contrib/wpa/src/ap/pmksa_cache_auth.h +++ b/contrib/wpa/src/ap/pmksa_cache_auth.h @@ -17,7 +17,7 @@ struct rsn_pmksa_cache_entry { struct rsn_pmksa_cache_entry *next, *hnext; u8 pmkid[PMKID_LEN]; - u8 pmk[PMK_LEN]; + u8 pmk[PMK_LEN_MAX]; size_t pmk_len; os_time_t expiration; int akmp; /* WPA_KEY_MGMT_* */ @@ -28,11 +28,10 @@ struct rsn_pmksa_cache_entry { struct wpabuf *cui; struct radius_class_data radius_class; u8 eap_type_authsrv; - int vlan_id; + struct vlan_description *vlan_desc; int opportunistic; - u32 acct_multi_session_id_hi; - u32 acct_multi_session_id_lo; + u64 acct_multi_session_id; }; struct rsn_pmksa_cache; @@ -49,7 +48,7 @@ struct rsn_pmksa_cache_entry * pmksa_cache_get_okc( const u8 *pmkid); struct rsn_pmksa_cache_entry * pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa, - const u8 *pmk, size_t pmk_len, + const u8 *pmk, size_t pmk_len, const u8 *pmkid, const u8 *kck, size_t kck_len, const u8 *aa, const u8 *spa, int session_timeout, struct eapol_state_machine *eapol, int akmp); @@ -57,11 +56,14 @@ struct rsn_pmksa_cache_entry * pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa, const struct rsn_pmksa_cache_entry *old_entry, const u8 *aa, const u8 *pmkid); -void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry, +void pmksa_cache_to_eapol_data(struct hostapd_data *hapd, + struct rsn_pmksa_cache_entry *entry, struct eapol_state_machine *eapol); void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, struct rsn_pmksa_cache_entry *entry); int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa, struct radius_das_attrs *attr); +int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len); +void pmksa_cache_auth_flush(struct rsn_pmksa_cache *pmksa); #endif /* PMKSA_CACHE_H */ diff --git a/contrib/wpa/src/ap/rrm.c b/contrib/wpa/src/ap/rrm.c new file mode 100644 index 000000000000..3569f955bcd2 --- /dev/null +++ b/contrib/wpa/src/ap/rrm.c @@ -0,0 +1,544 @@ +/* + * hostapd / Radio Measurement (RRM) + * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH. + * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "hostapd.h" +#include "ap_drv_ops.h" +#include "sta_info.h" +#include "eloop.h" +#include "neighbor_db.h" +#include "rrm.h" + +#define HOSTAPD_RRM_REQUEST_TIMEOUT 5 + + +static void hostapd_lci_rep_timeout_handler(void *eloop_data, void *user_ctx) +{ + struct hostapd_data *hapd = eloop_data; + + wpa_printf(MSG_DEBUG, "RRM: LCI request (token %u) timed out", + hapd->lci_req_token); + hapd->lci_req_active = 0; +} + + +static void hostapd_handle_lci_report(struct hostapd_data *hapd, u8 token, + const u8 *pos, size_t len) +{ + if (!hapd->lci_req_active || hapd->lci_req_token != token) { + wpa_printf(MSG_DEBUG, "Unexpected LCI report, token %u", token); + return; + } + + hapd->lci_req_active = 0; + eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd, NULL); + wpa_printf(MSG_DEBUG, "LCI report token %u len %zu", token, len); +} + + +static void hostapd_range_rep_timeout_handler(void *eloop_data, void *user_ctx) +{ + struct hostapd_data *hapd = eloop_data; + + wpa_printf(MSG_DEBUG, "RRM: Range request (token %u) timed out", + hapd->range_req_token); + hapd->range_req_active = 0; +} + + +static void hostapd_handle_range_report(struct hostapd_data *hapd, u8 token, + const u8 *pos, size_t len) +{ + if (!hapd->range_req_active || hapd->range_req_token != token) { + wpa_printf(MSG_DEBUG, "Unexpected range report, token %u", + token); + return; + } + + hapd->range_req_active = 0; + eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL); + wpa_printf(MSG_DEBUG, "Range report token %u len %zu", token, len); +} + + +static void hostapd_handle_radio_msmt_report(struct hostapd_data *hapd, + const u8 *buf, size_t len) +{ + const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf; + const u8 *pos, *ie, *end; + u8 token; + + end = buf + len; + token = mgmt->u.action.u.rrm.dialog_token; + pos = mgmt->u.action.u.rrm.variable; + + while ((ie = get_ie(pos, end - pos, WLAN_EID_MEASURE_REPORT))) { + if (ie[1] < 5) { + wpa_printf(MSG_DEBUG, "Bad Measurement Report element"); + break; + } + + wpa_printf(MSG_DEBUG, "Measurement report type %u", ie[4]); + + switch (ie[4]) { + case MEASURE_TYPE_LCI: + hostapd_handle_lci_report(hapd, token, ie + 2, ie[1]); + break; + case MEASURE_TYPE_FTM_RANGE: + hostapd_handle_range_report(hapd, token, ie + 2, ie[1]); + break; + default: + wpa_printf(MSG_DEBUG, + "Measurement report type %u is not supported", + ie[4]); + break; + } + + pos = ie + ie[1] + 2; + } +} + + +static u16 hostapd_parse_location_lci_req_age(const u8 *buf, size_t len) +{ + const u8 *subelem; + + /* Range Request element + Location Subject + Maximum Age subelement */ + if (len < 3 + 1 + 4) + return 0; + + /* Subelements are arranged as IEs */ + subelem = get_ie(buf + 4, len - 4, LCI_REQ_SUBELEM_MAX_AGE); + if (subelem && subelem[1] == 2) + return *(u16 *) (subelem + 2); + + return 0; +} + + +static int hostapd_check_lci_age(struct hostapd_neighbor_entry *nr, u16 max_age) +{ + struct os_time curr, diff; + unsigned long diff_l; + + if (!max_age) + return 0; + + if (max_age == 0xffff) + return 1; + + if (os_get_time(&curr)) + return 0; + + os_time_sub(&curr, &nr->lci_date, &diff); + + /* avoid overflow */ + if (diff.sec > 0xffff) + return 0; + + /* LCI age is calculated in 10th of a second units. */ + diff_l = diff.sec * 10 + diff.usec / 100000; + + return max_age > diff_l; +} + + +static size_t hostapd_neighbor_report_len(struct wpabuf *buf, + struct hostapd_neighbor_entry *nr, + int send_lci, int send_civic) +{ + size_t len = 2 + wpabuf_len(nr->nr); + + if (send_lci && nr->lci) + len += 2 + wpabuf_len(nr->lci); + + if (send_civic && nr->civic) + len += 2 + wpabuf_len(nr->civic); + + return len; +} + + +static void hostapd_send_nei_report_resp(struct hostapd_data *hapd, + const u8 *addr, u8 dialog_token, + struct wpa_ssid_value *ssid, u8 lci, + u8 civic, u16 lci_max_age) +{ + struct hostapd_neighbor_entry *nr; + struct wpabuf *buf; + u8 *msmt_token; + + /* + * The number and length of the Neighbor Report elements in a Neighbor + * Report frame is limited by the maximum allowed MMPDU size; + 3 bytes + * of RRM header. + */ + buf = wpabuf_alloc(3 + IEEE80211_MAX_MMPDU_SIZE); + if (!buf) + return; + + wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); + wpabuf_put_u8(buf, WLAN_RRM_NEIGHBOR_REPORT_RESPONSE); + wpabuf_put_u8(buf, dialog_token); + + dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry, + list) { + int send_lci; + size_t len; + + if (ssid->ssid_len != nr->ssid.ssid_len || + os_memcmp(ssid->ssid, nr->ssid.ssid, ssid->ssid_len) != 0) + continue; + + send_lci = (lci != 0) && hostapd_check_lci_age(nr, lci_max_age); + len = hostapd_neighbor_report_len(buf, nr, send_lci, civic); + + if (len - 2 > 0xff) { + wpa_printf(MSG_DEBUG, + "NR entry for " MACSTR " exceeds 0xFF bytes", + MAC2STR(nr->bssid)); + continue; + } + + if (len > wpabuf_tailroom(buf)) + break; + + wpabuf_put_u8(buf, WLAN_EID_NEIGHBOR_REPORT); + wpabuf_put_u8(buf, len - 2); + wpabuf_put_buf(buf, nr->nr); + + if (send_lci && nr->lci) { + wpabuf_put_u8(buf, WLAN_EID_MEASURE_REPORT); + wpabuf_put_u8(buf, wpabuf_len(nr->lci)); + /* + * Override measurement token - the first byte of the + * Measurement Report element. + */ + msmt_token = wpabuf_put(buf, 0); + wpabuf_put_buf(buf, nr->lci); + *msmt_token = lci; + } + + if (civic && nr->civic) { + wpabuf_put_u8(buf, WLAN_EID_MEASURE_REPORT); + wpabuf_put_u8(buf, wpabuf_len(nr->civic)); + /* + * Override measurement token - the first byte of the + * Measurement Report element. + */ + msmt_token = wpabuf_put(buf, 0); + wpabuf_put_buf(buf, nr->civic); + *msmt_token = civic; + } + } + + hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, + wpabuf_head(buf), wpabuf_len(buf)); + wpabuf_free(buf); +} + + +static void hostapd_handle_nei_report_req(struct hostapd_data *hapd, + const u8 *buf, size_t len) +{ + const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf; + const u8 *pos, *ie, *end; + struct wpa_ssid_value ssid = { + .ssid_len = 0 + }; + u8 token; + u8 lci = 0, civic = 0; /* Measurement tokens */ + u16 lci_max_age = 0; + + if (!(hapd->conf->radio_measurements[0] & + WLAN_RRM_CAPS_NEIGHBOR_REPORT)) + return; + + end = buf + len; + + token = mgmt->u.action.u.rrm.dialog_token; + pos = mgmt->u.action.u.rrm.variable; + len = end - pos; + + ie = get_ie(pos, len, WLAN_EID_SSID); + if (ie && ie[1] && ie[1] <= SSID_MAX_LEN) { + ssid.ssid_len = ie[1]; + os_memcpy(ssid.ssid, ie + 2, ssid.ssid_len); + } else { + ssid.ssid_len = hapd->conf->ssid.ssid_len; + os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len); + } + + while ((ie = get_ie(pos, len, WLAN_EID_MEASURE_REQUEST))) { + if (ie[1] < 3) + break; + + wpa_printf(MSG_DEBUG, + "Neighbor report request, measure type %u", + ie[4]); + + switch (ie[4]) { /* Measurement Type */ + case MEASURE_TYPE_LCI: + lci = ie[2]; /* Measurement Token */ + lci_max_age = hostapd_parse_location_lci_req_age(ie + 2, + ie[1]); + break; + case MEASURE_TYPE_LOCATION_CIVIC: + civic = ie[2]; /* Measurement token */ + break; + } + + pos = ie + ie[1] + 2; + len = end - pos; + } + + hostapd_send_nei_report_resp(hapd, mgmt->sa, token, &ssid, lci, civic, + lci_max_age); +} + + +void hostapd_handle_radio_measurement(struct hostapd_data *hapd, + const u8 *buf, size_t len) +{ + const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf; + + /* + * Check for enough bytes: header + (1B)Category + (1B)Action + + * (1B)Dialog Token. + */ + if (len < IEEE80211_HDRLEN + 3) + return; + + wpa_printf(MSG_DEBUG, "Radio measurement frame, action %u from " MACSTR, + mgmt->u.action.u.rrm.action, MAC2STR(mgmt->sa)); + + switch (mgmt->u.action.u.rrm.action) { + case WLAN_RRM_RADIO_MEASUREMENT_REPORT: + hostapd_handle_radio_msmt_report(hapd, buf, len); + break; + case WLAN_RRM_NEIGHBOR_REPORT_REQUEST: + hostapd_handle_nei_report_req(hapd, buf, len); + break; + default: + wpa_printf(MSG_DEBUG, "RRM action %u is not supported", + mgmt->u.action.u.rrm.action); + break; + } +} + + +int hostapd_send_lci_req(struct hostapd_data *hapd, const u8 *addr) +{ + struct wpabuf *buf; + struct sta_info *sta = ap_get_sta(hapd, addr); + int ret; + + if (!sta) { + wpa_printf(MSG_INFO, + "Request LCI: Destination address is not in station list"); + return -1; + } + + if (!(sta->flags & WLAN_STA_AUTHORIZED)) { + wpa_printf(MSG_INFO, + "Request LCI: Destination address is not connected"); + return -1; + } + + if (!(sta->rrm_enabled_capa[1] & WLAN_RRM_CAPS_LCI_MEASUREMENT)) { + wpa_printf(MSG_INFO, + "Request LCI: Station does not support LCI in RRM"); + return -1; + } + + if (hapd->lci_req_active) { + wpa_printf(MSG_DEBUG, + "Request LCI: LCI request is already in process, overriding"); + hapd->lci_req_active = 0; + eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd, + NULL); + } + + /* Measurement request (5) + Measurement element with LCI (10) */ + buf = wpabuf_alloc(5 + 10); + if (!buf) + return -1; + + hapd->lci_req_token++; + /* For wraparounds - the token must be nonzero */ + if (!hapd->lci_req_token) + hapd->lci_req_token++; + + wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); + wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST); + wpabuf_put_u8(buf, hapd->lci_req_token); + wpabuf_put_le16(buf, 0); /* Number of repetitions */ + + wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST); + wpabuf_put_u8(buf, 3 + 1 + 4); + + wpabuf_put_u8(buf, 1); /* Measurement Token */ + /* + * Parallel and Enable bits are 0, Duration, Request, and Report are + * reserved. + */ + wpabuf_put_u8(buf, 0); + wpabuf_put_u8(buf, MEASURE_TYPE_LCI); + + wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE); + + wpabuf_put_u8(buf, LCI_REQ_SUBELEM_MAX_AGE); + wpabuf_put_u8(buf, 2); + wpabuf_put_le16(buf, 0xffff); + + ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, + wpabuf_head(buf), wpabuf_len(buf)); + wpabuf_free(buf); + if (ret) + return ret; + + hapd->lci_req_active = 1; + + eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0, + hostapd_lci_rep_timeout_handler, hapd, NULL); + + return 0; +} + + +int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr, + u16 random_interval, u8 min_ap, + const u8 *responders, unsigned int n_responders) +{ + struct wpabuf *buf; + struct sta_info *sta; + u8 *len; + unsigned int i; + int ret; + + wpa_printf(MSG_DEBUG, "Request range: dest addr " MACSTR + " rand interval %u min AP %u n_responders %u", MAC2STR(addr), + random_interval, min_ap, n_responders); + + if (min_ap == 0 || min_ap > n_responders) { + wpa_printf(MSG_INFO, "Request range: Wrong min AP count"); + return -1; + } + + sta = ap_get_sta(hapd, addr); + if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) { + wpa_printf(MSG_INFO, + "Request range: Destination address is not connected"); + return -1; + } + + if (!(sta->rrm_enabled_capa[4] & WLAN_RRM_CAPS_FTM_RANGE_REPORT)) { + wpa_printf(MSG_ERROR, + "Request range: Destination station does not support FTM range report in RRM"); + return -1; + } + + if (hapd->range_req_active) { + wpa_printf(MSG_DEBUG, + "Request range: Range request is already in process; overriding"); + hapd->range_req_active = 0; + eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0, + hostapd_range_rep_timeout_handler, hapd, + NULL); + } + + /* Action + measurement type + token + reps + EID + len = 7 */ + buf = wpabuf_alloc(7 + 255); + if (!buf) + return -1; + + hapd->range_req_token++; + if (!hapd->range_req_token) /* For wraparounds */ + hapd->range_req_token++; + + /* IEEE P802.11-REVmc/D5.0, 9.6.7.2 */ + wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); + wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST); + wpabuf_put_u8(buf, hapd->range_req_token); /* Dialog Token */ + wpabuf_put_le16(buf, 0); /* Number of Repetitions */ + + /* IEEE P802.11-REVmc/D5.0, 9.4.2.21 */ + wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST); + len = wpabuf_put(buf, 1); /* Length will be set later */ + + wpabuf_put_u8(buf, 1); /* Measurement Token */ + /* + * Parallel and Enable bits are 0; Duration, Request, and Report are + * reserved. + */ + wpabuf_put_u8(buf, 0); /* Measurement Request Mode */ + wpabuf_put_u8(buf, MEASURE_TYPE_FTM_RANGE); /* Measurement Type */ + + /* IEEE P802.11-REVmc/D5.0, 9.4.2.21.19 */ + wpabuf_put_le16(buf, random_interval); /* Randomization Interval */ + wpabuf_put_u8(buf, min_ap); /* Minimum AP Count */ + + /* FTM Range Subelements */ + + /* + * Taking the neighbor report part of the range request from neighbor + * database instead of requesting the separate bits of data from the + * user. + */ + for (i = 0; i < n_responders; i++) { + struct hostapd_neighbor_entry *nr; + + nr = hostapd_neighbor_get(hapd, responders + ETH_ALEN * i, + NULL); + if (!nr) { + wpa_printf(MSG_INFO, "Missing neighbor report for " + MACSTR, MAC2STR(responders + ETH_ALEN * i)); + wpabuf_free(buf); + return -1; + } + + if (wpabuf_tailroom(buf) < 2 + wpabuf_len(nr->nr)) { + wpa_printf(MSG_ERROR, "Too long range request"); + wpabuf_free(buf); + return -1; + } + + wpabuf_put_u8(buf, WLAN_EID_NEIGHBOR_REPORT); + wpabuf_put_u8(buf, wpabuf_len(nr->nr)); + wpabuf_put_buf(buf, nr->nr); + } + + /* Action + measurement type + token + reps + EID + len = 7 */ + *len = wpabuf_len(buf) - 7; + + ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, + wpabuf_head(buf), wpabuf_len(buf)); + wpabuf_free(buf); + if (ret) + return ret; + + hapd->range_req_active = 1; + + eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0, + hostapd_range_rep_timeout_handler, hapd, NULL); + + return 0; +} + + +void hostapd_clean_rrm(struct hostapd_data *hapd) +{ + hostpad_free_neighbor_db(hapd); + eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd, NULL); + hapd->lci_req_active = 0; + eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL); + hapd->range_req_active = 0; +} diff --git a/contrib/wpa/src/ap/rrm.h b/contrib/wpa/src/ap/rrm.h new file mode 100644 index 000000000000..f07fd41ac019 --- /dev/null +++ b/contrib/wpa/src/ap/rrm.h @@ -0,0 +1,28 @@ +/* + * hostapd / Radio Measurement (RRM) + * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH. + * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef RRM_H +#define RRM_H + +/* + * Max measure request length is 255, -6 of the body we have 249 for the + * neighbor report elements. Each neighbor report element is at least 2 + 13 + * bytes, so we can't have more than 16 responders in the request. + */ +#define RRM_RANGE_REQ_MAX_RESPONDERS 16 + +void hostapd_handle_radio_measurement(struct hostapd_data *hapd, + const u8 *buf, size_t len); +int hostapd_send_lci_req(struct hostapd_data *hapd, const u8 *addr); +int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr, + u16 random_interval, u8 min_ap, + const u8 *responders, unsigned int n_responders); +void hostapd_clean_rrm(struct hostapd_data *hapd); + +#endif /* RRM_H */ diff --git a/contrib/wpa/src/ap/sta_info.c b/contrib/wpa/src/ap/sta_info.c index d64307ccfd08..f12d4088b131 100644 --- a/contrib/wpa/src/ap/sta_info.c +++ b/contrib/wpa/src/ap/sta_info.c @@ -32,8 +32,10 @@ #include "ap_drv_ops.h" #include "gas_serv.h" #include "wnm_ap.h" +#include "mbo_ap.h" #include "ndisc_snoop.h" #include "sta_info.h" +#include "vlan.h" static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd, struct sta_info *sta); @@ -169,21 +171,10 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) ap_sta_ip6addr_del(hapd, sta); if (!hapd->iface->driver_ap_teardown && - !(sta->flags & WLAN_STA_PREAUTH)) + !(sta->flags & WLAN_STA_PREAUTH)) { hostapd_drv_sta_remove(hapd, sta->addr); - -#ifndef CONFIG_NO_VLAN - if (sta->vlan_id_bound) { - /* - * Need to remove the STA entry before potentially removing the - * VLAN. - */ - if (hapd->iface->driver_ap_teardown && - !(sta->flags & WLAN_STA_PREAUTH)) - hostapd_drv_sta_remove(hapd, sta->addr); - vlan_remove_dynamic(hapd, sta->vlan_id_bound); + sta->added_unassoc = 0; } -#endif /* CONFIG_NO_VLAN */ ap_sta_hash_del(hapd, sta); ap_sta_list_del(hapd, sta); @@ -231,6 +222,13 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) hapd->iface->num_sta_ht_20mhz--; } +#ifdef CONFIG_TAXONOMY + wpabuf_free(sta->probe_ie_taxonomy); + sta->probe_ie_taxonomy = NULL; + wpabuf_free(sta->assoc_ie_taxonomy); + sta->assoc_ie_taxonomy = NULL; +#endif /* CONFIG_TAXONOMY */ + #ifdef CONFIG_IEEE80211N ht40_intolerant_remove(hapd->iface, sta); #endif /* CONFIG_IEEE80211N */ @@ -251,7 +249,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) #ifdef CONFIG_MESH if (hapd->mesh_sta_free_cb) - hapd->mesh_sta_free_cb(sta); + hapd->mesh_sta_free_cb(hapd, sta); #endif /* CONFIG_MESH */ if (set_beacon) @@ -262,11 +260,10 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) eloop_cancel_timeout(ap_handle_timer, hapd, sta); eloop_cancel_timeout(ap_handle_session_timer, hapd, sta); eloop_cancel_timeout(ap_handle_session_warning_timer, hapd, sta); - eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta); - eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta); + ap_sta_clear_disconnect_timeouts(hapd, sta); sae_clear_retransmit_timer(hapd, sta); - ieee802_1x_free_station(sta); + ieee802_1x_free_station(hapd, sta); wpa_auth_sta_deinit(sta->wpa_sm); rsn_preauth_free_station(hapd, sta); #ifndef CONFIG_NO_RADIUS @@ -274,6 +271,28 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) radius_client_flush_auth(hapd->radius, sta->addr); #endif /* CONFIG_NO_RADIUS */ +#ifndef CONFIG_NO_VLAN + /* + * sta->wpa_sm->group needs to be released before so that + * vlan_remove_dynamic() can check that no stations are left on the + * AP_VLAN netdev. + */ + if (sta->vlan_id) + vlan_remove_dynamic(hapd, sta->vlan_id); + if (sta->vlan_id_bound) { + /* + * Need to remove the STA entry before potentially removing the + * VLAN. + */ + if (hapd->iface->driver_ap_teardown && + !(sta->flags & WLAN_STA_PREAUTH)) { + hostapd_drv_sta_remove(hapd, sta->addr); + sta->added_unassoc = 0; + } + vlan_remove_dynamic(hapd, sta->vlan_id_bound); + } +#endif /* CONFIG_NO_VLAN */ + os_free(sta->challenge); #ifdef CONFIG_IEEE80211W @@ -315,6 +334,9 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) os_free(sta->sae); #endif /* CONFIG_SAE */ + mbo_ap_sta_free(sta); + os_free(sta->supp_op_classes); + os_free(sta); } @@ -354,8 +376,8 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) unsigned long next_time = 0; int reason; - wpa_printf(MSG_DEBUG, "%s: " MACSTR " flags=0x%x timeout_next=%d", - __func__, MAC2STR(sta->addr), sta->flags, + wpa_printf(MSG_DEBUG, "%s: %s: " MACSTR " flags=0x%x timeout_next=%d", + hapd->conf->iface, __func__, MAC2STR(sta->addr), sta->flags, sta->timeout_next); if (sta->timeout_next == STA_REMOVE) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, @@ -482,7 +504,7 @@ skip_poll: sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT; accounting_sta_stop(hapd, sta); - ieee802_1x_free_station(sta); + ieee802_1x_free_station(hapd, sta); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "disassociated due to " "inactivity"); @@ -519,6 +541,8 @@ static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx) struct hostapd_data *hapd = eloop_ctx; struct sta_info *sta = timeout_ctx; + wpa_printf(MSG_DEBUG, "%s: Session timer for STA " MACSTR, + hapd->conf->iface, MAC2STR(sta->addr)); if (!(sta->flags & WLAN_STA_AUTH)) { if (sta->flags & WLAN_STA_GAS) { wpa_printf(MSG_DEBUG, "GAS: Remove temporary STA " @@ -577,8 +601,8 @@ static void ap_handle_session_warning_timer(void *eloop_ctx, void *timeout_ctx) struct hostapd_data *hapd = eloop_ctx; struct sta_info *sta = timeout_ctx; - wpa_printf(MSG_DEBUG, "WNM: Session warning time reached for " MACSTR, - MAC2STR(sta->addr)); + wpa_printf(MSG_DEBUG, "%s: WNM: Session warning time reached for " + MACSTR, hapd->conf->iface, MAC2STR(sta->addr)); if (sta->hs20_session_info_url == NULL) return; @@ -619,7 +643,10 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr) return NULL; } sta->acct_interim_interval = hapd->conf->acct_interim_interval; - accounting_sta_get_id(hapd, sta); + if (accounting_sta_get_id(hapd, sta) < 0) { + os_free(sta); + return NULL; + } if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) { wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout " @@ -640,6 +667,11 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr) sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ; dl_list_init(&sta->ip6addr); +#ifdef CONFIG_TAXONOMY + sta_track_claim_taxonomy_info(hapd->iface, addr, + &sta->probe_ie_taxonomy); +#endif /* CONFIG_TAXONOMY */ + return sta; } @@ -652,14 +684,16 @@ static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta) hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr); ap_sta_ip6addr_del(hapd, sta); - wpa_printf(MSG_DEBUG, "Removing STA " MACSTR " from kernel driver", - MAC2STR(sta->addr)); + wpa_printf(MSG_DEBUG, "%s: Removing STA " MACSTR " from kernel driver", + hapd->conf->iface, MAC2STR(sta->addr)); if (hostapd_drv_sta_remove(hapd, sta->addr) && sta->flags & WLAN_STA_ASSOC) { - wpa_printf(MSG_DEBUG, "Could not remove station " MACSTR - " from kernel driver.", MAC2STR(sta->addr)); + wpa_printf(MSG_DEBUG, "%s: Could not remove station " MACSTR + " from kernel driver", + hapd->conf->iface, MAC2STR(sta->addr)); return -1; } + sta->added_unassoc = 0; return 0; } @@ -683,6 +717,10 @@ static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd, if (!sta2) continue; + wpa_printf(MSG_DEBUG, "%s: disconnect old STA " MACSTR + " association from another BSS %s", + hapd->conf->iface, MAC2STR(sta2->addr), + bss->conf->iface); ap_sta_disconnect(bss, sta2, sta2->addr, WLAN_REASON_PREV_AUTH_NOT_VALID); } @@ -694,6 +732,8 @@ static void ap_sta_disassoc_cb_timeout(void *eloop_ctx, void *timeout_ctx) struct hostapd_data *hapd = eloop_ctx; struct sta_info *sta = timeout_ctx; + wpa_printf(MSG_DEBUG, "%s: Disassociation callback for STA " MACSTR, + hapd->conf->iface, MAC2STR(sta->addr)); ap_sta_remove(hapd, sta); mlme_disassociate_indication(hapd, sta, sta->disassoc_reason); } @@ -717,7 +757,7 @@ void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta, eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DISASSOC, 0, ap_handle_timer, hapd, sta); accounting_sta_stop(hapd, sta); - ieee802_1x_free_station(sta); + ieee802_1x_free_station(hapd, sta); sta->disassoc_reason = reason; sta->flags |= WLAN_STA_PENDING_DISASSOC_CB; @@ -733,6 +773,8 @@ static void ap_sta_deauth_cb_timeout(void *eloop_ctx, void *timeout_ctx) struct hostapd_data *hapd = eloop_ctx; struct sta_info *sta = timeout_ctx; + wpa_printf(MSG_DEBUG, "%s: Deauthentication callback for STA " MACSTR, + hapd->conf->iface, MAC2STR(sta->addr)); ap_sta_remove(hapd, sta); mlme_deauthenticate_indication(hapd, sta, sta->deauth_reason); } @@ -756,7 +798,7 @@ void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta, eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0, ap_handle_timer, hapd, sta); accounting_sta_stop(hapd, sta); - ieee802_1x_free_station(sta); + ieee802_1x_free_station(hapd, sta); sta->deauth_reason = reason; sta->flags |= WLAN_STA_PENDING_DEAUTH_CB; @@ -784,6 +826,128 @@ int ap_sta_wps_cancel(struct hostapd_data *hapd, #endif /* CONFIG_WPS */ +static int ap_sta_get_free_vlan_id(struct hostapd_data *hapd) +{ + struct hostapd_vlan *vlan; + int vlan_id = MAX_VLAN_ID + 2; + +retry: + for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) { + if (vlan->vlan_id == vlan_id) { + vlan_id++; + goto retry; + } + } + return vlan_id; +} + + +int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta, + struct vlan_description *vlan_desc) +{ + struct hostapd_vlan *vlan = NULL, *wildcard_vlan = NULL; + int old_vlan_id, vlan_id = 0, ret = 0; + + if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED) + vlan_desc = NULL; + + /* Check if there is something to do */ + if (hapd->conf->ssid.per_sta_vif && !sta->vlan_id) { + /* This sta is lacking its own vif */ + } else if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED && + !hapd->conf->ssid.per_sta_vif && sta->vlan_id) { + /* sta->vlan_id needs to be reset */ + } else if (!vlan_compare(vlan_desc, sta->vlan_desc)) { + return 0; /* nothing to change */ + } + + /* Now the real VLAN changed or the STA just needs its own vif */ + if (hapd->conf->ssid.per_sta_vif) { + /* Assign a new vif, always */ + /* find a free vlan_id sufficiently big */ + vlan_id = ap_sta_get_free_vlan_id(hapd); + /* Get wildcard VLAN */ + for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) { + if (vlan->vlan_id == VLAN_ID_WILDCARD) + break; + } + if (!vlan) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "per_sta_vif missing wildcard"); + vlan_id = 0; + ret = -1; + goto done; + } + } else if (vlan_desc && vlan_desc->notempty) { + for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) { + if (!vlan_compare(&vlan->vlan_desc, vlan_desc)) + break; + if (vlan->vlan_id == VLAN_ID_WILDCARD) + wildcard_vlan = vlan; + } + if (vlan) { + vlan_id = vlan->vlan_id; + } else if (wildcard_vlan) { + vlan = wildcard_vlan; + vlan_id = vlan_desc->untagged; + if (vlan_desc->tagged[0]) { + /* Tagged VLAN configuration */ + vlan_id = ap_sta_get_free_vlan_id(hapd); + } + } else { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "missing vlan and wildcard for vlan=%d%s", + vlan_desc->untagged, + vlan_desc->tagged[0] ? "+" : ""); + vlan_id = 0; + ret = -1; + goto done; + } + } + + if (vlan && vlan->vlan_id == VLAN_ID_WILDCARD) { + vlan = vlan_add_dynamic(hapd, vlan, vlan_id, vlan_desc); + if (vlan == NULL) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "could not add dynamic VLAN interface for vlan=%d%s", + vlan_desc ? vlan_desc->untagged : -1, + (vlan_desc && vlan_desc->tagged[0]) ? + "+" : ""); + vlan_id = 0; + ret = -1; + goto done; + } + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "added new dynamic VLAN interface '%s'", + vlan->ifname); + } else if (vlan && vlan->dynamic_vlan > 0) { + vlan->dynamic_vlan++; + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "updated existing dynamic VLAN interface '%s'", + vlan->ifname); + } +done: + old_vlan_id = sta->vlan_id; + sta->vlan_id = vlan_id; + sta->vlan_desc = vlan ? &vlan->vlan_desc : NULL; + + if (vlan_id != old_vlan_id && old_vlan_id) + vlan_remove_dynamic(hapd, old_vlan_id); + + return ret; +} + + int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta) { #ifndef CONFIG_NO_VLAN @@ -796,20 +960,11 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta) if (hapd->conf->ssid.vlan[0]) iface = hapd->conf->ssid.vlan; - if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED) - sta->vlan_id = 0; - else if (sta->vlan_id > 0) { - struct hostapd_vlan *wildcard_vlan = NULL; - vlan = hapd->conf->vlan; - while (vlan) { + if (sta->vlan_id > 0) { + for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) { if (vlan->vlan_id == sta->vlan_id) break; - if (vlan->vlan_id == VLAN_ID_WILDCARD) - wildcard_vlan = vlan; - vlan = vlan->next; } - if (!vlan) - vlan = wildcard_vlan; if (vlan) iface = vlan->ifname; } @@ -829,54 +984,13 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta) sta->vlan_id); ret = -1; goto done; - } else if (sta->vlan_id > 0 && vlan->vlan_id == VLAN_ID_WILDCARD) { - vlan = vlan_add_dynamic(hapd, vlan, sta->vlan_id); - if (vlan == NULL) { - hostapd_logger(hapd, sta->addr, - HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, "could not add " - "dynamic VLAN interface for vlan_id=%d", - sta->vlan_id); - ret = -1; - goto done; - } - - iface = vlan->ifname; - if (vlan_setup_encryption_dyn(hapd, iface) != 0) { - hostapd_logger(hapd, sta->addr, - HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, "could not " - "configure encryption for dynamic VLAN " - "interface for vlan_id=%d", - sta->vlan_id); - } - - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, "added new dynamic VLAN " - "interface '%s'", iface); - } else if (vlan && vlan->vlan_id == sta->vlan_id) { - if (vlan->dynamic_vlan > 0) { - vlan->dynamic_vlan++; - hostapd_logger(hapd, sta->addr, - HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, "updated existing " - "dynamic VLAN interface '%s'", iface); - } - - /* - * Update encryption configuration for statically generated - * VLAN interface. This is only used for static WEP - * configuration for the case where hostapd did not yet know - * which keys are to be used when the interface was added. - */ - if (vlan_setup_encryption_dyn(hapd, iface) != 0) { - hostapd_logger(hapd, sta->addr, - HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, "could not " - "configure encryption for VLAN " - "interface for vlan_id=%d", - sta->vlan_id); - } + } else if (vlan && vlan->dynamic_vlan > 0) { + vlan->dynamic_vlan++; + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "updated existing dynamic VLAN interface '%s'", + iface); } /* ref counters have been increased, so mark the station */ @@ -942,6 +1056,10 @@ static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx) unsigned int timeout, sec, usec; u8 *trans_id, *nbuf; + wpa_printf(MSG_DEBUG, "%s: SA Query timer for STA " MACSTR + " (count=%d)", + hapd->conf->iface, MAC2STR(sta->addr), sta->sa_query_count); + if (sta->sa_query_count > 0 && ap_check_sa_query_timeout(hapd, sta)) return; @@ -1080,6 +1198,14 @@ void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta, void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta, const u8 *addr, u16 reason) { + if (sta) + wpa_printf(MSG_DEBUG, "%s: %s STA " MACSTR " reason=%u", + hapd->conf->iface, __func__, MAC2STR(sta->addr), + reason); + else if (addr) + wpa_printf(MSG_DEBUG, "%s: %s addr " MACSTR " reason=%u", + hapd->conf->iface, __func__, MAC2STR(addr), + reason); if (sta == NULL && addr) sta = ap_get_sta(hapd, addr); @@ -1093,10 +1219,10 @@ void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta, wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH); ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); - wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout " + wpa_printf(MSG_DEBUG, "%s: %s: reschedule ap_handle_timer timeout " "for " MACSTR " (%d seconds - " "AP_MAX_INACTIVITY_AFTER_DEAUTH)", - __func__, MAC2STR(sta->addr), + hapd->conf->iface, __func__, MAC2STR(sta->addr), AP_MAX_INACTIVITY_AFTER_DEAUTH); eloop_cancel_timeout(ap_handle_timer, hapd, sta); eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0, @@ -1136,6 +1262,22 @@ void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta) } +void ap_sta_clear_disconnect_timeouts(struct hostapd_data *hapd, + struct sta_info *sta) +{ + if (eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta) > 0) + wpa_printf(MSG_DEBUG, + "%s: Removed ap_sta_deauth_cb_timeout timeout for " + MACSTR, + hapd->conf->iface, MAC2STR(sta->addr)); + if (eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta) > 0) + wpa_printf(MSG_DEBUG, + "%s: Removed ap_sta_disassoc_cb_timeout timeout for " + MACSTR, + hapd->conf->iface, MAC2STR(sta->addr)); +} + + int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen) { int res; diff --git a/contrib/wpa/src/ap/sta_info.h b/contrib/wpa/src/ap/sta_info.h index 420d64e5793b..099de62d1a9a 100644 --- a/contrib/wpa/src/ap/sta_info.h +++ b/contrib/wpa/src/ap/sta_info.h @@ -12,9 +12,11 @@ #ifdef CONFIG_MESH /* needed for mesh_plink_state enum */ #include "common/defs.h" +#include "common/wpa_common.h" #endif /* CONFIG_MESH */ #include "list.h" +#include "vlan.h" /* STA flags */ #define WLAN_STA_AUTH BIT(0) @@ -45,6 +47,20 @@ #define WLAN_SUPP_RATES_MAX 32 +struct mbo_non_pref_chan_info { + struct mbo_non_pref_chan_info *next; + u8 op_class; + u8 pref; + u8 reason_code; + u8 num_channels; + u8 channels[]; +}; + +struct pending_eapol_rx { + struct wpabuf *buf; + struct os_reltime rx_time; +}; + struct sta_info { struct sta_info *next; /* next entry in sta list */ struct sta_info *hnext; /* next entry in hash table list */ @@ -63,13 +79,22 @@ struct sta_info { enum mesh_plink_state plink_state; u16 peer_lid; u16 my_lid; + u16 peer_aid; u16 mpm_close_reason; int mpm_retries; - u8 my_nonce[32]; - u8 peer_nonce[32]; + u8 my_nonce[WPA_NONCE_LEN]; + u8 peer_nonce[WPA_NONCE_LEN]; u8 aek[32]; /* SHA256 digest length */ - u8 mtk[16]; - u8 mgtk[16]; + u8 mtk[WPA_TK_MAX_LEN]; + size_t mtk_len; + u8 mgtk_rsc[6]; + u8 mgtk_key_id; + u8 mgtk[WPA_TK_MAX_LEN]; + size_t mgtk_len; + u8 igtk_rsc[6]; + u8 igtk[WPA_TK_MAX_LEN]; + size_t igtk_len; + u16 igtk_key_id; u8 sae_auth_retry; #endif /* CONFIG_MESH */ @@ -86,6 +111,8 @@ struct sta_info { unsigned int hs20_deauth_requested:1; unsigned int session_timeout_set:1; unsigned int radius_das_match:1; + unsigned int ecsa_supported:1; + unsigned int added_unassoc:1; u16 auth_alg; @@ -100,17 +127,20 @@ struct sta_info { /* IEEE 802.1X related data */ struct eapol_state_machine *eapol_sm; - u32 acct_session_id_hi; - u32 acct_session_id_lo; + struct pending_eapol_rx *pending_eapol_rx; + + u64 acct_session_id; struct os_reltime acct_session_start; int acct_session_started; int acct_terminate_cause; /* Acct-Terminate-Cause */ int acct_interim_interval; /* Acct-Interim-Interval */ + unsigned int acct_interim_errors; - unsigned long last_rx_bytes; - unsigned long last_tx_bytes; - u32 acct_input_gigawords; /* Acct-Input-Gigawords */ - u32 acct_output_gigawords; /* Acct-Output-Gigawords */ + /* For extending 32-bit driver counters to 64-bit counters */ + u32 last_rx_bytes_hi; + u32 last_rx_bytes_lo; + u32 last_tx_bytes_hi; + u32 last_tx_bytes_lo; u8 *challenge; /* IEEE 802.11 Shared Key Authentication Challenge */ @@ -118,6 +148,7 @@ struct sta_info { struct rsn_preauth_interface *preauth_iface; int vlan_id; /* 0: none, >0: VID */ + struct vlan_description *vlan_desc; int vlan_id_bound; /* updated by ap_sta_bind_vlan() */ /* PSKs from RADIUS authentication server */ struct hostapd_sta_wpa_psk_short *psk; @@ -161,6 +192,7 @@ struct sta_info { #ifdef CONFIG_SAE struct sae_data *sae; + unsigned int mesh_sae_pmksa_caching:1; #endif /* CONFIG_SAE */ u32 session_timeout; /* valid only if session_timeout_set == 1 */ @@ -170,6 +202,22 @@ struct sta_info { u16 last_seq_ctrl; /* Last Authentication/(Re)Association Request/Action frame subtype */ u8 last_subtype; + +#ifdef CONFIG_MBO + u8 cell_capa; /* 0 = unknown (not an MBO STA); otherwise, + * enum mbo_cellular_capa values */ + struct mbo_non_pref_chan_info *non_pref_chan; +#endif /* CONFIG_MBO */ + + u8 *supp_op_classes; /* Supported Operating Classes element, if + * received, starting from the Length field */ + + u8 rrm_enabled_capa[5]; + +#ifdef CONFIG_TAXONOMY + struct wpabuf *probe_ie_taxonomy; + struct wpabuf *assoc_ie_taxonomy; +#endif /* CONFIG_TAXONOMY */ }; @@ -180,7 +228,7 @@ struct sta_info { * AP_DISASSOC_DELAY seconds. Similarly, the station will be deauthenticated * after AP_DEAUTH_DELAY seconds has passed after disassociation. */ #define AP_MAX_INACTIVITY (5 * 60) -#define AP_DISASSOC_DELAY (1) +#define AP_DISASSOC_DELAY (3) #define AP_DEAUTH_DELAY (1) /* Number of seconds to keep STA entry with Authenticated flag after it has * been disassociated. */ @@ -220,6 +268,8 @@ int ap_sta_wps_cancel(struct hostapd_data *hapd, struct sta_info *sta, void *ctx); #endif /* CONFIG_WPS */ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta); +int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta, + struct vlan_description *vlan_desc); void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta); void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta); int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta); @@ -235,6 +285,8 @@ static inline int ap_sta_is_authorized(struct sta_info *sta) void ap_sta_deauth_cb(struct hostapd_data *hapd, struct sta_info *sta); void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta); +void ap_sta_clear_disconnect_timeouts(struct hostapd_data *hapd, + struct sta_info *sta); int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen); diff --git a/contrib/wpa/src/ap/taxonomy.c b/contrib/wpa/src/ap/taxonomy.c new file mode 100644 index 000000000000..cea8b726f47a --- /dev/null +++ b/contrib/wpa/src/ap/taxonomy.c @@ -0,0 +1,291 @@ +/* + * hostapd / Client taxonomy + * Copyright (c) 2015 Google, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + * + * Parse a series of IEs, as in Probe Request or (Re)Association Request frames, + * and render them to a descriptive string. The tag number of standard options + * is written to the string, while the vendor ID and subtag are written for + * vendor options. + * + * Example strings: + * 0,1,50,45,221(00904c,51) + * 0,1,33,36,48,45,221(00904c,51),221(0050f2,2) + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/wpa_ctrl.h" +#include "hostapd.h" +#include "sta_info.h" + + +/* Copy a string with no funny schtuff allowed; only alphanumerics. */ +static void no_mischief_strncpy(char *dst, const char *src, size_t n) +{ + size_t i; + + for (i = 0; i < n; i++) { + unsigned char s = src[i]; + int is_lower = s >= 'a' && s <= 'z'; + int is_upper = s >= 'A' && s <= 'Z'; + int is_digit = s >= '0' && s <= '9'; + + if (is_lower || is_upper || is_digit) { + /* TODO: if any manufacturer uses Unicode within the + * WPS header, it will get mangled here. */ + dst[i] = s; + } else { + /* Note that even spaces will be transformed to + * underscores, so 'Nexus 7' will turn into 'Nexus_7'. + * This is deliberate, to make the string easier to + * parse. */ + dst[i] = '_'; + } + } +} + + +static int get_wps_name(char *name, size_t name_len, + const u8 *data, size_t data_len) +{ + /* Inside the WPS IE are a series of attributes, using two byte IDs + * and two byte lengths. We're looking for the model name, if + * present. */ + while (data_len >= 4) { + u16 id, elen; + + id = WPA_GET_BE16(data); + elen = WPA_GET_BE16(data + 2); + data += 4; + data_len -= 4; + + if (elen > data_len) + return 0; + + if (id == 0x1023) { + /* Model name, like 'Nexus 7' */ + size_t n = (elen < name_len) ? elen : name_len; + no_mischief_strncpy(name, (const char *) data, n); + return n; + } + + data += elen; + data_len -= elen; + } + + return 0; +} + + +static void ie_to_string(char *fstr, size_t fstr_len, const struct wpabuf *ies) +{ + char *fpos = fstr; + char *fend = fstr + fstr_len; + char htcap[7 + 4 + 1]; /* ",htcap:" + %04hx + trailing NUL */ + char htagg[7 + 2 + 1]; /* ",htagg:" + %02hx + trailing NUL */ + char htmcs[7 + 8 + 1]; /* ",htmcs:" + %08x + trailing NUL */ + char vhtcap[8 + 8 + 1]; /* ",vhtcap:" + %08x + trailing NUL */ + char vhtrxmcs[10 + 8 + 1]; /* ",vhtrxmcs:" + %08x + trailing NUL */ + char vhttxmcs[10 + 8 + 1]; /* ",vhttxmcs:" + %08x + trailing NUL */ +#define MAX_EXTCAP 254 + char extcap[8 + 2 * MAX_EXTCAP + 1]; /* ",extcap:" + hex + trailing NUL + */ + char txpow[7 + 4 + 1]; /* ",txpow:" + %04hx + trailing NUL */ +#define WPS_NAME_LEN 32 + char wps[WPS_NAME_LEN + 5 + 1]; /* room to prepend ",wps:" + trailing + * NUL */ + int num = 0; + const u8 *ie; + size_t ie_len; + int ret; + + os_memset(htcap, 0, sizeof(htcap)); + os_memset(htagg, 0, sizeof(htagg)); + os_memset(htmcs, 0, sizeof(htmcs)); + os_memset(vhtcap, 0, sizeof(vhtcap)); + os_memset(vhtrxmcs, 0, sizeof(vhtrxmcs)); + os_memset(vhttxmcs, 0, sizeof(vhttxmcs)); + os_memset(extcap, 0, sizeof(extcap)); + os_memset(txpow, 0, sizeof(txpow)); + os_memset(wps, 0, sizeof(wps)); + *fpos = '\0'; + + if (!ies) + return; + ie = wpabuf_head(ies); + ie_len = wpabuf_len(ies); + + while (ie_len >= 2) { + u8 id, elen; + char *sep = (num++ == 0) ? "" : ","; + + id = *ie++; + elen = *ie++; + ie_len -= 2; + + if (elen > ie_len) + break; + + if (id == WLAN_EID_VENDOR_SPECIFIC && elen >= 4) { + /* Vendor specific */ + if (WPA_GET_BE32(ie) == WPS_IE_VENDOR_TYPE) { + /* WPS */ + char model_name[WPS_NAME_LEN + 1]; + const u8 *data = &ie[4]; + size_t data_len = elen - 4; + + os_memset(model_name, 0, sizeof(model_name)); + if (get_wps_name(model_name, WPS_NAME_LEN, data, + data_len)) { + os_snprintf(wps, sizeof(wps), + ",wps:%s", model_name); + } + } + + ret = os_snprintf(fpos, fend - fpos, + "%s%d(%02x%02x%02x,%d)", + sep, id, ie[0], ie[1], ie[2], ie[3]); + } else { + if (id == WLAN_EID_HT_CAP && elen >= 2) { + /* HT Capabilities (802.11n) */ + os_snprintf(htcap, sizeof(htcap), + ",htcap:%04hx", + WPA_GET_LE16(ie)); + } + if (id == WLAN_EID_HT_CAP && elen >= 3) { + /* HT Capabilities (802.11n), A-MPDU information + */ + os_snprintf(htagg, sizeof(htagg), + ",htagg:%02hx", (u16) ie[2]); + } + if (id == WLAN_EID_HT_CAP && elen >= 7) { + /* HT Capabilities (802.11n), MCS information */ + os_snprintf(htmcs, sizeof(htmcs), + ",htmcs:%08hx", + (u16) WPA_GET_LE32(ie + 3)); + } + if (id == WLAN_EID_VHT_CAP && elen >= 4) { + /* VHT Capabilities (802.11ac) */ + os_snprintf(vhtcap, sizeof(vhtcap), + ",vhtcap:%08x", + WPA_GET_LE32(ie)); + } + if (id == WLAN_EID_VHT_CAP && elen >= 8) { + /* VHT Capabilities (802.11ac), RX MCS + * information */ + os_snprintf(vhtrxmcs, sizeof(vhtrxmcs), + ",vhtrxmcs:%08x", + WPA_GET_LE32(ie + 4)); + } + if (id == WLAN_EID_VHT_CAP && elen >= 12) { + /* VHT Capabilities (802.11ac), TX MCS + * information */ + os_snprintf(vhttxmcs, sizeof(vhttxmcs), + ",vhttxmcs:%08x", + WPA_GET_LE32(ie + 8)); + } + if (id == WLAN_EID_EXT_CAPAB) { + /* Extended Capabilities */ + int i; + int len = (elen < MAX_EXTCAP) ? elen : + MAX_EXTCAP; + char *p = extcap; + + p += os_snprintf(extcap, sizeof(extcap), + ",extcap:"); + for (i = 0; i < len; i++) { + int lim; + + lim = sizeof(extcap) - + os_strlen(extcap); + if (lim <= 0) + break; + p += os_snprintf(p, lim, "%02x", + *(ie + i)); + } + } + if (id == WLAN_EID_PWR_CAPABILITY && elen == 2) { + /* TX Power */ + os_snprintf(txpow, sizeof(txpow), + ",txpow:%04hx", + WPA_GET_LE16(ie)); + } + + ret = os_snprintf(fpos, fend - fpos, "%s%d", sep, id); + } + if (os_snprintf_error(fend - fpos, ret)) + goto fail; + fpos += ret; + + ie += elen; + ie_len -= elen; + } + + ret = os_snprintf(fpos, fend - fpos, "%s%s%s%s%s%s%s%s%s", + htcap, htagg, htmcs, vhtcap, vhtrxmcs, vhttxmcs, + txpow, extcap, wps); + if (os_snprintf_error(fend - fpos, ret)) { + fail: + fstr[0] = '\0'; + } +} + + +int retrieve_sta_taxonomy(const struct hostapd_data *hapd, + struct sta_info *sta, char *buf, size_t buflen) +{ + int ret; + char *pos, *end; + + if (!sta->probe_ie_taxonomy || !sta->assoc_ie_taxonomy) + return 0; + + ret = os_snprintf(buf, buflen, "wifi4|probe:"); + if (os_snprintf_error(buflen, ret)) + return 0; + pos = buf + ret; + end = buf + buflen; + + ie_to_string(pos, end - pos, sta->probe_ie_taxonomy); + pos = os_strchr(pos, '\0'); + if (pos >= end) + return 0; + ret = os_snprintf(pos, end - pos, "|assoc:"); + if (os_snprintf_error(end - pos, ret)) + return 0; + pos += ret; + ie_to_string(pos, end - pos, sta->assoc_ie_taxonomy); + pos = os_strchr(pos, '\0'); + return pos - buf; +} + + +void taxonomy_sta_info_probe_req(const struct hostapd_data *hapd, + struct sta_info *sta, + const u8 *ie, size_t ie_len) +{ + wpabuf_free(sta->probe_ie_taxonomy); + sta->probe_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len); +} + + +void taxonomy_hostapd_sta_info_probe_req(const struct hostapd_data *hapd, + struct hostapd_sta_info *info, + const u8 *ie, size_t ie_len) +{ + wpabuf_free(info->probe_ie_taxonomy); + info->probe_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len); +} + + +void taxonomy_sta_info_assoc_req(const struct hostapd_data *hapd, + struct sta_info *sta, + const u8 *ie, size_t ie_len) +{ + wpabuf_free(sta->assoc_ie_taxonomy); + sta->assoc_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len); +} diff --git a/contrib/wpa/src/ap/taxonomy.h b/contrib/wpa/src/ap/taxonomy.h new file mode 100644 index 000000000000..80f245c77c82 --- /dev/null +++ b/contrib/wpa/src/ap/taxonomy.h @@ -0,0 +1,24 @@ +/* + * hostapd / Station client taxonomy + * Copyright (c) 2015 Google, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef TAXONOMY_H +#define TAXONOMY_H + +void taxonomy_sta_info_probe_req(const struct hostapd_data *hapd, + struct sta_info *sta, + const u8 *ie, size_t ie_len); +void taxonomy_hostapd_sta_info_probe_req(const struct hostapd_data *hapd, + struct hostapd_sta_info *sta, + const u8 *ie, size_t ie_len); +void taxonomy_sta_info_assoc_req(const struct hostapd_data *hapd, + struct sta_info *sta, + const u8 *ie, size_t ie_len); +int retrieve_sta_taxonomy(const struct hostapd_data *hapd, + struct sta_info *sta, char *buf, size_t buflen); + +#endif /* TAXONOMY_H */ diff --git a/contrib/wpa/src/ap/vlan.c b/contrib/wpa/src/ap/vlan.c new file mode 100644 index 000000000000..b6f6bb1afe05 --- /dev/null +++ b/contrib/wpa/src/ap/vlan.c @@ -0,0 +1,34 @@ +/* + * hostapd / VLAN definition + * Copyright (c) 2016, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "ap/vlan.h" + +/* compare the two arguments, NULL is treated as empty + * return zero iff they are equal + */ +int vlan_compare(struct vlan_description *a, struct vlan_description *b) +{ + int i; + const int a_empty = !a || !a->notempty; + const int b_empty = !b || !b->notempty; + + if (a_empty && b_empty) + return 0; + if (a_empty || b_empty) + return 1; + if (a->untagged != b->untagged) + return 1; + for (i = 0; i < MAX_NUM_TAGGED_VLAN; i++) { + if (a->tagged[i] != b->tagged[i]) + return 1; + } + return 0; +} diff --git a/contrib/wpa/src/ap/vlan.h b/contrib/wpa/src/ap/vlan.h new file mode 100644 index 000000000000..af84929decdc --- /dev/null +++ b/contrib/wpa/src/ap/vlan.h @@ -0,0 +1,30 @@ +/* + * hostapd / VLAN definition + * Copyright (c) 2015, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef VLAN_H +#define VLAN_H + +#define MAX_NUM_TAGGED_VLAN 32 + +struct vlan_description { + int notempty; /* 0 : no vlan information present, 1: else */ + int untagged; /* >0 802.1q vid */ + int tagged[MAX_NUM_TAGGED_VLAN]; /* first k items, ascending order */ +}; + +#ifndef CONFIG_NO_VLAN +int vlan_compare(struct vlan_description *a, struct vlan_description *b); +#else /* CONFIG_NO_VLAN */ +static inline int +vlan_compare(struct vlan_description *a, struct vlan_description *b) +{ + return 0; +} +#endif /* CONFIG_NO_VLAN */ + +#endif /* VLAN_H */ diff --git a/contrib/wpa/src/ap/vlan_full.c b/contrib/wpa/src/ap/vlan_full.c new file mode 100644 index 000000000000..aa42335b96a1 --- /dev/null +++ b/contrib/wpa/src/ap/vlan_full.c @@ -0,0 +1,752 @@ +/* + * hostapd / VLAN initialization - full dynamic VLAN + * Copyright 2003, Instant802 Networks, Inc. + * Copyright 2005-2006, Devicescape Software, Inc. + * Copyright (c) 2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#include +/* Avoid conflicts due to NetBSD net/if.h if_type define with driver.h */ +#undef if_type +#include + +#include "utils/common.h" +#include "drivers/priv_netlink.h" +#include "common/linux_bridge.h" +#include "common/linux_vlan.h" +#include "utils/eloop.h" +#include "hostapd.h" +#include "ap_config.h" +#include "ap_drv_ops.h" +#include "wpa_auth.h" +#include "vlan_init.h" +#include "vlan_util.h" + + +struct full_dynamic_vlan { + int s; /* socket on which to listen for new/removed interfaces. */ +}; + +#define DVLAN_CLEAN_BR 0x1 +#define DVLAN_CLEAN_VLAN 0x2 +#define DVLAN_CLEAN_VLAN_PORT 0x4 + +struct dynamic_iface { + char ifname[IFNAMSIZ + 1]; + int usage; + int clean; + struct dynamic_iface *next; +}; + + +/* Increment ref counter for ifname and add clean flag. + * If not in list, add it only if some flags are given. + */ +static void dyn_iface_get(struct hostapd_data *hapd, const char *ifname, + int clean) +{ + struct dynamic_iface *next, **dynamic_ifaces; + struct hapd_interfaces *interfaces; + + interfaces = hapd->iface->interfaces; + dynamic_ifaces = &interfaces->vlan_priv; + + for (next = *dynamic_ifaces; next; next = next->next) { + if (os_strcmp(ifname, next->ifname) == 0) + break; + } + + if (next) { + next->usage++; + next->clean |= clean; + return; + } + + if (!clean) + return; + + next = os_zalloc(sizeof(*next)); + if (!next) + return; + os_strlcpy(next->ifname, ifname, sizeof(next->ifname)); + next->usage = 1; + next->clean = clean; + next->next = *dynamic_ifaces; + *dynamic_ifaces = next; +} + + +/* Decrement reference counter for given ifname. + * Return clean flag iff reference counter was decreased to zero, else zero + */ +static int dyn_iface_put(struct hostapd_data *hapd, const char *ifname) +{ + struct dynamic_iface *next, *prev = NULL, **dynamic_ifaces; + struct hapd_interfaces *interfaces; + int clean; + + interfaces = hapd->iface->interfaces; + dynamic_ifaces = &interfaces->vlan_priv; + + for (next = *dynamic_ifaces; next; next = next->next) { + if (os_strcmp(ifname, next->ifname) == 0) + break; + prev = next; + } + + if (!next) + return 0; + + next->usage--; + if (next->usage) + return 0; + + if (prev) + prev->next = next->next; + else + *dynamic_ifaces = next->next; + clean = next->clean; + os_free(next); + + return clean; +} + + +static int ifconfig_down(const char *if_name) +{ + wpa_printf(MSG_DEBUG, "VLAN: Set interface %s down", if_name); + return ifconfig_helper(if_name, 0); +} + + +/* This value should be 256 ONLY. If it is something else, then hostapd + * might crash!, as this value has been hard-coded in 2.4.x kernel + * bridging code. + */ +#define MAX_BR_PORTS 256 + +static int br_delif(const char *br_name, const char *if_name) +{ + int fd; + struct ifreq ifr; + unsigned long args[2]; + int if_index; + + wpa_printf(MSG_DEBUG, "VLAN: br_delif(%s, %s)", br_name, if_name); + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + if_index = if_nametoindex(if_name); + + if (if_index == 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining " + "interface index for '%s'", + __func__, if_name); + close(fd); + return -1; + } + + args[0] = BRCTL_DEL_IF; + args[1] = if_index; + + os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); + ifr.ifr_data = (void *) args; + + if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0 && errno != EINVAL) { + /* No error if interface already removed. */ + wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE," + "BRCTL_DEL_IF] failed for br_name=%s if_name=%s: " + "%s", __func__, br_name, if_name, strerror(errno)); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +/* + Add interface 'if_name' to the bridge 'br_name' + + returns -1 on error + returns 1 if the interface is already part of the bridge + returns 0 otherwise +*/ +static int br_addif(const char *br_name, const char *if_name) +{ + int fd; + struct ifreq ifr; + unsigned long args[2]; + int if_index; + + wpa_printf(MSG_DEBUG, "VLAN: br_addif(%s, %s)", br_name, if_name); + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + if_index = if_nametoindex(if_name); + + if (if_index == 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining " + "interface index for '%s'", + __func__, if_name); + close(fd); + return -1; + } + + args[0] = BRCTL_ADD_IF; + args[1] = if_index; + + os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); + ifr.ifr_data = (void *) args; + + if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { + if (errno == EBUSY) { + /* The interface is already added. */ + close(fd); + return 1; + } + + wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE," + "BRCTL_ADD_IF] failed for br_name=%s if_name=%s: " + "%s", __func__, br_name, if_name, strerror(errno)); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +static int br_delbr(const char *br_name) +{ + int fd; + unsigned long arg[2]; + + wpa_printf(MSG_DEBUG, "VLAN: br_delbr(%s)", br_name); + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + arg[0] = BRCTL_DEL_BRIDGE; + arg[1] = (unsigned long) br_name; + + if (ioctl(fd, SIOCGIFBR, arg) < 0 && errno != ENXIO) { + /* No error if bridge already removed. */ + wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_DEL_BRIDGE failed for " + "%s: %s", __func__, br_name, strerror(errno)); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +/* + Add a bridge with the name 'br_name'. + + returns -1 on error + returns 1 if the bridge already exists + returns 0 otherwise +*/ +static int br_addbr(const char *br_name) +{ + int fd; + unsigned long arg[4]; + struct ifreq ifr; + + wpa_printf(MSG_DEBUG, "VLAN: br_addbr(%s)", br_name); + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + arg[0] = BRCTL_ADD_BRIDGE; + arg[1] = (unsigned long) br_name; + + if (ioctl(fd, SIOCGIFBR, arg) < 0) { + if (errno == EEXIST) { + /* The bridge is already added. */ + close(fd); + return 1; + } else { + wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_ADD_BRIDGE " + "failed for %s: %s", + __func__, br_name, strerror(errno)); + close(fd); + return -1; + } + } + + /* Decrease forwarding delay to avoid EAPOL timeouts. */ + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, br_name, IFNAMSIZ); + arg[0] = BRCTL_SET_BRIDGE_FORWARD_DELAY; + arg[1] = 1; + arg[2] = 0; + arg[3] = 0; + ifr.ifr_data = (char *) &arg; + if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: " + "BRCTL_SET_BRIDGE_FORWARD_DELAY (1 sec) failed for " + "%s: %s", __func__, br_name, strerror(errno)); + /* Continue anyway */ + } + + close(fd); + return 0; +} + + +static int br_getnumports(const char *br_name) +{ + int fd; + int i; + int port_cnt = 0; + unsigned long arg[4]; + int ifindices[MAX_BR_PORTS]; + struct ifreq ifr; + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + arg[0] = BRCTL_GET_PORT_LIST; + arg[1] = (unsigned long) ifindices; + arg[2] = MAX_BR_PORTS; + arg[3] = 0; + + os_memset(ifindices, 0, sizeof(ifindices)); + os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); + ifr.ifr_data = (void *) arg; + + if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_GET_PORT_LIST " + "failed for %s: %s", + __func__, br_name, strerror(errno)); + close(fd); + return -1; + } + + for (i = 1; i < MAX_BR_PORTS; i++) { + if (ifindices[i] > 0) { + port_cnt++; + } + } + + close(fd); + return port_cnt; +} + + +static void vlan_newlink_tagged(int vlan_naming, const char *tagged_interface, + const char *br_name, int vid, + struct hostapd_data *hapd) +{ + char vlan_ifname[IFNAMSIZ]; + int clean; + + if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE) + os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d", + tagged_interface, vid); + else + os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d", vid); + + clean = 0; + ifconfig_up(tagged_interface); + if (!vlan_add(tagged_interface, vid, vlan_ifname)) + clean |= DVLAN_CLEAN_VLAN; + + if (!br_addif(br_name, vlan_ifname)) + clean |= DVLAN_CLEAN_VLAN_PORT; + + dyn_iface_get(hapd, vlan_ifname, clean); + + ifconfig_up(vlan_ifname); +} + + +static void vlan_bridge_name(char *br_name, struct hostapd_data *hapd, int vid) +{ + char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; + + if (hapd->conf->vlan_bridge[0]) { + os_snprintf(br_name, IFNAMSIZ, "%s%d", + hapd->conf->vlan_bridge, vid); + } else if (tagged_interface) { + os_snprintf(br_name, IFNAMSIZ, "br%s.%d", + tagged_interface, vid); + } else { + os_snprintf(br_name, IFNAMSIZ, "brvlan%d", vid); + } +} + + +static void vlan_get_bridge(const char *br_name, struct hostapd_data *hapd, + int vid) +{ + char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; + int vlan_naming = hapd->conf->ssid.vlan_naming; + + dyn_iface_get(hapd, br_name, br_addbr(br_name) ? 0 : DVLAN_CLEAN_BR); + + ifconfig_up(br_name); + + if (tagged_interface) + vlan_newlink_tagged(vlan_naming, tagged_interface, br_name, + vid, hapd); +} + + +void vlan_newlink(const char *ifname, struct hostapd_data *hapd) +{ + char br_name[IFNAMSIZ]; + struct hostapd_vlan *vlan; + int untagged, *tagged, i, notempty; + + wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname); + + for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) { + if (vlan->configured || + os_strcmp(ifname, vlan->ifname) != 0) + continue; + break; + } + if (!vlan) + return; + + vlan->configured = 1; + + notempty = vlan->vlan_desc.notempty; + untagged = vlan->vlan_desc.untagged; + tagged = vlan->vlan_desc.tagged; + + if (!notempty) { + /* Non-VLAN STA */ + if (hapd->conf->bridge[0] && + !br_addif(hapd->conf->bridge, ifname)) + vlan->clean |= DVLAN_CLEAN_WLAN_PORT; + } else if (untagged > 0 && untagged <= MAX_VLAN_ID) { + vlan_bridge_name(br_name, hapd, untagged); + + vlan_get_bridge(br_name, hapd, untagged); + + if (!br_addif(br_name, ifname)) + vlan->clean |= DVLAN_CLEAN_WLAN_PORT; + } + + for (i = 0; i < MAX_NUM_TAGGED_VLAN && tagged[i]; i++) { + if (tagged[i] == untagged || + tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID || + (i > 0 && tagged[i] == tagged[i - 1])) + continue; + vlan_bridge_name(br_name, hapd, tagged[i]); + vlan_get_bridge(br_name, hapd, tagged[i]); + vlan_newlink_tagged(DYNAMIC_VLAN_NAMING_WITH_DEVICE, + ifname, br_name, tagged[i], hapd); + } + + ifconfig_up(ifname); +} + + +static void vlan_dellink_tagged(int vlan_naming, const char *tagged_interface, + const char *br_name, int vid, + struct hostapd_data *hapd) +{ + char vlan_ifname[IFNAMSIZ]; + int clean; + + if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE) + os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d", + tagged_interface, vid); + else + os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d", vid); + + clean = dyn_iface_put(hapd, vlan_ifname); + + if (clean & DVLAN_CLEAN_VLAN_PORT) + br_delif(br_name, vlan_ifname); + + if (clean & DVLAN_CLEAN_VLAN) { + ifconfig_down(vlan_ifname); + vlan_rem(vlan_ifname); + } +} + + +static void vlan_put_bridge(const char *br_name, struct hostapd_data *hapd, + int vid) +{ + int clean; + char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; + int vlan_naming = hapd->conf->ssid.vlan_naming; + + if (tagged_interface) + vlan_dellink_tagged(vlan_naming, tagged_interface, br_name, + vid, hapd); + + clean = dyn_iface_put(hapd, br_name); + if ((clean & DVLAN_CLEAN_BR) && br_getnumports(br_name) == 0) { + ifconfig_down(br_name); + br_delbr(br_name); + } +} + + +void vlan_dellink(const char *ifname, struct hostapd_data *hapd) +{ + struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan; + + wpa_printf(MSG_DEBUG, "VLAN: vlan_dellink(%s)", ifname); + + first = prev = vlan; + + while (vlan) { + if (os_strcmp(ifname, vlan->ifname) != 0) { + prev = vlan; + vlan = vlan->next; + continue; + } + break; + } + if (!vlan) + return; + + if (vlan->configured) { + int notempty = vlan->vlan_desc.notempty; + int untagged = vlan->vlan_desc.untagged; + int *tagged = vlan->vlan_desc.tagged; + char br_name[IFNAMSIZ]; + int i; + + for (i = 0; i < MAX_NUM_TAGGED_VLAN && tagged[i]; i++) { + if (tagged[i] == untagged || + tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID || + (i > 0 && tagged[i] == tagged[i - 1])) + continue; + vlan_bridge_name(br_name, hapd, tagged[i]); + vlan_dellink_tagged(DYNAMIC_VLAN_NAMING_WITH_DEVICE, + ifname, br_name, tagged[i], hapd); + vlan_put_bridge(br_name, hapd, tagged[i]); + } + + if (!notempty) { + /* Non-VLAN STA */ + if (hapd->conf->bridge[0] && + (vlan->clean & DVLAN_CLEAN_WLAN_PORT)) + br_delif(hapd->conf->bridge, ifname); + } else if (untagged > 0 && untagged <= MAX_VLAN_ID) { + vlan_bridge_name(br_name, hapd, untagged); + + if (vlan->clean & DVLAN_CLEAN_WLAN_PORT) + br_delif(br_name, vlan->ifname); + + vlan_put_bridge(br_name, hapd, untagged); + } + } + + /* + * Ensure this VLAN interface is actually removed even if + * NEWLINK message is only received later. + */ + if (if_nametoindex(vlan->ifname) && vlan_if_remove(hapd, vlan)) + wpa_printf(MSG_ERROR, + "VLAN: Could not remove VLAN iface: %s: %s", + vlan->ifname, strerror(errno)); + + if (vlan == first) + hapd->conf->vlan = vlan->next; + else + prev->next = vlan->next; + + os_free(vlan); +} + + +static void +vlan_read_ifnames(struct nlmsghdr *h, size_t len, int del, + struct hostapd_data *hapd) +{ + struct ifinfomsg *ifi; + int attrlen, nlmsg_len, rta_len; + struct rtattr *attr; + char ifname[IFNAMSIZ + 1]; + + if (len < sizeof(*ifi)) + return; + + ifi = NLMSG_DATA(h); + + nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); + + attrlen = h->nlmsg_len - nlmsg_len; + if (attrlen < 0) + return; + + attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); + + os_memset(ifname, 0, sizeof(ifname)); + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_IFNAME) { + int n = attr->rta_len - rta_len; + if (n < 0) + break; + + if ((size_t) n >= sizeof(ifname)) + n = sizeof(ifname) - 1; + os_memcpy(ifname, ((char *) attr) + rta_len, n); + + } + + attr = RTA_NEXT(attr, attrlen); + } + + if (!ifname[0]) + return; + if (del && if_nametoindex(ifname)) { + /* interface still exists, race condition -> + * iface has just been recreated */ + return; + } + + wpa_printf(MSG_DEBUG, + "VLAN: RTM_%sLINK: ifi_index=%d ifname=%s ifi_family=%d ifi_flags=0x%x (%s%s%s%s)", + del ? "DEL" : "NEW", + ifi->ifi_index, ifname, ifi->ifi_family, ifi->ifi_flags, + (ifi->ifi_flags & IFF_UP) ? "[UP]" : "", + (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "", + (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", + (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); + + if (del) + vlan_dellink(ifname, hapd); + else + vlan_newlink(ifname, hapd); +} + + +static void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + char buf[8192]; + int left; + struct sockaddr_nl from; + socklen_t fromlen; + struct nlmsghdr *h; + struct hostapd_data *hapd = eloop_ctx; + + fromlen = sizeof(from); + left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, + (struct sockaddr *) &from, &fromlen); + if (left < 0) { + if (errno != EINTR && errno != EAGAIN) + wpa_printf(MSG_ERROR, "VLAN: %s: recvfrom failed: %s", + __func__, strerror(errno)); + return; + } + + h = (struct nlmsghdr *) buf; + while (NLMSG_OK(h, left)) { + int len, plen; + + len = h->nlmsg_len; + plen = len - sizeof(*h); + if (len > left || plen < 0) { + wpa_printf(MSG_DEBUG, "VLAN: Malformed netlink " + "message: len=%d left=%d plen=%d", + len, left, plen); + break; + } + + switch (h->nlmsg_type) { + case RTM_NEWLINK: + vlan_read_ifnames(h, plen, 0, hapd); + break; + case RTM_DELLINK: + vlan_read_ifnames(h, plen, 1, hapd); + break; + } + + h = NLMSG_NEXT(h, left); + } + + if (left > 0) { + wpa_printf(MSG_DEBUG, "VLAN: %s: %d extra bytes in the end of " + "netlink message", __func__, left); + } +} + + +struct full_dynamic_vlan * +full_dynamic_vlan_init(struct hostapd_data *hapd) +{ + struct sockaddr_nl local; + struct full_dynamic_vlan *priv; + + priv = os_zalloc(sizeof(*priv)); + if (priv == NULL) + return NULL; + + vlan_set_name_type(hapd->conf->ssid.vlan_naming == + DYNAMIC_VLAN_NAMING_WITH_DEVICE ? + VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD : + VLAN_NAME_TYPE_PLUS_VID_NO_PAD); + + priv->s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (priv->s < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(PF_NETLINK,SOCK_RAW," + "NETLINK_ROUTE) failed: %s", + __func__, strerror(errno)); + os_free(priv); + return NULL; + } + + os_memset(&local, 0, sizeof(local)); + local.nl_family = AF_NETLINK; + local.nl_groups = RTMGRP_LINK; + if (bind(priv->s, (struct sockaddr *) &local, sizeof(local)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: bind(netlink) failed: %s", + __func__, strerror(errno)); + close(priv->s); + os_free(priv); + return NULL; + } + + if (eloop_register_read_sock(priv->s, vlan_event_receive, hapd, NULL)) + { + close(priv->s); + os_free(priv); + return NULL; + } + + return priv; +} + + +void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv) +{ + if (priv == NULL) + return; + eloop_unregister_read_sock(priv->s); + close(priv->s); + os_free(priv); +} diff --git a/contrib/wpa/src/ap/vlan_ifconfig.c b/contrib/wpa/src/ap/vlan_ifconfig.c new file mode 100644 index 000000000000..ef953a5c4c95 --- /dev/null +++ b/contrib/wpa/src/ap/vlan_ifconfig.c @@ -0,0 +1,69 @@ +/* + * hostapd / VLAN ifconfig helpers + * Copyright 2003, Instant802 Networks, Inc. + * Copyright 2005-2006, Devicescape Software, Inc. + * Copyright (c) 2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#include +#include + +#include "utils/common.h" +#include "vlan_util.h" + + +int ifconfig_helper(const char *if_name, int up) +{ + int fd; + struct ifreq ifr; + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, if_name, IFNAMSIZ); + + if (ioctl(fd, SIOCGIFFLAGS, &ifr) != 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCGIFFLAGS) failed " + "for interface %s: %s", + __func__, if_name, strerror(errno)); + close(fd); + return -1; + } + + if (up) + ifr.ifr_flags |= IFF_UP; + else + ifr.ifr_flags &= ~IFF_UP; + + if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCSIFFLAGS) failed " + "for interface %s (up=%d): %s", + __func__, if_name, up, strerror(errno)); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +int ifconfig_up(const char *if_name) +{ + wpa_printf(MSG_DEBUG, "VLAN: Set interface %s up", if_name); + return ifconfig_helper(if_name, 1); +} + + +int iface_exists(const char *ifname) +{ + return if_nametoindex(ifname); +} diff --git a/contrib/wpa/src/ap/vlan_init.c b/contrib/wpa/src/ap/vlan_init.c index fd1c8ddacee6..31e4fc6b396a 100644 --- a/contrib/wpa/src/ap/vlan_init.c +++ b/contrib/wpa/src/ap/vlan_init.c @@ -9,902 +9,72 @@ */ #include "utils/includes.h" -#ifdef CONFIG_FULL_DYNAMIC_VLAN -#include -#include -#include -#include -#include -#endif /* CONFIG_FULL_DYNAMIC_VLAN */ #include "utils/common.h" #include "hostapd.h" #include "ap_config.h" #include "ap_drv_ops.h" +#include "wpa_auth.h" #include "vlan_init.h" #include "vlan_util.h" -#ifdef CONFIG_FULL_DYNAMIC_VLAN - -#include "drivers/priv_netlink.h" -#include "utils/eloop.h" - - -struct full_dynamic_vlan { - int s; /* socket on which to listen for new/removed interfaces. */ -}; - -#define DVLAN_CLEAN_BR 0x1 -#define DVLAN_CLEAN_VLAN 0x2 -#define DVLAN_CLEAN_VLAN_PORT 0x4 - -struct dynamic_iface { - char ifname[IFNAMSIZ + 1]; - int usage; - int clean; - struct dynamic_iface *next; -}; - - -/* Increment ref counter for ifname and add clean flag. - * If not in list, add it only if some flags are given. - */ -static void dyn_iface_get(struct hostapd_data *hapd, const char *ifname, - int clean) +static int vlan_if_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan, + int existsok) { - struct dynamic_iface *next, **dynamic_ifaces; - struct hapd_interfaces *interfaces; + int ret, i; - interfaces = hapd->iface->interfaces; - dynamic_ifaces = &interfaces->vlan_priv; - - for (next = *dynamic_ifaces; next; next = next->next) { - if (os_strcmp(ifname, next->ifname) == 0) - break; + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (!hapd->conf->ssid.wep.key[i]) + continue; + wpa_printf(MSG_ERROR, + "VLAN: Refusing to set up VLAN iface %s with WEP", + vlan->ifname); + return -1; } - if (next) { - next->usage++; - next->clean |= clean; - return; - } - - if (!clean) - return; - - next = os_zalloc(sizeof(*next)); - if (!next) - return; - os_strlcpy(next->ifname, ifname, sizeof(next->ifname)); - next->usage = 1; - next->clean = clean; - next->next = *dynamic_ifaces; - *dynamic_ifaces = next; -} - - -/* Decrement reference counter for given ifname. - * Return clean flag iff reference counter was decreased to zero, else zero - */ -static int dyn_iface_put(struct hostapd_data *hapd, const char *ifname) -{ - struct dynamic_iface *next, *prev = NULL, **dynamic_ifaces; - struct hapd_interfaces *interfaces; - int clean; - - interfaces = hapd->iface->interfaces; - dynamic_ifaces = &interfaces->vlan_priv; - - for (next = *dynamic_ifaces; next; next = next->next) { - if (os_strcmp(ifname, next->ifname) == 0) - break; - prev = next; - } - - if (!next) - return 0; - - next->usage--; - if (next->usage) - return 0; - - if (prev) - prev->next = next->next; + if (!iface_exists(vlan->ifname)) + ret = hostapd_vlan_if_add(hapd, vlan->ifname); + else if (!existsok) + return -1; else - *dynamic_ifaces = next->next; - clean = next->clean; - os_free(next); + ret = 0; - return clean; + if (ret) + return ret; + + ifconfig_up(vlan->ifname); /* else wpa group will fail fatal */ + + if (hapd->wpa_auth) + ret = wpa_auth_ensure_group(hapd->wpa_auth, vlan->vlan_id); + + if (ret == 0) + return ret; + + wpa_printf(MSG_ERROR, "WPA initialization for VLAN %d failed (%d)", + vlan->vlan_id, ret); + if (wpa_auth_release_group(hapd->wpa_auth, vlan->vlan_id)) + wpa_printf(MSG_ERROR, "WPA deinit of %s failed", vlan->ifname); + + /* group state machine setup failed */ + if (hostapd_vlan_if_remove(hapd, vlan->ifname)) + wpa_printf(MSG_ERROR, "Removal of %s failed", vlan->ifname); + + return ret; } -static int ifconfig_helper(const char *if_name, int up) +int vlan_if_remove(struct hostapd_data *hapd, struct hostapd_vlan *vlan) { - int fd; - struct ifreq ifr; + int ret; - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " - "failed: %s", __func__, strerror(errno)); - return -1; - } + ret = wpa_auth_release_group(hapd->wpa_auth, vlan->vlan_id); + if (ret) + wpa_printf(MSG_ERROR, + "WPA deinitialization for VLAN %d failed (%d)", + vlan->vlan_id, ret); - os_memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, if_name, IFNAMSIZ); - - if (ioctl(fd, SIOCGIFFLAGS, &ifr) != 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCGIFFLAGS) failed " - "for interface %s: %s", - __func__, if_name, strerror(errno)); - close(fd); - return -1; - } - - if (up) - ifr.ifr_flags |= IFF_UP; - else - ifr.ifr_flags &= ~IFF_UP; - - if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCSIFFLAGS) failed " - "for interface %s (up=%d): %s", - __func__, if_name, up, strerror(errno)); - close(fd); - return -1; - } - - close(fd); - return 0; -} - - -static int ifconfig_up(const char *if_name) -{ - wpa_printf(MSG_DEBUG, "VLAN: Set interface %s up", if_name); - return ifconfig_helper(if_name, 1); -} - - -static int ifconfig_down(const char *if_name) -{ - wpa_printf(MSG_DEBUG, "VLAN: Set interface %s down", if_name); - return ifconfig_helper(if_name, 0); -} - - -/* - * These are only available in recent linux headers (without the leading - * underscore). - */ -#define _GET_VLAN_REALDEV_NAME_CMD 8 -#define _GET_VLAN_VID_CMD 9 - -/* This value should be 256 ONLY. If it is something else, then hostapd - * might crash!, as this value has been hard-coded in 2.4.x kernel - * bridging code. - */ -#define MAX_BR_PORTS 256 - -static int br_delif(const char *br_name, const char *if_name) -{ - int fd; - struct ifreq ifr; - unsigned long args[2]; - int if_index; - - wpa_printf(MSG_DEBUG, "VLAN: br_delif(%s, %s)", br_name, if_name); - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " - "failed: %s", __func__, strerror(errno)); - return -1; - } - - if_index = if_nametoindex(if_name); - - if (if_index == 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining " - "interface index for '%s'", - __func__, if_name); - close(fd); - return -1; - } - - args[0] = BRCTL_DEL_IF; - args[1] = if_index; - - os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); - ifr.ifr_data = (__caddr_t) args; - - if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0 && errno != EINVAL) { - /* No error if interface already removed. */ - wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE," - "BRCTL_DEL_IF] failed for br_name=%s if_name=%s: " - "%s", __func__, br_name, if_name, strerror(errno)); - close(fd); - return -1; - } - - close(fd); - return 0; -} - - -/* - Add interface 'if_name' to the bridge 'br_name' - - returns -1 on error - returns 1 if the interface is already part of the bridge - returns 0 otherwise -*/ -static int br_addif(const char *br_name, const char *if_name) -{ - int fd; - struct ifreq ifr; - unsigned long args[2]; - int if_index; - - wpa_printf(MSG_DEBUG, "VLAN: br_addif(%s, %s)", br_name, if_name); - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " - "failed: %s", __func__, strerror(errno)); - return -1; - } - - if_index = if_nametoindex(if_name); - - if (if_index == 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining " - "interface index for '%s'", - __func__, if_name); - close(fd); - return -1; - } - - args[0] = BRCTL_ADD_IF; - args[1] = if_index; - - os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); - ifr.ifr_data = (__caddr_t) args; - - if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { - if (errno == EBUSY) { - /* The interface is already added. */ - close(fd); - return 1; - } - - wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE," - "BRCTL_ADD_IF] failed for br_name=%s if_name=%s: " - "%s", __func__, br_name, if_name, strerror(errno)); - close(fd); - return -1; - } - - close(fd); - return 0; -} - - -static int br_delbr(const char *br_name) -{ - int fd; - unsigned long arg[2]; - - wpa_printf(MSG_DEBUG, "VLAN: br_delbr(%s)", br_name); - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " - "failed: %s", __func__, strerror(errno)); - return -1; - } - - arg[0] = BRCTL_DEL_BRIDGE; - arg[1] = (unsigned long) br_name; - - if (ioctl(fd, SIOCGIFBR, arg) < 0 && errno != ENXIO) { - /* No error if bridge already removed. */ - wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_DEL_BRIDGE failed for " - "%s: %s", __func__, br_name, strerror(errno)); - close(fd); - return -1; - } - - close(fd); - return 0; -} - - -/* - Add a bridge with the name 'br_name'. - - returns -1 on error - returns 1 if the bridge already exists - returns 0 otherwise -*/ -static int br_addbr(const char *br_name) -{ - int fd; - unsigned long arg[4]; - struct ifreq ifr; - - wpa_printf(MSG_DEBUG, "VLAN: br_addbr(%s)", br_name); - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " - "failed: %s", __func__, strerror(errno)); - return -1; - } - - arg[0] = BRCTL_ADD_BRIDGE; - arg[1] = (unsigned long) br_name; - - if (ioctl(fd, SIOCGIFBR, arg) < 0) { - if (errno == EEXIST) { - /* The bridge is already added. */ - close(fd); - return 1; - } else { - wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_ADD_BRIDGE " - "failed for %s: %s", - __func__, br_name, strerror(errno)); - close(fd); - return -1; - } - } - - /* Decrease forwarding delay to avoid EAPOL timeouts. */ - os_memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, br_name, IFNAMSIZ); - arg[0] = BRCTL_SET_BRIDGE_FORWARD_DELAY; - arg[1] = 1; - arg[2] = 0; - arg[3] = 0; - ifr.ifr_data = (char *) &arg; - if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: " - "BRCTL_SET_BRIDGE_FORWARD_DELAY (1 sec) failed for " - "%s: %s", __func__, br_name, strerror(errno)); - /* Continue anyway */ - } - - close(fd); - return 0; -} - - -static int br_getnumports(const char *br_name) -{ - int fd; - int i; - int port_cnt = 0; - unsigned long arg[4]; - int ifindices[MAX_BR_PORTS]; - struct ifreq ifr; - - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " - "failed: %s", __func__, strerror(errno)); - return -1; - } - - arg[0] = BRCTL_GET_PORT_LIST; - arg[1] = (unsigned long) ifindices; - arg[2] = MAX_BR_PORTS; - arg[3] = 0; - - os_memset(ifindices, 0, sizeof(ifindices)); - os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); - ifr.ifr_data = (__caddr_t) arg; - - if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_GET_PORT_LIST " - "failed for %s: %s", - __func__, br_name, strerror(errno)); - close(fd); - return -1; - } - - for (i = 1; i < MAX_BR_PORTS; i++) { - if (ifindices[i] > 0) { - port_cnt++; - } - } - - close(fd); - return port_cnt; -} - - -#ifndef CONFIG_VLAN_NETLINK - -int vlan_rem(const char *if_name) -{ - int fd; - struct vlan_ioctl_args if_request; - - wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(%s)", if_name); - if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) { - wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'", - if_name); - return -1; - } - - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " - "failed: %s", __func__, strerror(errno)); - return -1; - } - - os_memset(&if_request, 0, sizeof(if_request)); - - os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1)); - if_request.cmd = DEL_VLAN_CMD; - - if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: DEL_VLAN_CMD failed for %s: " - "%s", __func__, if_name, strerror(errno)); - close(fd); - return -1; - } - - close(fd); - return 0; -} - - -/* - Add a vlan interface with VLAN ID 'vid' and tagged interface - 'if_name'. - - returns -1 on error - returns 1 if the interface already exists - returns 0 otherwise -*/ -int vlan_add(const char *if_name, int vid, const char *vlan_if_name) -{ - int fd; - struct vlan_ioctl_args if_request; - - wpa_printf(MSG_DEBUG, "VLAN: vlan_add(if_name=%s, vid=%d)", - if_name, vid); - ifconfig_up(if_name); - - if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) { - wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'", - if_name); - return -1; - } - - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " - "failed: %s", __func__, strerror(errno)); - return -1; - } - - os_memset(&if_request, 0, sizeof(if_request)); - - /* Determine if a suitable vlan device already exists. */ - - os_snprintf(if_request.device1, sizeof(if_request.device1), "vlan%d", - vid); - - if_request.cmd = _GET_VLAN_VID_CMD; - - if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0) { - - if (if_request.u.VID == vid) { - if_request.cmd = _GET_VLAN_REALDEV_NAME_CMD; - - if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0 && - os_strncmp(if_request.u.device2, if_name, - sizeof(if_request.u.device2)) == 0) { - close(fd); - wpa_printf(MSG_DEBUG, "VLAN: vlan_add: " - "if_name %s exists already", - if_request.device1); - return 1; - } - } - } - - /* A suitable vlan device does not already exist, add one. */ - - os_memset(&if_request, 0, sizeof(if_request)); - os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1)); - if_request.u.VID = vid; - if_request.cmd = ADD_VLAN_CMD; - - if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: ADD_VLAN_CMD failed for %s: " - "%s", - __func__, if_request.device1, strerror(errno)); - close(fd); - return -1; - } - - close(fd); - return 0; -} - - -static int vlan_set_name_type(unsigned int name_type) -{ - int fd; - struct vlan_ioctl_args if_request; - - wpa_printf(MSG_DEBUG, "VLAN: vlan_set_name_type(name_type=%u)", - name_type); - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " - "failed: %s", __func__, strerror(errno)); - return -1; - } - - os_memset(&if_request, 0, sizeof(if_request)); - - if_request.u.name_type = name_type; - if_request.cmd = SET_VLAN_NAME_TYPE_CMD; - if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: SET_VLAN_NAME_TYPE_CMD " - "name_type=%u failed: %s", - __func__, name_type, strerror(errno)); - close(fd); - return -1; - } - - close(fd); - return 0; -} - -#endif /* CONFIG_VLAN_NETLINK */ - - -static void vlan_newlink(char *ifname, struct hostapd_data *hapd) -{ - char vlan_ifname[IFNAMSIZ]; - char br_name[IFNAMSIZ]; - struct hostapd_vlan *vlan = hapd->conf->vlan; - char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; - int vlan_naming = hapd->conf->ssid.vlan_naming; - int clean; - - wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname); - - while (vlan) { - if (os_strcmp(ifname, vlan->ifname) == 0 && !vlan->configured) { - vlan->configured = 1; - - if (hapd->conf->vlan_bridge[0]) { - os_snprintf(br_name, sizeof(br_name), "%s%d", - hapd->conf->vlan_bridge, - vlan->vlan_id); - } else if (tagged_interface) { - os_snprintf(br_name, sizeof(br_name), - "br%s.%d", tagged_interface, - vlan->vlan_id); - } else { - os_snprintf(br_name, sizeof(br_name), - "brvlan%d", vlan->vlan_id); - } - - dyn_iface_get(hapd, br_name, - br_addbr(br_name) ? 0 : DVLAN_CLEAN_BR); - - ifconfig_up(br_name); - - if (tagged_interface) { - if (vlan_naming == - DYNAMIC_VLAN_NAMING_WITH_DEVICE) - os_snprintf(vlan_ifname, - sizeof(vlan_ifname), - "%s.%d", tagged_interface, - vlan->vlan_id); - else - os_snprintf(vlan_ifname, - sizeof(vlan_ifname), - "vlan%d", vlan->vlan_id); - - clean = 0; - ifconfig_up(tagged_interface); - if (!vlan_add(tagged_interface, vlan->vlan_id, - vlan_ifname)) - clean |= DVLAN_CLEAN_VLAN; - - if (!br_addif(br_name, vlan_ifname)) - clean |= DVLAN_CLEAN_VLAN_PORT; - - dyn_iface_get(hapd, vlan_ifname, clean); - - ifconfig_up(vlan_ifname); - } - - if (!br_addif(br_name, ifname)) - vlan->clean |= DVLAN_CLEAN_WLAN_PORT; - - ifconfig_up(ifname); - - break; - } - vlan = vlan->next; - } -} - - -static void vlan_dellink(char *ifname, struct hostapd_data *hapd) -{ - char vlan_ifname[IFNAMSIZ]; - char br_name[IFNAMSIZ]; - struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan; - char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; - int vlan_naming = hapd->conf->ssid.vlan_naming; - int clean; - - wpa_printf(MSG_DEBUG, "VLAN: vlan_dellink(%s)", ifname); - - first = prev = vlan; - - while (vlan) { - if (os_strcmp(ifname, vlan->ifname) == 0 && - vlan->configured) { - if (hapd->conf->vlan_bridge[0]) { - os_snprintf(br_name, sizeof(br_name), "%s%d", - hapd->conf->vlan_bridge, - vlan->vlan_id); - } else if (tagged_interface) { - os_snprintf(br_name, sizeof(br_name), - "br%s.%d", tagged_interface, - vlan->vlan_id); - } else { - os_snprintf(br_name, sizeof(br_name), - "brvlan%d", vlan->vlan_id); - } - - if (vlan->clean & DVLAN_CLEAN_WLAN_PORT) - br_delif(br_name, vlan->ifname); - - if (tagged_interface) { - if (vlan_naming == - DYNAMIC_VLAN_NAMING_WITH_DEVICE) - os_snprintf(vlan_ifname, - sizeof(vlan_ifname), - "%s.%d", tagged_interface, - vlan->vlan_id); - else - os_snprintf(vlan_ifname, - sizeof(vlan_ifname), - "vlan%d", vlan->vlan_id); - - clean = dyn_iface_put(hapd, vlan_ifname); - - if (clean & DVLAN_CLEAN_VLAN_PORT) - br_delif(br_name, vlan_ifname); - - if (clean & DVLAN_CLEAN_VLAN) { - ifconfig_down(vlan_ifname); - vlan_rem(vlan_ifname); - } - } - - clean = dyn_iface_put(hapd, br_name); - if ((clean & DVLAN_CLEAN_BR) && - br_getnumports(br_name) == 0) { - ifconfig_down(br_name); - br_delbr(br_name); - } - } - - if (os_strcmp(ifname, vlan->ifname) == 0) { - if (vlan == first) { - hapd->conf->vlan = vlan->next; - } else { - prev->next = vlan->next; - } - os_free(vlan); - - break; - } - prev = vlan; - vlan = vlan->next; - } -} - - -static void -vlan_read_ifnames(struct nlmsghdr *h, size_t len, int del, - struct hostapd_data *hapd) -{ - struct ifinfomsg *ifi; - int attrlen, nlmsg_len, rta_len; - struct rtattr *attr; - char ifname[IFNAMSIZ + 1]; - - if (len < sizeof(*ifi)) - return; - - ifi = NLMSG_DATA(h); - - nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); - - attrlen = h->nlmsg_len - nlmsg_len; - if (attrlen < 0) - return; - - attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); - - os_memset(ifname, 0, sizeof(ifname)); - rta_len = RTA_ALIGN(sizeof(struct rtattr)); - while (RTA_OK(attr, attrlen)) { - if (attr->rta_type == IFLA_IFNAME) { - int n = attr->rta_len - rta_len; - if (n < 0) - break; - - if ((size_t) n >= sizeof(ifname)) - n = sizeof(ifname) - 1; - os_memcpy(ifname, ((char *) attr) + rta_len, n); - - } - - attr = RTA_NEXT(attr, attrlen); - } - - if (!ifname[0]) - return; - if (del && if_nametoindex(ifname)) { - /* interface still exists, race condition -> - * iface has just been recreated */ - return; - } - - wpa_printf(MSG_DEBUG, - "VLAN: RTM_%sLINK: ifi_index=%d ifname=%s ifi_family=%d ifi_flags=0x%x (%s%s%s%s)", - del ? "DEL" : "NEW", - ifi->ifi_index, ifname, ifi->ifi_family, ifi->ifi_flags, - (ifi->ifi_flags & IFF_UP) ? "[UP]" : "", - (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "", - (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", - (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); - - if (del) - vlan_dellink(ifname, hapd); - else - vlan_newlink(ifname, hapd); -} - - -static void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx) -{ - char buf[8192]; - int left; - struct sockaddr_nl from; - socklen_t fromlen; - struct nlmsghdr *h; - struct hostapd_data *hapd = eloop_ctx; - - fromlen = sizeof(from); - left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, - (struct sockaddr *) &from, &fromlen); - if (left < 0) { - if (errno != EINTR && errno != EAGAIN) - wpa_printf(MSG_ERROR, "VLAN: %s: recvfrom failed: %s", - __func__, strerror(errno)); - return; - } - - h = (struct nlmsghdr *) buf; - while (NLMSG_OK(h, left)) { - int len, plen; - - len = h->nlmsg_len; - plen = len - sizeof(*h); - if (len > left || plen < 0) { - wpa_printf(MSG_DEBUG, "VLAN: Malformed netlink " - "message: len=%d left=%d plen=%d", - len, left, plen); - break; - } - - switch (h->nlmsg_type) { - case RTM_NEWLINK: - vlan_read_ifnames(h, plen, 0, hapd); - break; - case RTM_DELLINK: - vlan_read_ifnames(h, plen, 1, hapd); - break; - } - - h = NLMSG_NEXT(h, left); - } - - if (left > 0) { - wpa_printf(MSG_DEBUG, "VLAN: %s: %d extra bytes in the end of " - "netlink message", __func__, left); - } -} - - -static struct full_dynamic_vlan * -full_dynamic_vlan_init(struct hostapd_data *hapd) -{ - struct sockaddr_nl local; - struct full_dynamic_vlan *priv; - - priv = os_zalloc(sizeof(*priv)); - if (priv == NULL) - return NULL; - -#ifndef CONFIG_VLAN_NETLINK - vlan_set_name_type(hapd->conf->ssid.vlan_naming == - DYNAMIC_VLAN_NAMING_WITH_DEVICE ? - VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD : - VLAN_NAME_TYPE_PLUS_VID_NO_PAD); -#endif /* CONFIG_VLAN_NETLINK */ - - priv->s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - if (priv->s < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: socket(PF_NETLINK,SOCK_RAW," - "NETLINK_ROUTE) failed: %s", - __func__, strerror(errno)); - os_free(priv); - return NULL; - } - - os_memset(&local, 0, sizeof(local)); - local.nl_family = AF_NETLINK; - local.nl_groups = RTMGRP_LINK; - if (bind(priv->s, (struct sockaddr *) &local, sizeof(local)) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: bind(netlink) failed: %s", - __func__, strerror(errno)); - close(priv->s); - os_free(priv); - return NULL; - } - - if (eloop_register_read_sock(priv->s, vlan_event_receive, hapd, NULL)) - { - close(priv->s); - os_free(priv); - return NULL; - } - - return priv; -} - - -static void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv) -{ - if (priv == NULL) - return; - eloop_unregister_read_sock(priv->s); - close(priv->s); - os_free(priv); -} -#endif /* CONFIG_FULL_DYNAMIC_VLAN */ - - -int vlan_setup_encryption_dyn(struct hostapd_data *hapd, const char *dyn_vlan) -{ - int i; - - if (dyn_vlan == NULL) - return 0; - - /* Static WEP keys are set here; IEEE 802.1X and WPA uses their own - * functions for setting up dynamic broadcast keys. */ - for (i = 0; i < 4; i++) { - if (hapd->conf->ssid.wep.key[i] && - hostapd_drv_set_key(dyn_vlan, hapd, WPA_ALG_WEP, NULL, i, - i == hapd->conf->ssid.wep.idx, NULL, 0, - hapd->conf->ssid.wep.key[i], - hapd->conf->ssid.wep.len[i])) - { - wpa_printf(MSG_ERROR, "VLAN: Could not set WEP " - "encryption for dynamic VLAN"); - return -1; - } - } - - return 0; + return hostapd_vlan_if_remove(hapd, vlan->ifname); } @@ -913,17 +83,14 @@ static int vlan_dynamic_add(struct hostapd_data *hapd, { while (vlan) { if (vlan->vlan_id != VLAN_ID_WILDCARD) { - if (hostapd_vlan_if_add(hapd, vlan->ifname)) { - if (errno != EEXIST) { - wpa_printf(MSG_ERROR, "VLAN: Could " - "not add VLAN %s: %s", - vlan->ifname, - strerror(errno)); - return -1; - } + if (vlan_if_add(hapd, vlan, 1)) { + wpa_printf(MSG_ERROR, + "VLAN: Could not add VLAN %s: %s", + vlan->ifname, strerror(errno)); + return -1; } #ifdef CONFIG_FULL_DYNAMIC_VLAN - ifconfig_up(vlan->ifname); + vlan_newlink(vlan->ifname, hapd); #endif /* CONFIG_FULL_DYNAMIC_VLAN */ } @@ -942,15 +109,17 @@ static void vlan_dynamic_remove(struct hostapd_data *hapd, while (vlan) { next = vlan->next; +#ifdef CONFIG_FULL_DYNAMIC_VLAN + /* vlan_dellink() takes care of cleanup and interface removal */ + if (vlan->vlan_id != VLAN_ID_WILDCARD) + vlan_dellink(vlan->ifname, hapd); +#else /* CONFIG_FULL_DYNAMIC_VLAN */ if (vlan->vlan_id != VLAN_ID_WILDCARD && - hostapd_vlan_if_remove(hapd, vlan->ifname)) { + vlan_if_remove(hapd, vlan)) { wpa_printf(MSG_ERROR, "VLAN: Could not remove VLAN " "iface: %s: %s", vlan->ifname, strerror(errno)); } -#ifdef CONFIG_FULL_DYNAMIC_VLAN - if (vlan->clean) - vlan_dellink(vlan->ifname, hapd); #endif /* CONFIG_FULL_DYNAMIC_VLAN */ vlan = next; @@ -964,7 +133,8 @@ int vlan_init(struct hostapd_data *hapd) hapd->full_dynamic_vlan = full_dynamic_vlan_init(hapd); #endif /* CONFIG_FULL_DYNAMIC_VLAN */ - if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED && + if ((hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED || + hapd->conf->ssid.per_sta_vif) && !hapd->conf->vlan) { /* dynamic vlans enabled but no (or empty) vlan_file given */ struct hostapd_vlan *vlan; @@ -1002,50 +172,45 @@ void vlan_deinit(struct hostapd_data *hapd) struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, struct hostapd_vlan *vlan, - int vlan_id) + int vlan_id, + struct vlan_description *vlan_desc) { - struct hostapd_vlan *n = NULL; - char *ifname, *pos; + struct hostapd_vlan *n; + char ifname[IFNAMSIZ + 1], *pos; - if (vlan == NULL || vlan_id <= 0 || vlan_id > MAX_VLAN_ID || - vlan->vlan_id != VLAN_ID_WILDCARD) + if (vlan == NULL || vlan->vlan_id != VLAN_ID_WILDCARD) return NULL; wpa_printf(MSG_DEBUG, "VLAN: %s(vlan_id=%d ifname=%s)", __func__, vlan_id, vlan->ifname); - ifname = os_strdup(vlan->ifname); - if (ifname == NULL) - return NULL; + os_strlcpy(ifname, vlan->ifname, sizeof(ifname)); pos = os_strchr(ifname, '#'); if (pos == NULL) - goto free_ifname; + return NULL; *pos++ = '\0'; n = os_zalloc(sizeof(*n)); if (n == NULL) - goto free_ifname; + return NULL; n->vlan_id = vlan_id; + if (vlan_desc) + n->vlan_desc = *vlan_desc; n->dynamic_vlan = 1; os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", ifname, vlan_id, pos); - if (hostapd_vlan_if_add(hapd, n->ifname)) { - os_free(n); - n = NULL; - goto free_ifname; - } - n->next = hapd->conf->vlan; hapd->conf->vlan = n; -#ifdef CONFIG_FULL_DYNAMIC_VLAN - ifconfig_up(n->ifname); -#endif /* CONFIG_FULL_DYNAMIC_VLAN */ + /* hapd->conf->vlan needs this new VLAN here for WPA setup */ + if (vlan_if_add(hapd, n, 0)) { + hapd->conf->vlan = n->next; + os_free(n); + n = NULL; + } -free_ifname: - os_free(ifname); return n; } @@ -1054,7 +219,7 @@ int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id) { struct hostapd_vlan *vlan; - if (vlan_id <= 0 || vlan_id > MAX_VLAN_ID) + if (vlan_id <= 0) return 1; wpa_printf(MSG_DEBUG, "VLAN: %s(ifname=%s vlan_id=%d)", @@ -1073,7 +238,7 @@ int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id) return 1; if (vlan->dynamic_vlan == 0) { - hostapd_vlan_if_remove(hapd, vlan->ifname); + vlan_if_remove(hapd, vlan); #ifdef CONFIG_FULL_DYNAMIC_VLAN vlan_dellink(vlan->ifname, hapd); #endif /* CONFIG_FULL_DYNAMIC_VLAN */ diff --git a/contrib/wpa/src/ap/vlan_init.h b/contrib/wpa/src/ap/vlan_init.h index fc39443e5d34..d17c82c326ab 100644 --- a/contrib/wpa/src/ap/vlan_init.h +++ b/contrib/wpa/src/ap/vlan_init.h @@ -15,10 +15,9 @@ int vlan_init(struct hostapd_data *hapd); void vlan_deinit(struct hostapd_data *hapd); struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, struct hostapd_vlan *vlan, - int vlan_id); + int vlan_id, + struct vlan_description *vlan_desc); int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id); -int vlan_setup_encryption_dyn(struct hostapd_data *hapd, - const char *dyn_vlan); #else /* CONFIG_NO_VLAN */ static inline int vlan_init(struct hostapd_data *hapd) { @@ -29,9 +28,9 @@ static inline void vlan_deinit(struct hostapd_data *hapd) { } -static inline struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, - struct hostapd_vlan *vlan, - int vlan_id) +static inline struct hostapd_vlan * +vlan_add_dynamic(struct hostapd_data *hapd, struct hostapd_vlan *vlan, + int vlan_id, struct vlan_description *vlan_desc) { return NULL; } @@ -40,12 +39,6 @@ static inline int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id) { return -1; } - -static inline int vlan_setup_encryption_dyn(struct hostapd_data *hapd, - const char *dyn_vlan) -{ - return -1; -} #endif /* CONFIG_NO_VLAN */ #endif /* VLAN_INIT_H */ diff --git a/contrib/wpa/src/ap/vlan_ioctl.c b/contrib/wpa/src/ap/vlan_ioctl.c new file mode 100644 index 000000000000..987b612e1d9f --- /dev/null +++ b/contrib/wpa/src/ap/vlan_ioctl.c @@ -0,0 +1,155 @@ +/* + * hostapd / VLAN ioctl API + * Copyright 2003, Instant802 Networks, Inc. + * Copyright 2005-2006, Devicescape Software, Inc. + * Copyright (c) 2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#include + +#include "utils/common.h" +#include "common/linux_vlan.h" +#include "vlan_util.h" + + +int vlan_rem(const char *if_name) +{ + int fd; + struct vlan_ioctl_args if_request; + + wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(%s)", if_name); + if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) { + wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'", + if_name); + return -1; + } + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + os_memset(&if_request, 0, sizeof(if_request)); + + os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1)); + if_request.cmd = DEL_VLAN_CMD; + + if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: DEL_VLAN_CMD failed for %s: " + "%s", __func__, if_name, strerror(errno)); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +/* + Add a vlan interface with VLAN ID 'vid' and tagged interface + 'if_name'. + + returns -1 on error + returns 1 if the interface already exists + returns 0 otherwise +*/ +int vlan_add(const char *if_name, int vid, const char *vlan_if_name) +{ + int fd; + struct vlan_ioctl_args if_request; + + wpa_printf(MSG_DEBUG, "VLAN: vlan_add(if_name=%s, vid=%d)", + if_name, vid); + ifconfig_up(if_name); + + if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) { + wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'", + if_name); + return -1; + } + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + os_memset(&if_request, 0, sizeof(if_request)); + + /* Determine if a suitable vlan device already exists. */ + + os_snprintf(if_request.device1, sizeof(if_request.device1), "vlan%d", + vid); + + if_request.cmd = GET_VLAN_VID_CMD; + + if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0 && + if_request.u.VID == vid) { + if_request.cmd = GET_VLAN_REALDEV_NAME_CMD; + + if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0 && + os_strncmp(if_request.u.device2, if_name, + sizeof(if_request.u.device2)) == 0) { + close(fd); + wpa_printf(MSG_DEBUG, + "VLAN: vlan_add: if_name %s exists already", + if_request.device1); + return 1; + } + } + + /* A suitable vlan device does not already exist, add one. */ + + os_memset(&if_request, 0, sizeof(if_request)); + os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1)); + if_request.u.VID = vid; + if_request.cmd = ADD_VLAN_CMD; + + if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { + wpa_printf(MSG_ERROR, + "VLAN: %s: ADD_VLAN_CMD failed for %s: %s", + __func__, if_request.device1, strerror(errno)); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +int vlan_set_name_type(unsigned int name_type) +{ + int fd; + struct vlan_ioctl_args if_request; + + wpa_printf(MSG_DEBUG, "VLAN: vlan_set_name_type(name_type=%u)", + name_type); + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, + "VLAN: %s: socket(AF_INET,SOCK_STREAM) failed: %s", + __func__, strerror(errno)); + return -1; + } + + os_memset(&if_request, 0, sizeof(if_request)); + + if_request.u.name_type = name_type; + if_request.cmd = SET_VLAN_NAME_TYPE_CMD; + if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { + wpa_printf(MSG_ERROR, + "VLAN: %s: SET_VLAN_NAME_TYPE_CMD name_type=%u failed: %s", + __func__, name_type, strerror(errno)); + close(fd); + return -1; + } + + close(fd); + return 0; +} diff --git a/contrib/wpa/src/ap/vlan_util.c b/contrib/wpa/src/ap/vlan_util.c index d4e0efb9b024..56d1d3d123e8 100644 --- a/contrib/wpa/src/ap/vlan_util.c +++ b/contrib/wpa/src/ap/vlan_util.c @@ -7,18 +7,10 @@ */ #include "utils/includes.h" -#include -#include -#include -#include -#include -#include #include #include #include "utils/common.h" -#include "utils/eloop.h" -#include "hostapd.h" #include "vlan_util.h" /* @@ -33,7 +25,6 @@ int vlan_add(const char *if_name, int vid, const char *vlan_if_name) { int err, ret = -1; struct nl_sock *handle = NULL; - struct nl_cache *cache = NULL; struct rtnl_link *rlink = NULL; int if_idx = 0; @@ -65,22 +56,19 @@ int vlan_add(const char *if_name, int vid, const char *vlan_if_name) goto vlan_add_error; } - err = rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache); + err = rtnl_link_get_kernel(handle, 0, if_name, &rlink); if (err < 0) { - cache = NULL; - wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache: %s", - nl_geterror(err)); - goto vlan_add_error; - } - - if (!(if_idx = rtnl_link_name2i(cache, if_name))) { /* link does not exist */ wpa_printf(MSG_ERROR, "VLAN: interface %s does not exist", if_name); goto vlan_add_error; } + if_idx = rtnl_link_get_ifindex(rlink); + rtnl_link_put(rlink); + rlink = NULL; - if ((rlink = rtnl_link_get_by_name(cache, vlan_if_name))) { + err = rtnl_link_get_kernel(handle, 0, vlan_if_name, &rlink); + if (err >= 0) { /* link does exist */ rtnl_link_put(rlink); rlink = NULL; @@ -127,8 +115,6 @@ int vlan_add(const char *if_name, int vid, const char *vlan_if_name) vlan_add_error: if (rlink) rtnl_link_put(rlink); - if (cache) - nl_cache_free(cache); if (handle) nl_socket_free(handle); return ret; @@ -139,7 +125,6 @@ int vlan_rem(const char *if_name) { int err, ret = -1; struct nl_sock *handle = NULL; - struct nl_cache *cache = NULL; struct rtnl_link *rlink = NULL; wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(if_name=%s)", if_name); @@ -157,15 +142,8 @@ int vlan_rem(const char *if_name) goto vlan_rem_error; } - err = rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache); + err = rtnl_link_get_kernel(handle, 0, if_name, &rlink); if (err < 0) { - cache = NULL; - wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache: %s", - nl_geterror(err)); - goto vlan_rem_error; - } - - if (!(rlink = rtnl_link_get_by_name(cache, if_name))) { /* link does not exist */ wpa_printf(MSG_ERROR, "VLAN: interface %s does not exists", if_name); @@ -184,9 +162,13 @@ int vlan_rem(const char *if_name) vlan_rem_error: if (rlink) rtnl_link_put(rlink); - if (cache) - nl_cache_free(cache); if (handle) nl_socket_free(handle); return ret; } + + +int vlan_set_name_type(unsigned int name_type) +{ + return 0; +} diff --git a/contrib/wpa/src/ap/vlan_util.h b/contrib/wpa/src/ap/vlan_util.h index bef5a16f6c90..244685975c0b 100644 --- a/contrib/wpa/src/ap/vlan_util.h +++ b/contrib/wpa/src/ap/vlan_util.h @@ -1,5 +1,5 @@ /* - * hostapd / VLAN netlink api + * hostapd / VLAN netlink/ioctl api * Copyright (c) 2012, Michael Braun * * This software may be distributed under the terms of the BSD license. @@ -9,7 +9,23 @@ #ifndef VLAN_UTIL_H #define VLAN_UTIL_H +struct hostapd_data; +struct hostapd_vlan; +struct full_dynamic_vlan; + int vlan_add(const char *if_name, int vid, const char *vlan_if_name); int vlan_rem(const char *if_name); +int vlan_set_name_type(unsigned int name_type); + +int ifconfig_helper(const char *if_name, int up); +int ifconfig_up(const char *if_name); +int iface_exists(const char *ifname); +int vlan_if_remove(struct hostapd_data *hapd, struct hostapd_vlan *vlan); + +struct full_dynamic_vlan * +full_dynamic_vlan_init(struct hostapd_data *hapd); +void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv); +void vlan_newlink(const char *ifname, struct hostapd_data *hapd); +void vlan_dellink(const char *ifname, struct hostapd_data *hapd); #endif /* VLAN_UTIL_H */ diff --git a/contrib/wpa/src/ap/wnm_ap.c b/contrib/wpa/src/ap/wnm_ap.c index 4c8bc10083c4..41d50cebfbe0 100644 --- a/contrib/wpa/src/ap/wnm_ap.c +++ b/contrib/wpa/src/ap/wnm_ap.c @@ -17,6 +17,7 @@ #include "ap/ap_config.h" #include "ap/ap_drv_ops.h" #include "ap/wpa_auth.h" +#include "mbo_ap.h" #include "wnm_ap.h" #define MAX_TFS_IE_LEN 1024 @@ -94,6 +95,7 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, if (mgmt == NULL) { wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for " "WNM-Sleep Response action frame"); + os_free(wnmtfs_ie); return -1; } os_memcpy(mgmt->da, addr, ETH_ALEN); @@ -376,6 +378,29 @@ static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd, } +static void ieee802_11_rx_wnm_notification_req(struct hostapd_data *hapd, + const u8 *addr, const u8 *buf, + size_t len) +{ + u8 dialog_token, type; + + if (len < 2) + return; + dialog_token = *buf++; + type = *buf++; + len -= 2; + + wpa_printf(MSG_DEBUG, + "WNM: Received WNM Notification Request frame from " + MACSTR " (dialog_token=%u type=%u)", + MAC2STR(addr), dialog_token, type); + wpa_hexdump(MSG_MSGDUMP, "WNM: Notification Request subelements", + buf, len); + if (type == WLAN_EID_VENDOR_SPECIFIC) + mbo_ap_wnm_notification_req(hapd, addr, buf, len); +} + + int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len) { @@ -402,6 +427,10 @@ int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd, case WNM_SLEEP_MODE_REQ: ieee802_11_rx_wnmsleep_req(hapd, mgmt->sa, payload, plen); return 0; + case WNM_NOTIFICATION_REQ: + ieee802_11_rx_wnm_notification_req(hapd, mgmt->sa, payload, + plen); + return 0; } wpa_printf(MSG_DEBUG, "WNM: Unsupported WNM Action %u from " MACSTR, @@ -527,7 +556,8 @@ int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd, int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta, u8 req_mode, int disassoc_timer, u8 valid_int, const u8 *bss_term_dur, const char *url, - const u8 *nei_rep, size_t nei_rep_len) + const u8 *nei_rep, size_t nei_rep_len, + const u8 *mbo_attrs, size_t mbo_len) { u8 *buf, *pos; struct ieee80211_mgmt *mgmt; @@ -536,7 +566,7 @@ int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta, wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to " MACSTR " req_mode=0x%x disassoc_timer=%d valid_int=0x%x", MAC2STR(sta->addr), req_mode, disassoc_timer, valid_int); - buf = os_zalloc(1000 + nei_rep_len); + buf = os_zalloc(1000 + nei_rep_len + mbo_len); if (buf == NULL) return -1; mgmt = (struct ieee80211_mgmt *) buf; @@ -579,6 +609,11 @@ int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta, pos += nei_rep_len; } + if (mbo_len > 0) { + pos += mbo_add_ie(pos, buf + sizeof(buf) - pos, mbo_attrs, + mbo_len); + } + if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) { wpa_printf(MSG_DEBUG, "Failed to send BSS Transition Management Request frame"); diff --git a/contrib/wpa/src/ap/wnm_ap.h b/contrib/wpa/src/ap/wnm_ap.h index 7789307209c9..a44eadb85e55 100644 --- a/contrib/wpa/src/ap/wnm_ap.h +++ b/contrib/wpa/src/ap/wnm_ap.h @@ -21,6 +21,7 @@ int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd, int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta, u8 req_mode, int disassoc_timer, u8 valid_int, const u8 *bss_term_dur, const char *url, - const u8 *nei_rep, size_t nei_rep_len); + const u8 *nei_rep, size_t nei_rep_len, + const u8 *mbo_attrs, size_t mbo_len); #endif /* WNM_AP_H */ diff --git a/contrib/wpa/src/ap/wpa_auth.c b/contrib/wpa/src/ap/wpa_auth.c index 63f252fb55b0..81297fa4c0ae 100644 --- a/contrib/wpa/src/ap/wpa_auth.c +++ b/contrib/wpa/src/ap/wpa_auth.c @@ -44,7 +44,8 @@ static int wpa_gtk_update(struct wpa_authenticator *wpa_auth, static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth, struct wpa_group *group); static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, - const u8 *pmk, struct wpa_ptk *ptk); + const u8 *pmk, unsigned int pmk_len, + struct wpa_ptk *ptk); static void wpa_group_free(struct wpa_authenticator *wpa_auth, struct wpa_group *group); static void wpa_group_get(struct wpa_authenticator *wpa_auth, @@ -827,6 +828,7 @@ static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data, struct wpa_ptk PTK; int ok = 0; const u8 *pmk = NULL; + unsigned int pmk_len; for (;;) { if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { @@ -834,10 +836,13 @@ static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data, sm->p2p_dev_addr, pmk); if (pmk == NULL) break; - } else + pmk_len = PMK_LEN; + } else { pmk = sm->PMK; + pmk_len = sm->pmk_len; + } - wpa_derive_ptk(sm, sm->alt_SNonce, pmk, &PTK); + wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK); if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, data, data_len) == 0) { @@ -1919,11 +1924,27 @@ SM_STATE(WPA_PTK, INITPMK) #endif /* CONFIG_IEEE80211R */ if (sm->pmksa) { wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache"); - os_memcpy(sm->PMK, sm->pmksa->pmk, PMK_LEN); + os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len); + sm->pmk_len = sm->pmksa->pmk_len; } else if (wpa_auth_get_msk(sm->wpa_auth, sm->addr, msk, &len) == 0) { + unsigned int pmk_len; + + if (sm->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) + pmk_len = PMK_LEN_SUITE_B_192; + else + pmk_len = PMK_LEN; wpa_printf(MSG_DEBUG, "WPA: PMK from EAPOL state machine " - "(len=%lu)", (unsigned long) len); - os_memcpy(sm->PMK, msk, PMK_LEN); + "(MSK len=%lu PMK len=%u)", (unsigned long) len, + pmk_len); + if (len < pmk_len) { + wpa_printf(MSG_DEBUG, + "WPA: MSK not long enough (%u) to create PMK (%u)", + (unsigned int) len, (unsigned int) pmk_len); + sm->Disconnect = TRUE; + return; + } + os_memcpy(sm->PMK, msk, pmk_len); + sm->pmk_len = pmk_len; #ifdef CONFIG_IEEE80211R if (len >= 2 * PMK_LEN) { os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN); @@ -1958,6 +1979,7 @@ SM_STATE(WPA_PTK, INITPSK) psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL); if (psk) { os_memcpy(sm->PMK, psk, PMK_LEN); + sm->pmk_len = PMK_LEN; #ifdef CONFIG_IEEE80211R os_memcpy(sm->xxkey, psk, PMK_LEN); sm->xxkey_len = PMK_LEN; @@ -2009,7 +2031,7 @@ SM_STATE(WPA_PTK, PTKSTART) * Calculate PMKID since no PMKSA cache entry was * available with pre-calculated PMKID. */ - rsn_pmkid(sm->PMK, PMK_LEN, sm->wpa_auth->addr, + rsn_pmkid(sm->PMK, sm->pmk_len, sm->wpa_auth->addr, sm->addr, &pmkid[2 + RSN_SELECTOR_LEN], wpa_key_mgmt_sha256(sm->wpa_key_mgmt)); } @@ -2021,14 +2043,15 @@ SM_STATE(WPA_PTK, PTKSTART) static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, - const u8 *pmk, struct wpa_ptk *ptk) + const u8 *pmk, unsigned int pmk_len, + struct wpa_ptk *ptk) { #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) return wpa_auth_derive_ptk_ft(sm, pmk, ptk); #endif /* CONFIG_IEEE80211R */ - return wpa_pmk_to_ptk(pmk, PMK_LEN, "Pairwise key expansion", + 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); } @@ -2039,6 +2062,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) struct wpa_ptk PTK; int ok = 0, psk_found = 0; const u8 *pmk = NULL; + unsigned int pmk_len; SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk); sm->EAPOLKeyReceived = FALSE; @@ -2054,10 +2078,13 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) if (pmk == NULL) break; psk_found = 1; - } else + pmk_len = PMK_LEN; + } else { pmk = sm->PMK; + pmk_len = sm->pmk_len; + } - wpa_derive_ptk(sm, sm->SNonce, pmk, &PTK); + wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK); if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, sm->last_rx_eapol_key, @@ -2107,6 +2134,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) * state machine data based on whatever PSK was selected here. */ os_memcpy(sm->PMK, pmk, PMK_LEN); + sm->pmk_len = PMK_LEN; } sm->MICVerified = TRUE; @@ -2285,14 +2313,19 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) pos += wpa_ie_len; #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { - int res = wpa_insert_pmkid(kde, pos - kde, sm->pmk_r1_name); + int res; + size_t elen; + + elen = pos - kde; + res = wpa_insert_pmkid(kde, &elen, sm->pmk_r1_name); if (res < 0) { wpa_printf(MSG_ERROR, "FT: Failed to insert " "PMKR1Name into RSN IE in EAPOL-Key data"); os_free(kde); return; } - pos += res; + pos -= wpa_ie_len; + pos += elen; } #endif /* CONFIG_IEEE80211R */ if (gtk) { @@ -2310,10 +2343,18 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) struct wpa_auth_config *conf; conf = &sm->wpa_auth->conf; - res = wpa_write_ftie(conf, conf->r0_key_holder, - conf->r0_key_holder_len, - NULL, NULL, pos, kde + kde_len - pos, - NULL, 0); + if (sm->assoc_resp_ftie && + kde + kde_len - pos >= 2 + sm->assoc_resp_ftie[1]) { + os_memcpy(pos, sm->assoc_resp_ftie, + 2 + sm->assoc_resp_ftie[1]); + res = 2 + sm->assoc_resp_ftie[1]; + } else { + res = wpa_write_ftie(conf, conf->r0_key_holder, + conf->r0_key_holder_len, + NULL, NULL, pos, + kde + kde_len - pos, + NULL, 0); + } if (res < 0) { wpa_printf(MSG_ERROR, "FT: Failed to insert FTIE " "into EAPOL-Key Key Data"); @@ -3269,13 +3310,21 @@ const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth, size_t *len) int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk, + unsigned int pmk_len, int session_timeout, struct eapol_state_machine *eapol) { if (sm == NULL || sm->wpa != WPA_VERSION_WPA2 || sm->wpa_auth->conf.disable_pmksa_caching) return -1; - if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, PMK_LEN, + if (sm->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) { + if (pmk_len > PMK_LEN_SUITE_B_192) + pmk_len = PMK_LEN_SUITE_B_192; + } else if (pmk_len > PMK_LEN) { + pmk_len = PMK_LEN; + } + + if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, pmk_len, NULL, sm->PTK.kck, sm->PTK.kck_len, sm->wpa_auth->addr, sm->addr, session_timeout, eapol, sm->wpa_key_mgmt)) @@ -3293,7 +3342,7 @@ int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, if (wpa_auth == NULL) return -1; - if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len, + if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len, NULL, NULL, 0, wpa_auth->addr, sta_addr, session_timeout, eapol, @@ -3305,12 +3354,12 @@ int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr, - const u8 *pmk) + const u8 *pmk, const u8 *pmkid) { if (wpa_auth->conf.disable_pmksa_caching) return -1; - if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, PMK_LEN, + if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, PMK_LEN, pmkid, NULL, 0, wpa_auth->addr, addr, 0, NULL, WPA_KEY_MGMT_SAE)) @@ -3336,6 +3385,46 @@ void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth, } +int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf, + size_t len) +{ + if (!wpa_auth || !wpa_auth->pmksa) + return 0; + return pmksa_cache_auth_list(wpa_auth->pmksa, buf, len); +} + + +void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth) +{ + if (wpa_auth && wpa_auth->pmksa) + pmksa_cache_auth_flush(wpa_auth->pmksa); +} + + +struct rsn_pmksa_cache_entry * +wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr) +{ + if (!wpa_auth || !wpa_auth->pmksa) + return NULL; + return pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, NULL); +} + + +void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa, + struct wpa_state_machine *sm, + struct wpa_authenticator *wpa_auth, + u8 *pmkid, u8 *pmk) +{ + if (!sm) + return; + + sm->pmksa = pmksa; + os_memcpy(pmk, pmksa->pmk, PMK_LEN); + os_memcpy(pmkid, pmksa->pmkid, PMKID_LEN); + os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmksa->pmkid, PMKID_LEN); +} + + /* * Remove and free the group from wpa_authenticator. This is triggered by a * callback to make sure nobody is currently iterating the group list while it @@ -3414,6 +3503,98 @@ wpa_auth_add_group(struct wpa_authenticator *wpa_auth, int vlan_id) } +/* + * Enforce that the group state machine for the VLAN is running, increase + * reference counter as interface is up. References might have been increased + * even if a negative value is returned. + * Returns: -1 on error (group missing, group already failed); otherwise, 0 + */ +int wpa_auth_ensure_group(struct wpa_authenticator *wpa_auth, int vlan_id) +{ + struct wpa_group *group; + + if (wpa_auth == NULL) + return 0; + + group = wpa_auth->group; + while (group) { + if (group->vlan_id == vlan_id) + break; + group = group->next; + } + + if (group == NULL) { + group = wpa_auth_add_group(wpa_auth, vlan_id); + if (group == NULL) + return -1; + } + + wpa_printf(MSG_DEBUG, + "WPA: Ensure group state machine running for VLAN ID %d", + vlan_id); + + wpa_group_get(wpa_auth, group); + group->num_setup_iface++; + + if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) + return -1; + + return 0; +} + + +/* + * Decrease reference counter, expected to be zero afterwards. + * returns: -1 on error (group not found, group in fail state) + * -2 if wpa_group is still referenced + * 0 else + */ +int wpa_auth_release_group(struct wpa_authenticator *wpa_auth, int vlan_id) +{ + struct wpa_group *group; + int ret = 0; + + if (wpa_auth == NULL) + return 0; + + group = wpa_auth->group; + while (group) { + if (group->vlan_id == vlan_id) + break; + group = group->next; + } + + if (group == NULL) + return -1; + + wpa_printf(MSG_DEBUG, + "WPA: Try stopping group state machine for VLAN ID %d", + vlan_id); + + if (group->num_setup_iface <= 0) { + wpa_printf(MSG_ERROR, + "WPA: wpa_auth_release_group called more often than wpa_auth_ensure_group for VLAN ID %d, skipping.", + vlan_id); + return -1; + } + group->num_setup_iface--; + + if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) + ret = -1; + + if (group->references > 1) { + wpa_printf(MSG_DEBUG, + "WPA: Cannot stop group state machine for VLAN ID %d as references are still hold", + vlan_id); + ret = -2; + } + + wpa_group_put(wpa_auth, group); + + return ret; +} + + int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id) { struct wpa_group *group; diff --git a/contrib/wpa/src/ap/wpa_auth.h b/contrib/wpa/src/ap/wpa_auth.h index 1b1442f414fa..03ae58990daa 100644 --- a/contrib/wpa/src/ap/wpa_auth.h +++ b/contrib/wpa/src/ap/wpa_auth.h @@ -42,10 +42,11 @@ struct ft_rrb_frame { #define FT_PACKET_R0KH_R1KH_RESP 201 #define FT_PACKET_R0KH_R1KH_PUSH 202 -#define FT_R0KH_R1KH_PULL_DATA_LEN 44 -#define FT_R0KH_R1KH_RESP_DATA_LEN 76 -#define FT_R0KH_R1KH_PUSH_DATA_LEN 88 #define FT_R0KH_R1KH_PULL_NONCE_LEN 16 +#define FT_R0KH_R1KH_PULL_DATA_LEN (FT_R0KH_R1KH_PULL_NONCE_LEN + \ + WPA_PMK_NAME_LEN + FT_R1KH_ID_LEN + \ + ETH_ALEN) +#define FT_R0KH_R1KH_PULL_PAD_LEN ((8 - FT_R0KH_R1KH_PULL_DATA_LEN % 8) % 8) struct ft_r0kh_r1kh_pull_frame { u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ @@ -57,14 +58,18 @@ struct ft_r0kh_r1kh_pull_frame { u8 pmk_r0_name[WPA_PMK_NAME_LEN]; u8 r1kh_id[FT_R1KH_ID_LEN]; u8 s1kh_id[ETH_ALEN]; - u8 pad[4]; /* 8-octet boundary for AES key wrap */ + u8 pad[FT_R0KH_R1KH_PULL_PAD_LEN]; /* 8-octet boundary for AES block */ u8 key_wrap_extra[8]; } STRUCT_PACKED; +#define FT_R0KH_R1KH_RESP_DATA_LEN (FT_R0KH_R1KH_PULL_NONCE_LEN + \ + FT_R1KH_ID_LEN + ETH_ALEN + PMK_LEN + \ + WPA_PMK_NAME_LEN + 2) +#define FT_R0KH_R1KH_RESP_PAD_LEN ((8 - FT_R0KH_R1KH_RESP_DATA_LEN % 8) % 8) struct ft_r0kh_r1kh_resp_frame { u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ u8 packet_type; /* FT_PACKET_R0KH_R1KH_RESP */ - le16 data_length; /* little endian length of data (76) */ + le16 data_length; /* little endian length of data (78) */ u8 ap_address[ETH_ALEN]; u8 nonce[FT_R0KH_R1KH_PULL_NONCE_LEN]; /* copied from pull */ @@ -73,14 +78,18 @@ struct ft_r0kh_r1kh_resp_frame { u8 pmk_r1[PMK_LEN]; u8 pmk_r1_name[WPA_PMK_NAME_LEN]; le16 pairwise; - u8 pad[2]; /* 8-octet boundary for AES key wrap */ + u8 pad[FT_R0KH_R1KH_RESP_PAD_LEN]; /* 8-octet boundary for AES block */ u8 key_wrap_extra[8]; } STRUCT_PACKED; +#define FT_R0KH_R1KH_PUSH_DATA_LEN (4 + FT_R1KH_ID_LEN + ETH_ALEN + \ + WPA_PMK_NAME_LEN + PMK_LEN + \ + WPA_PMK_NAME_LEN + 2) +#define FT_R0KH_R1KH_PUSH_PAD_LEN ((8 - FT_R0KH_R1KH_PUSH_DATA_LEN % 8) % 8) struct ft_r0kh_r1kh_push_frame { u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ u8 packet_type; /* FT_PACKET_R0KH_R1KH_PUSH */ - le16 data_length; /* little endian length of data (88) */ + le16 data_length; /* little endian length of data (82) */ u8 ap_address[ETH_ALEN]; /* Encrypted with AES key-wrap */ @@ -92,7 +101,7 @@ struct ft_r0kh_r1kh_push_frame { u8 pmk_r1[PMK_LEN]; u8 pmk_r1_name[WPA_PMK_NAME_LEN]; le16 pairwise; - u8 pad[6]; /* 8-octet boundary for AES key wrap */ + u8 pad[FT_R0KH_R1KH_PUSH_PAD_LEN]; /* 8-octet boundary for AES block */ u8 key_wrap_extra[8]; } STRUCT_PACKED; @@ -280,15 +289,25 @@ void wpa_auth_sta_local_mic_failure_report(struct wpa_state_machine *sm); const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth, size_t *len); int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk, + unsigned int pmk_len, int session_timeout, struct eapol_state_machine *eapol); int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, const u8 *pmk, size_t len, const u8 *sta_addr, int session_timeout, struct eapol_state_machine *eapol); int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr, - const u8 *pmk); + const u8 *pmk, const u8 *pmkid); void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth, const u8 *sta_addr); +int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf, + size_t len); +void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth); +struct rsn_pmksa_cache_entry * +wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr); +void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa, + struct wpa_state_machine *sm, + struct wpa_authenticator *wpa_auth, + u8 *pmkid, u8 *pmk); int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id); void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, int ack); @@ -326,4 +345,7 @@ int wpa_auth_radius_das_disconnect_pmksa(struct wpa_authenticator *wpa_auth, struct radius_das_attrs *attr); void wpa_auth_reconfig_group_keys(struct wpa_authenticator *wpa_auth); +int wpa_auth_ensure_group(struct wpa_authenticator *wpa_auth, int vlan_id); +int wpa_auth_release_group(struct wpa_authenticator *wpa_auth, int vlan_id); + #endif /* WPA_AUTH_H */ diff --git a/contrib/wpa/src/ap/wpa_auth_ft.c b/contrib/wpa/src/ap/wpa_auth_ft.c index f8f5dbe3e7da..e63b99ad2034 100644 --- a/contrib/wpa/src/ap/wpa_auth_ft.c +++ b/contrib/wpa/src/ap/wpa_auth_ft.c @@ -720,11 +720,6 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, ftie_len = res; pos += res; - os_free(sm->assoc_resp_ftie); - sm->assoc_resp_ftie = os_malloc(ftie_len); - if (sm->assoc_resp_ftie) - os_memcpy(sm->assoc_resp_ftie, ftie, ftie_len); - _ftie = (struct rsn_ftie *) (ftie + 2); if (auth_alg == WLAN_AUTH_FT) _ftie->mic_control[1] = 3; /* Information element count */ @@ -750,6 +745,11 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, _ftie->mic) < 0) wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC"); + os_free(sm->assoc_resp_ftie); + sm->assoc_resp_ftie = os_malloc(ftie_len); + if (sm->assoc_resp_ftie) + os_memcpy(sm->assoc_resp_ftie, ftie, ftie_len); + return pos; } diff --git a/contrib/wpa/src/ap/wpa_auth_glue.c b/contrib/wpa/src/ap/wpa_auth_glue.c index f98cc50599e3..21424147e443 100644 --- a/contrib/wpa/src/ap/wpa_auth_glue.c +++ b/contrib/wpa/src/ap/wpa_auth_glue.c @@ -12,6 +12,7 @@ #include "common/ieee802_11_defs.h" #include "common/sae.h" #include "common/wpa_ctrl.h" +#include "crypto/sha1.h" #include "eapol_auth/eapol_auth_sm.h" #include "eapol_auth/eapol_auth_sm_i.h" #include "eap_server/eap.h" @@ -246,6 +247,13 @@ static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr, struct hostapd_sta_wpa_psk_short *pos; psk = sta->psk->psk; for (pos = sta->psk; pos; pos = pos->next) { + if (pos->is_passphrase) { + pbkdf2_sha1(pos->passphrase, + hapd->conf->ssid.ssid, + hapd->conf->ssid.ssid_len, 4096, + pos->psk, PMK_LEN); + pos->is_passphrase = 0; + } if (pos->psk == prev_psk) { psk = pos->next ? pos->next->psk : NULL; break; @@ -413,6 +421,8 @@ static int hostapd_wpa_auth_ft_iter(struct hostapd_iface *iface, void *ctx) hapd = iface->bss[j]; if (hapd == idata->src_hapd) continue; + if (!hapd->wpa_auth) + continue; if (os_memcmp(hapd->own_addr, idata->dst, ETH_ALEN) == 0) { wpa_printf(MSG_DEBUG, "FT: Send RRB data directly to " "locally managed BSS " MACSTR "@%s -> " @@ -563,6 +573,9 @@ static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf, ethhdr = (struct l2_ethhdr *) buf; wpa_printf(MSG_DEBUG, "FT: RRB received packet " MACSTR " -> " MACSTR, MAC2STR(ethhdr->h_source), MAC2STR(ethhdr->h_dest)); + if (!is_multicast_ether_addr(ethhdr->h_dest) && + os_memcmp(hapd->own_addr, ethhdr->h_dest, ETH_ALEN) != 0) + return; wpa_ft_rrb_rx(hapd->wpa_auth, ethhdr->h_source, buf + sizeof(*ethhdr), len - sizeof(*ethhdr)); } @@ -637,7 +650,7 @@ int hostapd_setup_wpa(struct hostapd_data *hapd) } #ifdef CONFIG_IEEE80211R - if (!hostapd_drv_none(hapd) && hapd->conf->ft_over_ds && + if (!hostapd_drv_none(hapd) && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt)) { hapd->l2 = l2_packet_init(hapd->conf->bridge[0] ? hapd->conf->bridge : @@ -674,13 +687,14 @@ void hostapd_deinit_wpa(struct hostapd_data *hapd) wpa_deinit(hapd->wpa_auth); hapd->wpa_auth = NULL; - if (hostapd_set_privacy(hapd, 0)) { + if (hapd->drv_priv && hostapd_set_privacy(hapd, 0)) { wpa_printf(MSG_DEBUG, "Could not disable " "PrivacyInvoked for interface %s", hapd->conf->iface); } - if (hostapd_set_generic_elem(hapd, (u8 *) "", 0)) { + if (hapd->drv_priv && + hostapd_set_generic_elem(hapd, (u8 *) "", 0)) { wpa_printf(MSG_DEBUG, "Could not remove generic " "information element from interface %s", hapd->conf->iface); diff --git a/contrib/wpa/src/ap/wpa_auth_i.h b/contrib/wpa/src/ap/wpa_auth_i.h index 234d84c84689..7fd8f05fa8ff 100644 --- a/contrib/wpa/src/ap/wpa_auth_i.h +++ b/contrib/wpa/src/ap/wpa_auth_i.h @@ -60,7 +60,8 @@ struct wpa_state_machine { u8 SNonce[WPA_NONCE_LEN]; u8 alt_SNonce[WPA_NONCE_LEN]; u8 alt_replay_counter[WPA_REPLAY_COUNTER_LEN]; - u8 PMK[PMK_LEN]; + u8 PMK[PMK_LEN_MAX]; + unsigned int pmk_len; struct wpa_ptk PTK; Boolean PTK_valid; Boolean pairwise_set; @@ -172,6 +173,7 @@ struct wpa_group { #endif /* CONFIG_IEEE80211W */ /* Number of references except those in struct wpa_group->next */ unsigned int references; + unsigned int num_setup_iface; }; diff --git a/contrib/wpa/src/ap/wpa_auth_ie.c b/contrib/wpa/src/ap/wpa_auth_ie.c index eafb828b8d60..f79783b91929 100644 --- a/contrib/wpa/src/ap/wpa_auth_ie.c +++ b/contrib/wpa/src/ap/wpa_auth_ie.c @@ -251,7 +251,7 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, pos += 2; if (pmkid) { - if (pos + 2 + PMKID_LEN > buf + len) + if (2 + PMKID_LEN > buf + len - pos) return -1; /* PMKID Count */ WPA_PUT_LE16(pos, 1); @@ -263,7 +263,7 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, #ifdef CONFIG_IEEE80211W if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION && conf->group_mgmt_cipher != WPA_CIPHER_AES_128_CMAC) { - if (pos + 2 + 4 > buf + len) + if (2 + 4 > buf + len - pos) return -1; if (pmkid == NULL) { /* PMKID Count */ @@ -712,11 +712,14 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, } } if (sm->pmksa && pmkid) { + struct vlan_description *vlan; + + vlan = sm->pmksa->vlan_desc; wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, - "PMKID found from PMKSA cache " - "eap_type=%d vlan_id=%d", + "PMKID found from PMKSA cache eap_type=%d vlan=%d%s", sm->pmksa->eap_type_authsrv, - sm->pmksa->vlan_id); + vlan ? vlan->untagged : 0, + (vlan && vlan->tagged[0]) ? "+" : ""); os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmkid, PMKID_LEN); } @@ -791,7 +794,7 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end, return 0; } - if (pos + 1 + RSN_SELECTOR_LEN < end && + if (1 + RSN_SELECTOR_LEN < end - pos && pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN && RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) { ie->pmkid = pos + 2 + RSN_SELECTOR_LEN; @@ -887,13 +890,13 @@ int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie) int ret = 0; os_memset(ie, 0, sizeof(*ie)); - for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) { + for (pos = buf, end = pos + len; end - pos > 1; pos += 2 + pos[1]) { if (pos[0] == 0xdd && ((pos == buf + len - 1) || pos[1] == 0)) { /* Ignore padding */ break; } - if (pos + 2 + pos[1] > end) { + if (2 + pos[1] > end - pos) { wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data " "underflow (ie=%d len=%d pos=%d)", pos[0], pos[1], (int) (pos - buf)); diff --git a/contrib/wpa/src/ap/wps_hostapd.c b/contrib/wpa/src/ap/wps_hostapd.c index cde31e60e03b..95b40da0f6bb 100644 --- a/contrib/wpa/src/ap/wps_hostapd.c +++ b/contrib/wpa/src/ap/wps_hostapd.c @@ -1,6 +1,6 @@ /* * hostapd / WPS integration - * Copyright (c) 2008-2012, Jouni Malinen + * Copyright (c) 2008-2016, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -269,12 +269,6 @@ static void hostapd_wps_enrollee_seen_cb(void *ctx, const u8 *addr, } -static int str_starts(const char *str, const char *start) -{ - return os_strncmp(str, start, os_strlen(start)) == 0; -} - - static void wps_reload_config(void *eloop_data, void *user_ctx) { struct hostapd_iface *iface = eloop_data; @@ -445,6 +439,8 @@ static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx) os_memcpy(hapd->wps->ssid, cred->ssid, cred->ssid_len); hapd->wps->ssid_len = cred->ssid_len; hapd->wps->encr_types = cred->encr_type; + hapd->wps->encr_types_rsn = cred->encr_type; + hapd->wps->encr_types_wpa = cred->encr_type; hapd->wps->auth_types = cred->auth_type; hapd->wps->ap_encr_type = cred->encr_type; hapd->wps->ap_auth_type = cred->auth_type; @@ -872,7 +868,8 @@ static void hostapd_wps_clear_ies(struct hostapd_data *hapd, int deinit_only) hapd->wps_probe_resp_ie = NULL; if (deinit_only) { - hostapd_reset_ap_wps_ie(hapd); + if (hapd->drv_priv) + hostapd_reset_ap_wps_ie(hapd); return; } @@ -1067,10 +1064,14 @@ int hostapd_init_wps(struct hostapd_data *hapd, if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) wps->auth_types |= WPS_AUTH_WPA2; - if (conf->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) + if (conf->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) { wps->encr_types |= WPS_ENCR_AES; - if (conf->rsn_pairwise & WPA_CIPHER_TKIP) + wps->encr_types_rsn |= WPS_ENCR_AES; + } + if (conf->rsn_pairwise & WPA_CIPHER_TKIP) { wps->encr_types |= WPS_ENCR_TKIP; + wps->encr_types_rsn |= WPS_ENCR_TKIP; + } } if (conf->wpa & WPA_PROTO_WPA) { @@ -1079,10 +1080,14 @@ int hostapd_init_wps(struct hostapd_data *hapd, if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) wps->auth_types |= WPS_AUTH_WPA; - if (conf->wpa_pairwise & WPA_CIPHER_CCMP) + if (conf->wpa_pairwise & WPA_CIPHER_CCMP) { wps->encr_types |= WPS_ENCR_AES; - if (conf->wpa_pairwise & WPA_CIPHER_TKIP) + wps->encr_types_wpa |= WPS_ENCR_AES; + } + if (conf->wpa_pairwise & WPA_CIPHER_TKIP) { wps->encr_types |= WPS_ENCR_TKIP; + wps->encr_types_wpa |= WPS_ENCR_TKIP; + } } if (conf->ssid.security_policy == SECURITY_PLAINTEXT) { @@ -1122,6 +1127,8 @@ int hostapd_init_wps(struct hostapd_data *hapd, /* Override parameters to enable security by default */ wps->auth_types = WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK; wps->encr_types = WPS_ENCR_AES | WPS_ENCR_TKIP; + wps->encr_types_rsn = WPS_ENCR_AES | WPS_ENCR_TKIP; + wps->encr_types_wpa = WPS_ENCR_AES | WPS_ENCR_TKIP; } wps->ap_settings = conf->ap_settings; @@ -1614,7 +1621,8 @@ const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout) unsigned int pin; struct wps_ap_pin_data data; - pin = wps_generate_pin(); + if (wps_generate_pin(&pin) < 0) + return NULL; os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%08u", pin); data.timeout = timeout; hostapd_wps_for_each(hapd, wps_ap_pin_set, &data); diff --git a/contrib/wpa/src/common/cli.c b/contrib/wpa/src/common/cli.c new file mode 100644 index 000000000000..b583d1cd575d --- /dev/null +++ b/contrib/wpa/src/common/cli.c @@ -0,0 +1,267 @@ +/* + * Common hostapd/wpa_supplicant command line interface functions + * Copyright (c) 2004-2016, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "utils/common.h" +#include "common/cli.h" + + +const char *const cli_license = +"This software may be distributed under the terms of the BSD license.\n" +"See README for more details.\n"; + +const char *const cli_full_license = +"This software may be distributed under the terms of the BSD license.\n" +"\n" +"Redistribution and use in source and binary forms, with or without\n" +"modification, are permitted provided that the following conditions are\n" +"met:\n" +"\n" +"1. Redistributions of source code must retain the above copyright\n" +" notice, this list of conditions and the following disclaimer.\n" +"\n" +"2. Redistributions in binary form must reproduce the above copyright\n" +" notice, this list of conditions and the following disclaimer in the\n" +" documentation and/or other materials provided with the distribution.\n" +"\n" +"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n" +" names of its contributors may be used to endorse or promote products\n" +" derived from this software without specific prior written permission.\n" +"\n" +"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n" +"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n" +"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n" +"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n" +"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n" +"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n" +"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n" +"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n" +"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n" +"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n" +"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" +"\n"; + + +void cli_txt_list_free(struct cli_txt_entry *e) +{ + dl_list_del(&e->list); + os_free(e->txt); + os_free(e); +} + + +void cli_txt_list_flush(struct dl_list *list) +{ + struct cli_txt_entry *e; + + while ((e = dl_list_first(list, struct cli_txt_entry, list))) + cli_txt_list_free(e); +} + + +struct cli_txt_entry * cli_txt_list_get(struct dl_list *txt_list, + const char *txt) +{ + struct cli_txt_entry *e; + + dl_list_for_each(e, txt_list, struct cli_txt_entry, list) { + if (os_strcmp(e->txt, txt) == 0) + return e; + } + return NULL; +} + + +void cli_txt_list_del(struct dl_list *txt_list, const char *txt) +{ + struct cli_txt_entry *e; + + e = cli_txt_list_get(txt_list, txt); + if (e) + cli_txt_list_free(e); +} + + +void cli_txt_list_del_addr(struct dl_list *txt_list, const char *txt) +{ + u8 addr[ETH_ALEN]; + char buf[18]; + + if (hwaddr_aton(txt, addr) < 0) + return; + os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr)); + cli_txt_list_del(txt_list, buf); +} + + +void cli_txt_list_del_word(struct dl_list *txt_list, const char *txt, + int separator) +{ + const char *end; + char *buf; + + end = os_strchr(txt, separator); + if (end == NULL) + end = txt + os_strlen(txt); + buf = dup_binstr(txt, end - txt); + if (buf == NULL) + return; + cli_txt_list_del(txt_list, buf); + os_free(buf); +} + + +int cli_txt_list_add(struct dl_list *txt_list, const char *txt) +{ + struct cli_txt_entry *e; + + e = cli_txt_list_get(txt_list, txt); + if (e) + return 0; + e = os_zalloc(sizeof(*e)); + if (e == NULL) + return -1; + e->txt = os_strdup(txt); + if (e->txt == NULL) { + os_free(e); + return -1; + } + dl_list_add(txt_list, &e->list); + return 0; +} + + +int cli_txt_list_add_addr(struct dl_list *txt_list, const char *txt) +{ + u8 addr[ETH_ALEN]; + char buf[18]; + + if (hwaddr_aton(txt, addr) < 0) + return -1; + os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr)); + return cli_txt_list_add(txt_list, buf); +} + + +int cli_txt_list_add_word(struct dl_list *txt_list, const char *txt, + int separator) +{ + const char *end; + char *buf; + int ret; + + end = os_strchr(txt, separator); + if (end == NULL) + end = txt + os_strlen(txt); + buf = dup_binstr(txt, end - txt); + if (buf == NULL) + return -1; + ret = cli_txt_list_add(txt_list, buf); + os_free(buf); + return ret; +} + + +char ** cli_txt_list_array(struct dl_list *txt_list) +{ + unsigned int i, count = dl_list_len(txt_list); + char **res; + struct cli_txt_entry *e; + + res = os_calloc(count + 1, sizeof(char *)); + if (res == NULL) + return NULL; + + i = 0; + dl_list_for_each(e, txt_list, struct cli_txt_entry, list) { + res[i] = os_strdup(e->txt); + if (res[i] == NULL) + break; + i++; + } + + return res; +} + + +int get_cmd_arg_num(const char *str, int pos) +{ + int arg = 0, i; + + for (i = 0; i <= pos; i++) { + if (str[i] != ' ') { + arg++; + while (i <= pos && str[i] != ' ') + i++; + } + } + + if (arg > 0) + arg--; + return arg; +} + + +int write_cmd(char *buf, size_t buflen, const char *cmd, int argc, char *argv[]) +{ + int i, res; + char *pos, *end; + + pos = buf; + end = buf + buflen; + + res = os_snprintf(pos, end - pos, "%s", cmd); + if (os_snprintf_error(end - pos, res)) + goto fail; + pos += res; + + for (i = 0; i < argc; i++) { + res = os_snprintf(pos, end - pos, " %s", argv[i]); + if (os_snprintf_error(end - pos, res)) + goto fail; + pos += res; + } + + buf[buflen - 1] = '\0'; + return 0; + +fail: + printf("Too long command\n"); + return -1; +} + + +int tokenize_cmd(char *cmd, char *argv[]) +{ + char *pos; + int argc = 0; + + pos = cmd; + for (;;) { + while (*pos == ' ') + pos++; + if (*pos == '\0') + break; + argv[argc] = pos; + argc++; + if (argc == max_args) + break; + if (*pos == '"') { + char *pos2 = os_strrchr(pos, '"'); + if (pos2) + pos = pos2 + 1; + } + while (*pos != '\0' && *pos != ' ') + pos++; + if (*pos == ' ') + *pos++ = '\0'; + } + + return argc; +} diff --git a/contrib/wpa/src/common/cli.h b/contrib/wpa/src/common/cli.h new file mode 100644 index 000000000000..41ef329696ea --- /dev/null +++ b/contrib/wpa/src/common/cli.h @@ -0,0 +1,47 @@ +/* + * Common hostapd/wpa_supplicant command line interface functionality + * Copyright (c) 2004-2016, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef CLI_H +#define CLI_H + +#include "utils/list.h" + +extern const char *const cli_license; +extern const char *const cli_full_license; + +struct cli_txt_entry { + struct dl_list list; + char *txt; +}; + +void cli_txt_list_free(struct cli_txt_entry *e); +void cli_txt_list_flush(struct dl_list *list); + +struct cli_txt_entry * +cli_txt_list_get(struct dl_list *txt_list, const char *txt); + +void cli_txt_list_del(struct dl_list *txt_list, const char *txt); +void cli_txt_list_del_addr(struct dl_list *txt_list, const char *txt); +void cli_txt_list_del_word(struct dl_list *txt_list, const char *txt, + int separator); + +int cli_txt_list_add(struct dl_list *txt_list, const char *txt); +int cli_txt_list_add_addr(struct dl_list *txt_list, const char *txt); +int cli_txt_list_add_word(struct dl_list *txt_list, const char *txt, + int separator); + +char ** cli_txt_list_array(struct dl_list *txt_list); + +int get_cmd_arg_num(const char *str, int pos); +int write_cmd(char *buf, size_t buflen, const char *cmd, int argc, + char *argv[]); + +#define max_args 10 +int tokenize_cmd(char *cmd, char *argv[]); + +#endif /* CLI_H */ diff --git a/contrib/wpa/src/common/common_module_tests.c b/contrib/wpa/src/common/common_module_tests.c index d69448bd3800..e0769c08e764 100644 --- a/contrib/wpa/src/common/common_module_tests.c +++ b/contrib/wpa/src/common/common_module_tests.c @@ -9,6 +9,7 @@ #include "utils/includes.h" #include "utils/common.h" +#include "utils/module_tests.h" #include "ieee802_11_common.h" #include "ieee802_11_defs.h" #include "gas.h" diff --git a/contrib/wpa/src/common/ctrl_iface_common.c b/contrib/wpa/src/common/ctrl_iface_common.c new file mode 100644 index 000000000000..ebbe6ffdb385 --- /dev/null +++ b/contrib/wpa/src/common/ctrl_iface_common.c @@ -0,0 +1,173 @@ +/* + * Common hostapd/wpa_supplicant ctrl iface code. + * Copyright (c) 2002-2013, Jouni Malinen + * Copyright (c) 2015, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#include +#include + +#include "utils/common.h" +#include "ctrl_iface_common.h" + +static int sockaddr_compare(struct sockaddr_storage *a, socklen_t a_len, + struct sockaddr_storage *b, socklen_t b_len) +{ + if (a->ss_family != b->ss_family) + return 1; + + switch (a->ss_family) { +#ifdef CONFIG_CTRL_IFACE_UDP + case AF_INET: + { + struct sockaddr_in *in_a, *in_b; + + in_a = (struct sockaddr_in *) a; + in_b = (struct sockaddr_in *) b; + + if (in_a->sin_port != in_b->sin_port) + return 1; + if (in_a->sin_addr.s_addr != in_b->sin_addr.s_addr) + return 1; + break; + } + case AF_INET6: + { + struct sockaddr_in6 *in6_a, *in6_b; + + in6_a = (struct sockaddr_in6 *) a; + in6_b = (struct sockaddr_in6 *) b; + + if (in6_a->sin6_port != in6_b->sin6_port) + return 1; + if (os_memcmp(&in6_a->sin6_addr, &in6_b->sin6_addr, + sizeof(in6_a->sin6_addr)) != 0) + return 1; + break; + } +#endif /* CONFIG_CTRL_IFACE_UDP */ +#ifdef CONFIG_CTRL_IFACE_UNIX + case AF_UNIX: + { + struct sockaddr_un *u_a, *u_b; + + u_a = (struct sockaddr_un *) a; + u_b = (struct sockaddr_un *) b; + + if (a_len != b_len || + os_memcmp(u_a->sun_path, u_b->sun_path, + a_len - offsetof(struct sockaddr_un, sun_path)) + != 0) + return 1; + break; + } +#endif /* CONFIG_CTRL_IFACE_UNIX */ + default: + return 1; + } + + return 0; +} + + +void sockaddr_print(int level, const char *msg, struct sockaddr_storage *sock, + socklen_t socklen) +{ + switch (sock->ss_family) { +#ifdef CONFIG_CTRL_IFACE_UDP + case AF_INET: + case AF_INET6: + { + char host[NI_MAXHOST] = { 0 }; + char service[NI_MAXSERV] = { 0 }; + + getnameinfo((struct sockaddr *) sock, socklen, + host, sizeof(host), + service, sizeof(service), + NI_NUMERICHOST); + + wpa_printf(level, "%s %s:%s", msg, host, service); + break; + } +#endif /* CONFIG_CTRL_IFACE_UDP */ +#ifdef CONFIG_CTRL_IFACE_UNIX + case AF_UNIX: + { + char addr_txt[200]; + + printf_encode(addr_txt, sizeof(addr_txt), + (u8 *) ((struct sockaddr_un *) sock)->sun_path, + socklen - offsetof(struct sockaddr_un, sun_path)); + wpa_printf(level, "%s %s", msg, addr_txt); + break; + } +#endif /* CONFIG_CTRL_IFACE_UNIX */ + default: + wpa_printf(level, "%s", msg); + break; + } +} + + +int ctrl_iface_attach(struct dl_list *ctrl_dst, struct sockaddr_storage *from, + socklen_t fromlen) +{ + struct wpa_ctrl_dst *dst; + + dst = os_zalloc(sizeof(*dst)); + if (dst == NULL) + return -1; + os_memcpy(&dst->addr, from, fromlen); + dst->addrlen = fromlen; + dst->debug_level = MSG_INFO; + dl_list_add(ctrl_dst, &dst->list); + + sockaddr_print(MSG_DEBUG, "CTRL_IFACE monitor attached", from, fromlen); + return 0; +} + + +int ctrl_iface_detach(struct dl_list *ctrl_dst, struct sockaddr_storage *from, + socklen_t fromlen) +{ + struct wpa_ctrl_dst *dst; + + dl_list_for_each(dst, ctrl_dst, struct wpa_ctrl_dst, list) { + if (!sockaddr_compare(from, fromlen, + &dst->addr, dst->addrlen)) { + sockaddr_print(MSG_DEBUG, "CTRL_IFACE monitor detached", + from, fromlen); + dl_list_del(&dst->list); + os_free(dst); + return 0; + } + } + + return -1; +} + + +int ctrl_iface_level(struct dl_list *ctrl_dst, struct sockaddr_storage *from, + socklen_t fromlen, const char *level) +{ + struct wpa_ctrl_dst *dst; + + wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level); + + dl_list_for_each(dst, ctrl_dst, struct wpa_ctrl_dst, list) { + if (!sockaddr_compare(from, fromlen, + &dst->addr, dst->addrlen)) { + sockaddr_print(MSG_DEBUG, + "CTRL_IFACE changed monitor level", + from, fromlen); + dst->debug_level = atoi(level); + return 0; + } + } + + return -1; +} diff --git a/contrib/wpa/src/common/ctrl_iface_common.h b/contrib/wpa/src/common/ctrl_iface_common.h new file mode 100644 index 000000000000..0b6e3e740291 --- /dev/null +++ b/contrib/wpa/src/common/ctrl_iface_common.h @@ -0,0 +1,38 @@ +/* + * Common hostapd/wpa_supplicant ctrl iface code. + * Copyright (c) 2002-2013, Jouni Malinen + * Copyright (c) 2015, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ +#ifndef CONTROL_IFACE_COMMON_H +#define CONTROL_IFACE_COMMON_H + +#include "utils/list.h" + +/** + * struct wpa_ctrl_dst - Data structure of control interface monitors + * + * This structure is used to store information about registered control + * interface monitors into struct wpa_supplicant. + */ +struct wpa_ctrl_dst { + struct dl_list list; + struct sockaddr_storage addr; + socklen_t addrlen; + int debug_level; + int errors; +}; + +void sockaddr_print(int level, const char *msg, struct sockaddr_storage *sock, + socklen_t socklen); + +int ctrl_iface_attach(struct dl_list *ctrl_dst, struct sockaddr_storage *from, + socklen_t fromlen); +int ctrl_iface_detach(struct dl_list *ctrl_dst, struct sockaddr_storage *from, + socklen_t fromlen); +int ctrl_iface_level(struct dl_list *ctrl_dst, struct sockaddr_storage *from, + socklen_t fromlen, const char *level); + +#endif /* CONTROL_IFACE_COMMON_H */ diff --git a/contrib/wpa/src/common/defs.h b/contrib/wpa/src/common/defs.h index 6aea3751a2bc..4f567945942e 100644 --- a/contrib/wpa/src/common/defs.h +++ b/contrib/wpa/src/common/defs.h @@ -312,6 +312,7 @@ enum wpa_ctrl_req_type { WPA_CTRL_REQ_EAP_PASSPHRASE, WPA_CTRL_REQ_SIM, WPA_CTRL_REQ_PSK_PASSPHRASE, + WPA_CTRL_REQ_EXT_CERT_CHECK, NUM_WPA_CTRL_REQS }; @@ -319,13 +320,13 @@ enum wpa_ctrl_req_type { #define EAP_MAX_METHODS 8 enum mesh_plink_state { - PLINK_LISTEN = 1, - PLINK_OPEN_SENT, - PLINK_OPEN_RCVD, + PLINK_IDLE = 1, + PLINK_OPN_SNT, + PLINK_OPN_RCVD, PLINK_CNF_RCVD, PLINK_ESTAB, PLINK_HOLDING, - PLINK_BLOCKED, + PLINK_BLOCKED, /* not defined in the IEEE 802.11 standard */ }; enum set_band { @@ -334,4 +335,10 @@ enum set_band { WPA_SETBAND_2G }; +enum wpa_radio_work_band { + BAND_2_4_GHZ = BIT(0), + BAND_5_GHZ = BIT(1), + BAND_60_GHZ = BIT(2), +}; + #endif /* DEFS_H */ diff --git a/contrib/wpa/src/common/eapol_common.h b/contrib/wpa/src/common/eapol_common.h index 6958661f78b5..d773348b42c5 100644 --- a/contrib/wpa/src/common/eapol_common.h +++ b/contrib/wpa/src/common/eapol_common.h @@ -25,7 +25,7 @@ struct ieee802_1x_hdr { struct ieee8023_hdr { u8 dest[ETH_ALEN]; u8 src[ETH_ALEN]; - u16 ethertype; + be16 ethertype; } STRUCT_PACKED; #ifdef _MSC_VER diff --git a/contrib/wpa/src/common/ieee802_11_common.c b/contrib/wpa/src/common/ieee802_11_common.c index d07a316a7929..b6bc449bf7dc 100644 --- a/contrib/wpa/src/common/ieee802_11_common.c +++ b/contrib/wpa/src/common/ieee802_11_common.c @@ -115,6 +115,11 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen, elems->osen = pos; elems->osen_len = elen; break; + case MBO_OUI_TYPE: + /* MBO-OCE */ + elems->mbo = pos; + elems->mbo_len = elen; + break; default: wpa_printf(MSG_MSGDUMP, "Unknown WFA " "information element ignored " @@ -366,6 +371,14 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, elems->mb_ies.ies[elems->mb_ies.nof_ies].ie_len = elen; elems->mb_ies.nof_ies++; break; + case WLAN_EID_SUPPORTED_OPERATING_CLASSES: + elems->supp_op_classes = pos; + elems->supp_op_classes_len = elen; + break; + case WLAN_EID_RRM_ENABLED_CAPABILITIES: + elems->rrm_enabled = pos; + elems->rrm_enabled_len = elen; + break; default: unknown++; if (!show_errors) @@ -398,8 +411,8 @@ int ieee802_11_ie_count(const u8 *ies, size_t ies_len) pos = ies; end = ies + ies_len; - while (pos + 2 <= end) { - if (pos + 2 + pos[1] > end) + while (end - pos >= 2) { + if (2 + pos[1] > end - pos) break; count++; pos += 2 + pos[1]; @@ -419,8 +432,8 @@ struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len, end = ies + ies_len; ie = NULL; - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) + 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) { @@ -441,8 +454,8 @@ 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 (pos + 1 < end) { - if (pos + 2 + pos[1] > end) + 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) @@ -570,7 +583,8 @@ enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel) { u8 op_class; - return ieee80211_freq_to_channel_ext(freq, 0, 0, &op_class, channel); + return ieee80211_freq_to_channel_ext(freq, 0, VHT_CHANWIDTH_USE_HT, + &op_class, channel); } @@ -579,7 +593,7 @@ enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel) * for HT40 and VHT. DFS channels are not covered. * @freq: Frequency (MHz) to convert * @sec_channel: 0 = non-HT40, 1 = sec. channel above, -1 = sec. channel below - * @vht: 0 - non-VHT, 1 - 80 MHz + * @vht: VHT channel width (VHT_CHANWIDTH_*) * @op_class: Buffer for returning operating class * @channel: Buffer for returning channel number * Returns: hw_mode on success, NUM_HOSTAPD_MODES on failure @@ -588,6 +602,8 @@ enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq, int sec_channel, int vht, u8 *op_class, u8 *channel) { + u8 vht_opclass; + /* TODO: more operating classes */ if (sec_channel > 1 || sec_channel < -1) @@ -631,17 +647,32 @@ enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq, return HOSTAPD_MODE_IEEE80211A; } + switch (vht) { + case VHT_CHANWIDTH_80MHZ: + vht_opclass = 128; + break; + case VHT_CHANWIDTH_160MHZ: + vht_opclass = 129; + break; + case VHT_CHANWIDTH_80P80MHZ: + vht_opclass = 130; + break; + default: + vht_opclass = 0; + break; + } + /* 5 GHz, channels 36..48 */ if (freq >= 5180 && freq <= 5240) { if ((freq - 5000) % 5) return NUM_HOSTAPD_MODES; - if (sec_channel == 1) + if (vht_opclass) + *op_class = vht_opclass; + else if (sec_channel == 1) *op_class = 116; else if (sec_channel == -1) *op_class = 117; - else if (vht) - *op_class = 128; else *op_class = 115; @@ -650,31 +681,40 @@ enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq, return HOSTAPD_MODE_IEEE80211A; } - /* 5 GHz, channels 149..161 */ - if (freq >= 5745 && freq <= 5805) { - if ((freq - 5000) % 5) - return NUM_HOSTAPD_MODES; - - if (sec_channel == 1) - *op_class = 126; - else if (sec_channel == -1) - *op_class = 127; - else if (vht) - *op_class = 128; - else - *op_class = 124; - - *channel = (freq - 5000) / 5; - - return HOSTAPD_MODE_IEEE80211A; - } - /* 5 GHz, channels 149..169 */ if (freq >= 5745 && freq <= 5845) { if ((freq - 5000) % 5) return NUM_HOSTAPD_MODES; - *op_class = 125; + if (vht_opclass) + *op_class = vht_opclass; + else if (sec_channel == 1) + *op_class = 126; + else if (sec_channel == -1) + *op_class = 127; + else if (freq <= 5805) + *op_class = 124; + else + *op_class = 125; + + *channel = (freq - 5000) / 5; + + return HOSTAPD_MODE_IEEE80211A; + } + + /* 5 GHz, channels 100..140 */ + if (freq >= 5000 && freq <= 5700) { + if ((freq - 5000) % 5) + return NUM_HOSTAPD_MODES; + + if (vht_opclass) + *op_class = vht_opclass; + else if (sec_channel == 1) + *op_class = 122; + else if (sec_channel == -1) + *op_class = 123; + else + *op_class = 121; *channel = (freq - 5000) / 5; @@ -1145,3 +1185,135 @@ struct wpabuf * mb_ies_by_info(struct mb_ies_info *info) return mb_ies; } + + +const struct oper_class_map global_op_class[] = { + { HOSTAPD_MODE_IEEE80211G, 81, 1, 13, 1, BW20, P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211G, 82, 14, 14, 1, BW20, NO_P2P_SUPP }, + + /* Do not enable HT40 on 2.4 GHz for P2P use for now */ + { HOSTAPD_MODE_IEEE80211G, 83, 1, 9, 1, BW40PLUS, NO_P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211G, 84, 5, 13, 1, BW40MINUS, NO_P2P_SUPP }, + + { HOSTAPD_MODE_IEEE80211A, 115, 36, 48, 4, BW20, P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211A, 116, 36, 44, 8, BW40PLUS, P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211A, 117, 40, 48, 8, BW40MINUS, P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211A, 118, 52, 64, 4, BW20, NO_P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211A, 119, 52, 60, 8, BW40PLUS, NO_P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211A, 120, 56, 64, 8, BW40MINUS, NO_P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211A, 121, 100, 140, 4, BW20, NO_P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211A, 122, 100, 132, 8, BW40PLUS, NO_P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211A, 123, 104, 136, 8, BW40MINUS, NO_P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211A, 124, 149, 161, 4, BW20, P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211A, 125, 149, 169, 4, BW20, P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211A, 126, 149, 157, 8, BW40PLUS, P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211A, 127, 153, 161, 8, BW40MINUS, P2P_SUPP }, + + /* + * IEEE P802.11ac/D7.0 Table E-4 actually talks about channel center + * frequency index 42, 58, 106, 122, 138, 155 with channel spacing of + * 80 MHz, but currently use the following definition for simplicity + * (these center frequencies are not actual channels, which makes + * wpas_p2p_allow_channel() fail). wpas_p2p_verify_80mhz() should take + * care of removing invalid channels. + */ + { HOSTAPD_MODE_IEEE80211A, 128, 36, 161, 4, BW80, P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211A, 129, 50, 114, 16, BW160, P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211A, 130, 36, 161, 4, BW80P80, P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211AD, 180, 1, 4, 1, BW2160, P2P_SUPP }, + { -1, 0, 0, 0, 0, BW20, NO_P2P_SUPP } +}; + + +static enum phy_type ieee80211_phy_type_by_freq(int freq) +{ + enum hostapd_hw_mode hw_mode; + u8 channel; + + hw_mode = ieee80211_freq_to_chan(freq, &channel); + + switch (hw_mode) { + case HOSTAPD_MODE_IEEE80211A: + return PHY_TYPE_OFDM; + case HOSTAPD_MODE_IEEE80211B: + return PHY_TYPE_HRDSSS; + case HOSTAPD_MODE_IEEE80211G: + return PHY_TYPE_ERP; + case HOSTAPD_MODE_IEEE80211AD: + return PHY_TYPE_DMG; + default: + return PHY_TYPE_UNSPECIFIED; + }; +} + + +/* ieee80211_get_phy_type - Derive the phy type by freq and bandwidth */ +enum phy_type ieee80211_get_phy_type(int freq, int ht, int vht) +{ + if (vht) + return PHY_TYPE_VHT; + if (ht) + return PHY_TYPE_HT; + + return ieee80211_phy_type_by_freq(freq); +} + + +size_t global_op_class_size = ARRAY_SIZE(global_op_class); + + +/** + * get_ie - Fetch a specified information element from IEs buffer + * @ies: Information elements buffer + * @len: Information elements buffer length + * @eid: Information element identifier (WLAN_EID_*) + * Returns: Pointer to the information element (id field) or %NULL if not found + * + * This function returns the first matching information element in the IEs + * buffer or %NULL in case the element is not found. + */ +const u8 * get_ie(const u8 *ies, size_t len, u8 eid) +{ + const u8 *end; + + 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]; + } + + return NULL; +} + + +size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len) +{ + /* + * MBO IE requires 6 bytes without the attributes: EID (1), length (1), + * OUI (3), OUI type (1). + */ + if (len < 6 + attr_len) { + wpa_printf(MSG_DEBUG, + "MBO: Not enough room in buffer for MBO IE: buf len = %zu, attr_len = %zu", + len, attr_len); + return 0; + } + + *buf++ = WLAN_EID_VENDOR_SPECIFIC; + *buf++ = attr_len + 4; + WPA_PUT_BE24(buf, OUI_WFA); + buf += 3; + *buf++ = MBO_OUI_TYPE; + os_memcpy(buf, attr, attr_len); + + return 6 + attr_len; +} diff --git a/contrib/wpa/src/common/ieee802_11_common.h b/contrib/wpa/src/common/ieee802_11_common.h index 55ce0223d923..42f39096f86c 100644 --- a/contrib/wpa/src/common/ieee802_11_common.h +++ b/contrib/wpa/src/common/ieee802_11_common.h @@ -9,6 +9,8 @@ #ifndef IEEE802_11_COMMON_H #define IEEE802_11_COMMON_H +#include "defs.h" + #define MAX_NOF_MB_IES_SUPPORTED 5 struct mb_ies_info { @@ -56,9 +58,12 @@ struct ieee802_11_elems { const u8 *bss_max_idle_period; const u8 *ssid_list; const u8 *osen; + const u8 *mbo; const u8 *ampe; const u8 *mic; const u8 *pref_freq_list; + const u8 *supp_op_classes; + const u8 *rrm_enabled; u8 ssid_len; u8 supp_rates_len; @@ -85,9 +90,13 @@ struct ieee802_11_elems { u8 ext_capab_len; u8 ssid_list_len; u8 osen_len; + u8 mbo_len; u8 ampe_len; u8 mic_len; u8 pref_freq_list_len; + u8 supp_op_classes_len; + u8 rrm_enabled_len; + struct mb_ies_info mb_ies; }; @@ -118,6 +127,7 @@ enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq, int sec_channel, int vht, u8 *op_class, u8 *channel); int ieee80211_is_dfs(int freq); +enum phy_type ieee80211_get_phy_type(int freq, int ht, int vht); int supp_rates_11b_only(struct ieee802_11_elems *elems); int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf, @@ -125,4 +135,22 @@ int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf, struct wpabuf * mb_ies_by_info(struct mb_ies_info *info); const char * fc2str(u16 fc); + +struct oper_class_map { + enum hostapd_hw_mode mode; + u8 op_class; + u8 min_chan; + u8 max_chan; + u8 inc; + enum { BW20, BW40PLUS, BW40MINUS, BW80, BW2160, BW160, BW80P80 } bw; + enum { P2P_SUPP, NO_P2P_SUPP } p2p; +}; + +extern const struct oper_class_map global_op_class[]; +extern size_t global_op_class_size; + +const u8 * get_ie(const u8 *ies, size_t len, u8 eid); + +size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len); + #endif /* IEEE802_11_COMMON_H */ diff --git a/contrib/wpa/src/common/ieee802_11_defs.h b/contrib/wpa/src/common/ieee802_11_defs.h index 44530ce3cee6..d453aec790ad 100644 --- a/contrib/wpa/src/common/ieee802_11_defs.h +++ b/contrib/wpa/src/common/ieee802_11_defs.h @@ -94,8 +94,13 @@ #define WLAN_CAPABILITY_PBCC BIT(6) #define WLAN_CAPABILITY_CHANNEL_AGILITY BIT(7) #define WLAN_CAPABILITY_SPECTRUM_MGMT BIT(8) +#define WLAN_CAPABILITY_QOS BIT(9) #define WLAN_CAPABILITY_SHORT_SLOT_TIME BIT(10) +#define WLAN_CAPABILITY_APSD BIT(11) +#define WLAN_CAPABILITY_RADIO_MEASUREMENT BIT(12) #define WLAN_CAPABILITY_DSSS_OFDM BIT(13) +#define WLAN_CAPABILITY_DELAYED_BLOCK_ACK BIT(14) +#define WLAN_CAPABILITY_IMM_BLOCK_ACK BIT(15) /* Status codes (IEEE 802.11-2007, 7.3.1.9, Table 7-23) */ #define WLAN_STATUS_SUCCESS 0 @@ -247,6 +252,7 @@ #define WLAN_EID_TIMEOUT_INTERVAL 56 #define WLAN_EID_RIC_DATA 57 #define WLAN_EID_SUPPORTED_OPERATING_CLASSES 59 +#define WLAN_EID_EXT_CHANSWITCH_ANN 60 #define WLAN_EID_HT_OPERATION 61 #define WLAN_EID_SECONDARY_CHANNEL_OFFSET 62 #define WLAN_EID_WAPI 68 @@ -360,6 +366,16 @@ /* byte 1 (out of 5) */ #define WLAN_RRM_CAPS_LINK_MEASUREMENT BIT(0) #define WLAN_RRM_CAPS_NEIGHBOR_REPORT BIT(1) +/* byte 2 (out of 5) */ +#define WLAN_RRM_CAPS_LCI_MEASUREMENT BIT(4) +/* byte 5 (out of 5) */ +#define WLAN_RRM_CAPS_FTM_RANGE_REPORT BIT(2) + +/* + * IEEE P802.11-REVmc/D5.0, 9.4.2.21.19 (Fine Timing Measurement Range + * request) - Minimum AP count + */ +#define WLAN_RRM_RANGE_REQ_MAX_MIN_AP 15 /* Timeout Interval Type */ #define WLAN_TIMEOUT_REASSOC_DEADLINE 1 @@ -407,7 +423,12 @@ enum anqp_info_id { ANQP_AP_LOCATION_PUBLIC_URI = 267, ANQP_DOMAIN_NAME = 268, ANQP_EMERGENCY_ALERT_URI = 269, + ANQP_TDLS_CAPABILITY = 270, ANQP_EMERGENCY_NAI = 271, + ANQP_NEIGHBOR_REPORT = 272, + ANQP_VENUE_URL = 277, + ANQP_ADVICE_OF_CHARGE = 278, + ANQP_LOCAL_CONTENT = 279, ANQP_VENDOR_SPECIFIC = 56797 }; @@ -442,6 +463,48 @@ enum nai_realm_eap_cred_type { NAI_REALM_CRED_TYPE_VENDOR_SPECIFIC = 10 }; +/* + * IEEE P802.11-REVmc/D5.0 Table 9-81 - Measurement type definitions for + * measurement requests + */ +enum measure_type { + MEASURE_TYPE_BASIC = 0, + MEASURE_TYPE_CCA = 1, + MEASURE_TYPE_RPI_HIST = 2, + MEASURE_TYPE_CHANNEL_LOAD = 3, + MEASURE_TYPE_NOISE_HIST = 4, + MEASURE_TYPE_BEACON = 5, + MEASURE_TYPE_FRAME = 6, + MEASURE_TYPE_STA_STATISTICS = 7, + MEASURE_TYPE_LCI = 8, + MEASURE_TYPE_TRANSMIT_STREAM = 9, + MEASURE_TYPE_MULTICAST_DIAG = 10, + MEASURE_TYPE_LOCATION_CIVIC = 11, + MEASURE_TYPE_LOCATION_ID = 12, + MEASURE_TYPE_DIRECTIONAL_CHAN_QUALITY = 13, + MEASURE_TYPE_DIRECTIONAL_MEASURE = 14, + MEASURE_TYPE_DIRECTIONAL_STATS = 15, + MEASURE_TYPE_FTM_RANGE = 16, + MEASURE_TYPE_MEASURE_PAUSE = 255, +}; + +/* IEEE Std 802.11-2012 Table 8-71 - Location subject definition */ +enum location_subject { + LOCATION_SUBJECT_LOCAL = 0, + LOCATION_SUBJECT_REMOTE = 1, + LOCATION_SUBJECT_3RD_PARTY = 2, +}; + +/* + * IEEE P802.11-REVmc/D5.0 Table 9-94 - Optional subelement IDs for LCI request + */ +enum lci_req_subelem { + LCI_REQ_SUBELEM_AZIMUTH_REQ = 1, + LCI_REQ_SUBELEM_ORIGINATOR_MAC_ADDR = 2, + LCI_REQ_SUBELEM_TARGET_MAC_ADDR = 3, + LCI_REQ_SUBELEM_MAX_AGE = 4, +}; + #ifdef _MSC_VER #pragma pack(push, 1) #endif /* _MSC_VER */ @@ -516,10 +579,7 @@ struct ieee80211_mgmt { * FH Params, DS Params, CF Params, IBSS Params, TIM */ u8 variable[]; } STRUCT_PACKED beacon; - struct { - /* only variable items: SSID, Supported rates */ - u8 variable[0]; - } STRUCT_PACKED probe_req; + /* probe_req: only variable items: SSID, Supported rates */ struct { u8 timestamp[8]; le16 beacon_int; @@ -625,12 +685,19 @@ struct ieee80211_mgmt { u8 action; u8 variable[]; } STRUCT_PACKED fst_action; + struct { + u8 action; + u8 dialog_token; + u8 variable[]; + } STRUCT_PACKED rrm; } u; } STRUCT_PACKED action; } u; } STRUCT_PACKED; +#define IEEE80211_MAX_MMPDU_SIZE 2304 + /* Rx MCS bitmask is in the first 77 bits of supported_mcs_set */ #define IEEE80211_HT_MCS_MASK_LEN 10 @@ -690,9 +757,14 @@ struct ieee80211_ampe_ie { u8 selected_pairwise_suite[4]; u8 local_nonce[32]; u8 peer_nonce[32]; - u8 mgtk[16]; - u8 key_rsc[8]; - u8 key_expiration[4]; + /* Followed by + * Key Replay Counter[8] (optional) + * (only in Mesh Group Key Inform/Acknowledge frames) + * GTKdata[variable] (optional) + * (MGTK[variable] || Key RSC[8] || GTKExpirationTime[4]) + * IGTKdata[variable] (optional) + * (Key ID[2], IPN[6], IGTK[variable] in IGTK KDE format) + */ } STRUCT_PACKED; #ifdef _MSC_VER @@ -879,6 +951,8 @@ struct ieee80211_ampe_ie { #define WFD_OUI_TYPE 10 #define HS20_IE_VENDOR_TYPE 0x506f9a10 #define OSEN_IE_VENDOR_TYPE 0x506f9a12 +#define MBO_IE_VENDOR_TYPE 0x506f9a16 +#define MBO_OUI_TYPE 22 #define WMM_OUI_TYPE 2 #define WMM_OUI_SUBTYPE_INFORMATION_ELEMENT 0 @@ -1021,6 +1095,95 @@ enum wmm_ac { #define HS20_DEAUTH_REASON_CODE_BSS 0 #define HS20_DEAUTH_REASON_CODE_ESS 1 +/* MBO v0.0_r19, 4.2: MBO Attributes */ +/* Table 4-5: MBO Attributes */ +enum mbo_attr_id { + MBO_ATTR_ID_AP_CAPA_IND = 1, + MBO_ATTR_ID_NON_PREF_CHAN_REPORT = 2, + MBO_ATTR_ID_CELL_DATA_CAPA = 3, + MBO_ATTR_ID_ASSOC_DISALLOW = 4, + MBO_ATTR_ID_CELL_DATA_PREF = 5, + MBO_ATTR_ID_TRANSITION_REASON = 6, + MBO_ATTR_ID_TRANSITION_REJECT_REASON = 7, + MBO_ATTR_ID_ASSOC_RETRY_DELAY = 8, +}; + +/* MBO v0.0_r19, 4.2.1: MBO AP Capability Indication Attribute */ +/* Table 4-7: MBO AP Capability Indication Field Values */ +#define MBO_AP_CAPA_CELL_AWARE BIT(6) + +/* MBO v0.0_r19, 4.2.2: Non-preferred Channel Report Attribute */ +/* Table 4-10: Reason Code Field Values */ +enum mbo_non_pref_chan_reason { + MBO_NON_PREF_CHAN_REASON_UNSPECIFIED = 0, + MBO_NON_PREF_CHAN_REASON_RSSI = 1, + MBO_NON_PREF_CHAN_REASON_EXT_INTERFERENCE = 2, + MBO_NON_PREF_CHAN_REASON_INT_INTERFERENCE = 3, +}; + +/* MBO v0.0_r19, 4.2.3: Cellular Data Capabilities Attribute */ +/* Table 4-13: Cellular Data Connectivity Field */ +enum mbo_cellular_capa { + MBO_CELL_CAPA_AVAILABLE = 1, + MBO_CELL_CAPA_NOT_AVAILABLE = 2, + MBO_CELL_CAPA_NOT_SUPPORTED = 3, +}; + +/* MBO v0.0_r19, 4.2.4: Association Disallowed Attribute */ +/* Table 4-15: Reason Code Field Values */ +enum mbo_assoc_disallow_reason { + MBO_ASSOC_DISALLOW_REASON_UNSPECIFIED = 1, + MBO_ASSOC_DISALLOW_REASON_MAX_STA = 2, + MBO_ASSOC_DISALLOW_REASON_AIR_INTERFERENCE = 3, + MBO_ASSOC_DISALLOW_REASON_AUTH_SERVER_OVERLOAD = 4, + MBO_ASSOC_DISALLOW_REASON_LOW_RSSI = 5, +}; + +/* MBO v0.0_r19, 4.2.5: Cellular Data Connection Preference Attribute */ +/* Table 4-17: Cellular Preference Field Values */ +enum mbo_cell_pref { + MBO_CELL_PREF_EXCLUDED = 0, + MBO_CELL_PREF_NO_USE = 1, + MBO_CELL_PREF_USE = 255 +}; + +/* MBO v0.0_r19, 4.2.6: Transition Reason Code Attribute */ +/* Table 4-19: Transition Reason Code Field Values */ +enum mbo_transition_reason { + MBO_TRANSITION_REASON_UNSPECIFIED = 0, + MBO_TRANSITION_REASON_FRAME_LOSS = 1, + MBO_TRANSITION_REASON_DELAY = 2, + MBO_TRANSITION_REASON_BANDWIDTH = 3, + MBO_TRANSITION_REASON_LOAD_BALANCE = 4, + MBO_TRANSITION_REASON_RSSI = 5, + MBO_TRANSITION_REASON_RETRANSMISSIONS = 6, + MBO_TRANSITION_REASON_INTERFERENCE = 7, + MBO_TRANSITION_REASON_GRAY_ZONE = 8, + MBO_TRANSITION_REASON_PREMIUM_AP = 9, +}; + +/* MBO v0.0_r19, 4.2.7: Transition Rejection Reason Code Attribute */ +/* Table 4-21: Transition Rejection Reason Code Field Values */ +enum mbo_transition_reject_reason { + MBO_TRANSITION_REJECT_REASON_UNSPECIFIED = 0, + MBO_TRANSITION_REJECT_REASON_FRAME_LOSS = 1, + MBO_TRANSITION_REJECT_REASON_DELAY = 2, + MBO_TRANSITION_REJECT_REASON_QOS_CAPACITY = 3, + MBO_TRANSITION_REJECT_REASON_RSSI = 4, + MBO_TRANSITION_REJECT_REASON_INTERFERENCE = 5, + 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_r25, 4.3: MBO ANQP-elements */ +#define MBO_ANQP_OUI_TYPE 0x12 +#define MBO_ANQP_SUBTYPE_CELL_CONN_PREF 1 + /* Wi-Fi Direct (P2P) */ #define P2P_OUI_TYPE 9 @@ -1178,6 +1341,14 @@ enum wifi_display_subelem { #define MESH_PATH_PROTOCOL_VENDOR 255 #define MESH_PATH_METRIC_AIRTIME 1 #define MESH_PATH_METRIC_VENDOR 255 +/* IEEE 802.11s - Mesh Capability */ +#define MESH_CAP_ACCEPT_ADDITIONAL_PEER BIT(0) +#define MESH_CAP_MCCA_SUPPORTED BIT(1) +#define MESH_CAP_MCCA_ENABLED BIT(2) +#define MESH_CAP_FORWARDING BIT(3) +#define MESH_CAP_MBCA_ENABLED BIT(4) +#define MESH_CAP_TBTT_ADJUSTING BIT(5) +#define MESH_CAP_MESH_PS_LEVEL BIT(6) enum plink_action_field { PLINK_OPEN = 1, @@ -1280,14 +1451,25 @@ enum bss_trans_mgmt_status_code { WNM_BSS_TM_REJECT_LEAVING_ESS = 8 }; +/* + * IEEE P802.11-REVmc/D5.0 Table 9-150 - Optional subelement IDs for + * neighbor report + */ #define WNM_NEIGHBOR_TSF 1 #define WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING 2 #define WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE 3 #define WNM_NEIGHBOR_BSS_TERMINATION_DURATION 4 #define WNM_NEIGHBOR_BEARING 5 +#define WNM_NEIGHBOR_WIDE_BW_CHAN 6 +#define WNM_NEIGHBOR_MEASUREMENT_REPORT 39 +#define WNM_NEIGHBOR_HT_CAPAB 45 +#define WNM_NEIGHBOR_HT_OPER 61 +#define WNM_NEIGHBOR_SEC_CHAN_OFFSET 62 #define WNM_NEIGHBOR_MEASUREMENT_PILOT 66 #define WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES 70 #define WNM_NEIGHBOR_MULTIPLE_BSSID 71 +#define WNM_NEIGHBOR_VHT_CAPAB 191 +#define WNM_NEIGHBOR_VHT_OPER 192 /* QoS action */ enum qos_action { @@ -1356,6 +1538,8 @@ struct tpc_report { u8 link_margin; } STRUCT_PACKED; +#define RRM_CAPABILITIES_IE_LEN 5 + /* IEEE Std 802.11-2012, 8.5.7.4 - Link Measurement Request frame format */ struct rrm_link_measurement_request { u8 dialog_token; @@ -1375,8 +1559,6 @@ struct rrm_link_measurement_report { u8 variable[0]; } STRUCT_PACKED; -#define SSID_MAX_LEN 32 - /* IEEE Std 802.11ad-2012 - Multi-band element */ struct multi_band_ie { u8 eid; /* WLAN_EID_MULTI_BAND */ @@ -1433,4 +1615,49 @@ enum fst_action { FST_ACTION_ON_CHANNEL_TUNNEL = 5, }; +/* IEEE Std 802.11ac-2013, Annex C - dot11PHYType */ +enum phy_type { + PHY_TYPE_UNSPECIFIED = 0, + PHY_TYPE_FHSS = 1, + PHY_TYPE_DSSS = 2, + PHY_TYPE_IRBASEBAND = 3, + PHY_TYPE_OFDM = 4, + PHY_TYPE_HRDSSS = 5, + PHY_TYPE_ERP = 6, + PHY_TYPE_HT = 7, + PHY_TYPE_DMG = 8, + PHY_TYPE_VHT = 9, +}; + +/* IEEE P802.11-REVmc/D5.0, 9.4.2.37 - Neighbor Report element */ +/* BSSID Information Field */ +#define NEI_REP_BSSID_INFO_AP_NOT_REACH BIT(0) +#define NEI_REP_BSSID_INFO_AP_UNKNOWN_REACH BIT(1) +#define NEI_REP_BSSID_INFO_AP_REACHABLE (BIT(0) | BIT(1)) +#define NEI_REP_BSSID_INFO_SECURITY BIT(2) +#define NEI_REP_BSSID_INFO_KEY_SCOPE BIT(3) +#define NEI_REP_BSSID_INFO_SPECTRUM_MGMT BIT(4) +#define NEI_REP_BSSID_INFO_QOS BIT(5) +#define NEI_REP_BSSID_INFO_APSD BIT(6) +#define NEI_REP_BSSID_INFO_RM BIT(7) +#define NEI_REP_BSSID_INFO_DELAYED_BA BIT(8) +#define NEI_REP_BSSID_INFO_IMM_BA BIT(9) +#define NEI_REP_BSSID_INFO_MOBILITY_DOMAIN BIT(10) +#define NEI_REP_BSSID_INFO_HT BIT(11) +#define NEI_REP_BSSID_INFO_VHT BIT(12) +#define NEI_REP_BSSID_INFO_FTM BIT(13) + +/* + * IEEE P802.11-REVmc/D5.0 Table 9-152 - HT/VHT Operation Information + * subfields. + * Note: These definitions are not the same as other VHT_CHANWIDTH_*. + */ +enum nr_chan_width { + NR_CHAN_WIDTH_20 = 0, + NR_CHAN_WIDTH_40 = 1, + NR_CHAN_WIDTH_80 = 2, + NR_CHAN_WIDTH_160 = 3, + NR_CHAN_WIDTH_80P80 = 4, +}; + #endif /* IEEE802_11_DEFS_H */ diff --git a/contrib/wpa/src/common/ieee802_1x_defs.h b/contrib/wpa/src/common/ieee802_1x_defs.h index cc88caa8d2f3..a0c1d1bfafc4 100644 --- a/contrib/wpa/src/common/ieee802_1x_defs.h +++ b/contrib/wpa/src/common/ieee802_1x_defs.h @@ -10,7 +10,7 @@ #define IEEE802_1X_DEFS_H #define CS_ID_LEN 8 -#define CS_ID_GCM_AES_128 {0x00, 0x80, 0x02, 0x00, 0x01, 0x00, 0x00, 0x01} +#define CS_ID_GCM_AES_128 0x0080020001000001ULL #define CS_NAME_GCM_AES_128 "GCM-AES-128" enum macsec_policy { diff --git a/contrib/wpa/src/common/qca-vendor.h b/contrib/wpa/src/common/qca-vendor.h index 28985f5194e2..adaec890b58d 100644 --- a/contrib/wpa/src/common/qca-vendor.h +++ b/contrib/wpa/src/common/qca-vendor.h @@ -89,6 +89,102 @@ enum qca_radiotap_vendor_ids { * @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED: Event used by driver, * which supports DFS offloading, to indicate a radar pattern has been * detected. The channel is now unusable. + * + * @QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_START: Command used to + * start the P2P Listen offload function in device and pass the listen + * channel, period, interval, count, device types, and vendor specific + * information elements to the device driver and firmware. + * Uses the attributes defines in + * enum qca_wlan_vendor_attr_p2p_listen_offload. + * + * @QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP: Command/event used to + * indicate stop request/response of the P2P Listen offload function in + * device. As an event, it indicates either the feature stopped after it + * was already running or feature has actually failed to start. Uses the + * attributes defines in enum qca_wlan_vendor_attr_p2p_listen_offload. + * + * @QCA_NL80211_VENDOR_SUBCMD_SAP_CONDITIONAL_CHAN_SWITCH: After AP starts + * beaconing, this sub command provides the driver, the frequencies on the + * 5 GHz band to check for any radar activity. Driver selects one channel + * from this priority list provided through + * @QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_FREQ_LIST and starts + * to check for radar activity on it. If no radar activity is detected + * during the channel availability check period, driver internally switches + * to the selected frequency of operation. If the frequency is zero, driver + * internally selects a channel. The status of this conditional switch is + * indicated through an event using the same sub command through + * @QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_STATUS. Attributes are + * listed in qca_wlan_vendor_attr_sap_conditional_chan_switch. + * + * @QCA_NL80211_VENDOR_SUBCMD_GPIO_CONFIG_COMMAND: Set GPIO pins. This uses the + * attributes defined in enum qca_wlan_gpio_attr. + * + * @QCA_NL80211_VENDOR_SUBCMD_GET_HW_CAPABILITY: Fetch hardware capabilities. + * This uses @QCA_WLAN_VENDOR_ATTR_GET_HW_CAPABILITY to indicate which + * capabilities are to be fetched and other + * enum qca_wlan_vendor_attr_get_hw_capability attributes to return the + * requested capabilities. + * + * @QCA_NL80211_VENDOR_SUBCMD_LL_STATS_EXT: Link layer statistics extension. + * enum qca_wlan_vendor_attr_ll_stats_ext attributes are used with this + * command and event. + * + * @QCA_NL80211_VENDOR_SUBCMD_LOC_GET_CAPA: Get capabilities for + * indoor location features. Capabilities are reported in + * QCA_WLAN_VENDOR_ATTR_LOC_CAPA. + * + * @QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION: Start an FTM + * (fine timing measurement) session with one or more peers. + * Specify Session cookie in QCA_WLAN_VENDOR_ATTR_FTM_SESSION_COOKIE and + * peer information in QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEERS. + * On success, 0 or more QCA_NL80211_VENDOR_SUBCMD_FTM_MEAS_RESULT + * events will be reported, followed by + * QCA_NL80211_VENDOR_SUBCMD_FTM_SESSION_DONE event to indicate + * end of session. + * Refer to IEEE P802.11-REVmc/D7.0, 11.24.6 + * + * @QCA_NL80211_VENDOR_SUBCMD_FTM_ABORT_SESSION: Abort a running session. + * A QCA_NL80211_VENDOR_SUBCMD_FTM_SESSION_DONE will be reported with + * status code indicating session was aborted. + * + * @QCA_NL80211_VENDOR_SUBCMD_FTM_MEAS_RESULT: Event with measurement + * results for one peer. Results are reported in + * QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEER_RESULTS. + * + * @QCA_NL80211_VENDOR_SUBCMD_FTM_SESSION_DONE: Event triggered when + * FTM session is finished, either successfully or aborted by + * request. + * + * @QCA_NL80211_VENDOR_SUBCMD_FTM_CFG_RESPONDER: Configure FTM responder + * mode. QCA_WLAN_VENDOR_ATTR_FTM_RESPONDER_ENABLE specifies whether + * to enable or disable the responder. LCI/LCR reports can be + * configured with QCA_WLAN_VENDOR_ATTR_FTM_LCI and + * QCA_WLAN_VENDOR_ATTR_FTM_LCR. Can be called multiple + * times to update the LCI/LCR reports. + * + * @QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS: Perform a standalone AOA (angle of + * arrival) measurement with a single peer. Specify peer MAC address in + * QCA_WLAN_VENDOR_ATTR_MAC_ADDR and measurement type in + * QCA_WLAN_VENDOR_ATTR_AOA_TYPE. Measurement result is reported in + * QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT event. + * + * @QCA_NL80211_VENDOR_SUBCMD_AOA_ABORT_MEAS: Abort an AOA measurement. Specify + * peer MAC address in QCA_WLAN_VENDOR_ATTR_MAC_ADDR. + * + * @QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT: Event that reports + * the AOA measurement result. + * Peer MAC address reported in QCA_WLAN_VENDOR_ATTR_MAC_ADDR. + * success/failure status is reported in + * QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS. + * Measurement data is reported in QCA_WLAN_VENDOR_ATTR_AOA_MEAS_RESULT. + * The antenna array(s) used in the measurement are reported in + * QCA_WLAN_VENDOR_ATTR_LOC_ANTENNA_ARRAY_MASK. + * + * @QCA_NL80211_VENDOR_SUBCMD_ENCRYPTION_TEST: Encrypt/decrypt the given + * data as per the given parameters. + * + * @QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI: Get antenna RSSI value for a + * specific chain. */ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0, @@ -140,7 +236,11 @@ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED = 58, QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED = 59, QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED = 60, - /* 61-90 - reserved for QCA */ + /* 61-73 - reserved for QCA */ + /* Wi-Fi configuration subcommands */ + QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION = 74, + QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_CONFIGURATION = 75, + /* 76-90 - reserved for QCA */ QCA_NL80211_VENDOR_SUBCMD_DATA_OFFLOAD = 91, QCA_NL80211_VENDOR_SUBCMD_OCB_SET_CONFIG = 92, QCA_NL80211_VENDOR_SUBCMD_OCB_SET_UTC_TIME = 93, @@ -156,6 +256,35 @@ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST = 103, QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL = 104, QCA_NL80211_VENDOR_SUBCMD_SETBAND = 105, + QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN = 106, + QCA_NL80211_VENDOR_SUBCMD_SCAN_DONE = 107, + QCA_NL80211_VENDOR_SUBCMD_OTA_TEST = 108, + QCA_NL80211_VENDOR_SUBCMD_SET_TXPOWER_SCALE = 109, + /* 110..114 - reserved for QCA */ + QCA_NL80211_VENDOR_SUBCMD_SET_TXPOWER_DECR_DB = 115, + /* 116..117 - reserved for QCA */ + QCA_NL80211_VENDOR_SUBCMD_SET_SAP_CONFIG = 118, + QCA_NL80211_VENDOR_SUBCMD_TSF = 119, + QCA_NL80211_VENDOR_SUBCMD_WISA = 120, + /* 121 - reserved for QCA */ + QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_START = 122, + QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP = 123, + QCA_NL80211_VENDOR_SUBCMD_SAP_CONDITIONAL_CHAN_SWITCH = 124, + QCA_NL80211_VENDOR_SUBCMD_GPIO_CONFIG_COMMAND = 125, + QCA_NL80211_VENDOR_SUBCMD_GET_HW_CAPABILITY = 126, + QCA_NL80211_VENDOR_SUBCMD_LL_STATS_EXT = 127, + /* FTM/indoor location subcommands */ + QCA_NL80211_VENDOR_SUBCMD_LOC_GET_CAPA = 128, + QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION = 129, + QCA_NL80211_VENDOR_SUBCMD_FTM_ABORT_SESSION = 130, + QCA_NL80211_VENDOR_SUBCMD_FTM_MEAS_RESULT = 131, + QCA_NL80211_VENDOR_SUBCMD_FTM_SESSION_DONE = 132, + QCA_NL80211_VENDOR_SUBCMD_FTM_CFG_RESPONDER = 133, + QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS = 134, + QCA_NL80211_VENDOR_SUBCMD_AOA_ABORT_MEAS = 135, + QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT = 136, + QCA_NL80211_VENDOR_SUBCMD_ENCRYPTION_TEST = 137, + QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI = 138, }; @@ -185,6 +314,84 @@ enum qca_wlan_vendor_attr { QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_5_0_BAND = 11, /* Unsigned 32-bit value from enum qca_set_band. */ QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE = 12, + /* Dummy (NOP) attribute for 64 bit padding */ + QCA_WLAN_VENDOR_ATTR_PAD = 13, + /* Unique FTM session cookie (Unsigned 64 bit). Specified in + * QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION. Reported in + * the session in QCA_NL80211_VENDOR_SUBCMD_FTM_MEAS_RESULT and + * QCA_NL80211_VENDOR_SUBCMD_FTM_SESSION_DONE. + */ + QCA_WLAN_VENDOR_ATTR_FTM_SESSION_COOKIE = 14, + /* Indoor location capabilities, returned by + * QCA_NL80211_VENDOR_SUBCMD_LOC_GET_CAPA. + * see enum qca_wlan_vendor_attr_loc_capa. + */ + QCA_WLAN_VENDOR_ATTR_LOC_CAPA = 15, + /* Array of nested attributes containing information about each peer + * in FTM measurement session. See enum qca_wlan_vendor_attr_peer_info + * for supported attributes for each peer. + */ + QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEERS = 16, + /* Array of nested attributes containing measurement results for + * one or more peers, reported by the + * QCA_NL80211_VENDOR_SUBCMD_FTM_MEAS_RESULT event. + * See enum qca_wlan_vendor_attr_peer_result for list of supported + * attributes. + */ + QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEER_RESULTS = 17, + /* Flag attribute for enabling or disabling responder functionality. */ + QCA_WLAN_VENDOR_ATTR_FTM_RESPONDER_ENABLE = 18, + /* Used in the QCA_NL80211_VENDOR_SUBCMD_FTM_CFG_RESPONDER + * command to specify the LCI report that will be sent by + * the responder during a measurement exchange. The format is + * defined in IEEE P802.11-REVmc/D7.0, 9.4.2.22.10. + */ + QCA_WLAN_VENDOR_ATTR_FTM_LCI = 19, + /* Used in the QCA_NL80211_VENDOR_SUBCMD_FTM_CFG_RESPONDER + * command to specify the location civic report that will + * be sent by the responder during a measurement exchange. + * The format is defined in IEEE P802.11-REVmc/D7.0, 9.4.2.22.13. + */ + QCA_WLAN_VENDOR_ATTR_FTM_LCR = 20, + /* Session/measurement completion status code, + * reported in QCA_NL80211_VENDOR_SUBCMD_FTM_SESSION_DONE and + * QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT + * see enum qca_vendor_attr_loc_session_status. + */ + QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS = 21, + /* Initial dialog token used by responder (0 if not specified), + * unsigned 8 bit value. + */ + QCA_WLAN_VENDOR_ATTR_FTM_INITIAL_TOKEN = 22, + /* AOA measurement type. Requested in QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS + * and optionally in QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION if + * AOA measurements are needed as part of an FTM session. + * Reported by QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT. See + * enum qca_wlan_vendor_attr_aoa_type. + */ + QCA_WLAN_VENDOR_ATTR_AOA_TYPE = 23, + /* A bit mask (unsigned 32 bit value) of antenna arrays used + * by indoor location measurements. Refers to the antenna + * arrays described by QCA_VENDOR_ATTR_LOC_CAPA_ANTENNA_ARRAYS. + */ + QCA_WLAN_VENDOR_ATTR_LOC_ANTENNA_ARRAY_MASK = 24, + /* AOA measurement data. Its contents depends on the AOA measurement + * type and antenna array mask: + * QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE: array of U16 values, + * phase of the strongest CIR path for each antenna in the measured + * array(s). + * QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE_AMP: array of 2 U16 + * values, phase and amplitude of the strongest CIR path for each + * antenna in the measured array(s). + */ + QCA_WLAN_VENDOR_ATTR_AOA_MEAS_RESULT = 25, + /* Used in QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command + * to specify the chain number (unsigned 32 bit value) to inquire + * the corresponding antenna RSSI value */ + QCA_WLAN_VENDOR_ATTR_CHAIN_INDEX = 26, + /* Used in QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command + * to report the specific antenna RSSI value (unsigned 32 bit value) */ + QCA_WLAN_VENDOR_ATTR_CHAIN_RSSI = 27, /* keep last */ QCA_WLAN_VENDOR_ATTR_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_MAX = QCA_WLAN_VENDOR_ATTR_AFTER_LAST - 1, @@ -205,12 +412,50 @@ enum qca_wlan_vendor_attr_roam_auth { QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK, + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS, /* keep last */ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX = QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST - 1 }; +enum qca_wlan_vendor_attr_p2p_listen_offload { + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_INVALID = 0, + /* A 32-bit unsigned value; the P2P listen frequency (MHz); must be one + * of the social channels. + */ + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CHANNEL, + /* A 32-bit unsigned value; the P2P listen offload period (ms). + */ + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_PERIOD, + /* A 32-bit unsigned value; the P2P listen interval duration (ms). + */ + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_INTERVAL, + /* A 32-bit unsigned value; number of interval times the firmware needs + * to run the offloaded P2P listen operation before it stops. + */ + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_COUNT, + /* An array of arbitrary binary data with one or more 8-byte values. + * The device types include both primary and secondary device types. + */ + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_DEVICE_TYPES, + /* An array of unsigned 8-bit characters; vendor information elements. + */ + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_VENDOR_IE, + /* A 32-bit unsigned value; a control flag to indicate whether listen + * results need to be flushed to wpa_supplicant. + */ + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CTRL_FLAG, + /* A 8-bit unsigned value; reason code for P2P listen offload stop + * event. + */ + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_STOP_REASON, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_MAX = + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_AFTER_LAST - 1 +}; + enum qca_wlan_vendor_attr_acs_offload { QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_INVALID = 0, QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL, @@ -247,11 +492,21 @@ enum qca_wlan_vendor_acs_hw_mode { * after roaming, rather than having the user space wpa_supplicant do it. * @QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY: Device supports automatic * band selection based on channel selection results. + * @QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS: Device supports + * simultaneous off-channel operations. + * @QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD: Device supports P2P + * Listen offload; a mechanism where the station's firmware takes care of + * responding to incoming Probe Request frames received from other P2P + * Devices whilst in Listen state, rather than having the user space + * wpa_supplicant do it. Information from received P2P requests are + * forwarded from firmware to host whenever the host processor wakes up. * @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits */ enum qca_wlan_vendor_features { QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD = 0, QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY = 1, + QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS = 2, + QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD = 3, NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */ }; @@ -324,6 +579,67 @@ enum qca_set_band { QCA_SETBAND_2G, }; +/** + * enum qca_access_policy - Access control policy + * + * Access control policy is applied on the configured IE + * (QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE). + * To be set with QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY. + * + * @QCA_ACCESS_POLICY_ACCEPT_UNLESS_LISTED: Deny Wi-Fi connections which match + * the specific configuration (IE) set, i.e., allow all the + * connections which do not match the configuration. + * @QCA_ACCESS_POLICY_DENY_UNLESS_LISTED: Accept Wi-Fi connections which match + * the specific configuration (IE) set, i.e., deny all the + * connections which do not match the configuration. + */ +enum qca_access_policy { + QCA_ACCESS_POLICY_ACCEPT_UNLESS_LISTED, + QCA_ACCESS_POLICY_DENY_UNLESS_LISTED, +}; + +/** + * enum qca_vendor_attr_get_tsf: Vendor attributes for TSF capture + * @QCA_WLAN_VENDOR_ATTR_TSF_CMD: enum qca_tsf_operation (u32) + * @QCA_WLAN_VENDOR_ATTR_TSF_TIMER_VALUE: Unsigned 64 bit TSF timer value + * @QCA_WLAN_VENDOR_ATTR_TSF_SOC_TIMER_VALUE: Unsigned 64 bit Synchronized + * SOC timer value at TSF capture + */ +enum qca_vendor_attr_tsf_cmd { + QCA_WLAN_VENDOR_ATTR_TSF_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_TSF_CMD, + QCA_WLAN_VENDOR_ATTR_TSF_TIMER_VALUE, + QCA_WLAN_VENDOR_ATTR_TSF_SOC_TIMER_VALUE, + QCA_WLAN_VENDOR_ATTR_TSF_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_TSF_MAX = + QCA_WLAN_VENDOR_ATTR_TSF_AFTER_LAST - 1 +}; + +/** + * enum qca_tsf_operation: TSF driver commands + * @QCA_TSF_CAPTURE: Initiate TSF Capture + * @QCA_TSF_GET: Get TSF capture value + * @QCA_TSF_SYNC_GET: Initiate TSF capture and return with captured value + */ +enum qca_tsf_cmd { + QCA_TSF_CAPTURE, + QCA_TSF_GET, + QCA_TSF_SYNC_GET, +}; + +/** + * enum qca_vendor_attr_wisa_cmd + * @QCA_WLAN_VENDOR_ATTR_WISA_MODE: WISA mode value (u32) + * WISA setup vendor commands + */ +enum qca_vendor_attr_wisa_cmd { + QCA_WLAN_VENDOR_ATTR_WISA_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_WISA_MODE, + QCA_WLAN_VENDOR_ATTR_WISA_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_WISA_MAX = + QCA_WLAN_VENDOR_ATTR_WISA_AFTER_LAST - 1 +}; + /* IEEE 802.11 Vendor Specific elements */ /** @@ -349,9 +665,926 @@ enum qca_set_band { * * This vendor element may be included in GO Negotiation Request, P2P * Invitation Request, and Provision Discovery Request frames. + * + * @QCA_VENDOR_ELEM_HE_CAPAB: HE Capabilities element. + * This element can be used for pre-standard publication testing of HE + * before P802.11ax draft assigns the element ID. The payload of this + * vendor specific element is defined by the latest P802.11ax draft. + * Please note that the draft is still work in progress and this element + * payload is subject to change. + * + * @QCA_VENDOR_ELEM_HE_OPER: HE Operation element. + * This element can be used for pre-standard publication testing of HE + * before P802.11ax draft assigns the element ID. The payload of this + * vendor specific element is defined by the latest P802.11ax draft. + * Please note that the draft is still work in progress and this element + * payload is subject to change. */ enum qca_vendor_element_id { QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST = 0, + QCA_VENDOR_ELEM_HE_CAPAB = 1, + QCA_VENDOR_ELEM_HE_OPER = 2, +}; + +/** + * enum qca_wlan_vendor_attr_scan - Specifies vendor scan attributes + * + * @QCA_WLAN_VENDOR_ATTR_SCAN_IE: IEs that should be included as part of scan + * @QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES: Nested unsigned 32-bit attributes + * with frequencies to be scanned (in MHz) + * @QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS: Nested attribute with SSIDs to be scanned + * @QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES: Nested array attribute of supported + * rates to be included + * @QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE: flag used to send probe requests + * at non CCK rate in 2GHz band + * @QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS: Unsigned 32-bit scan flags + * @QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE: Unsigned 64-bit cookie provided by the + * driver for the specific scan request + * @QCA_WLAN_VENDOR_ATTR_SCAN_STATUS: Unsigned 8-bit status of the scan + * request decoded as in enum scan_status + * @QCA_WLAN_VENDOR_ATTR_SCAN_MAC: 6-byte MAC address to use when randomisation + * scan flag is set + * @QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK: 6-byte MAC address mask to be used with + * randomisation + */ +enum qca_wlan_vendor_attr_scan { + QCA_WLAN_VENDOR_ATTR_SCAN_INVALID_PARAM = 0, + QCA_WLAN_VENDOR_ATTR_SCAN_IE, + QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES, + QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS, + QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES, + QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE, + QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS, + QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE, + QCA_WLAN_VENDOR_ATTR_SCAN_STATUS, + QCA_WLAN_VENDOR_ATTR_SCAN_MAC, + QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK, + QCA_WLAN_VENDOR_ATTR_SCAN_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_SCAN_MAX = + QCA_WLAN_VENDOR_ATTR_SCAN_AFTER_LAST - 1 +}; + +/** + * enum scan_status - Specifies the valid values the vendor scan attribute + * QCA_WLAN_VENDOR_ATTR_SCAN_STATUS can take + * + * @VENDOR_SCAN_STATUS_NEW_RESULTS: implies the vendor scan is successful with + * new scan results + * @VENDOR_SCAN_STATUS_ABORTED: implies the vendor scan was aborted in-between + */ +enum scan_status { + VENDOR_SCAN_STATUS_NEW_RESULTS, + VENDOR_SCAN_STATUS_ABORTED, + VENDOR_SCAN_STATUS_MAX, +}; + +/** + * enum qca_vendor_attr_ota_test - Specifies the values for vendor + * command QCA_NL80211_VENDOR_SUBCMD_OTA_TEST + * @QCA_WLAN_VENDOR_ATTR_OTA_TEST_ENABLE: enable ota test + */ +enum qca_vendor_attr_ota_test { + QCA_WLAN_VENDOR_ATTR_OTA_TEST_INVALID, + /* 8-bit unsigned value to indicate if OTA test is enabled */ + QCA_WLAN_VENDOR_ATTR_OTA_TEST_ENABLE, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_OTA_TEST_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_OTA_TEST_MAX = + QCA_WLAN_VENDOR_ATTR_OTA_TEST_AFTER_LAST - 1 +}; + +/** + * enum qca_vendor_attr_txpower_scale - vendor sub commands index + * + * @QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE: scaling value + */ +enum qca_vendor_attr_txpower_scale { + QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_INVALID, + /* 8-bit unsigned value to indicate the scaling of tx power */ + QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_MAX = + QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_AFTER_LAST - 1 +}; + +/** + * enum qca_vendor_attr_txpower_decr_db - Attributes for TX power decrease + * + * These attributes are used with QCA_NL80211_VENDOR_SUBCMD_SET_TXPOWER_DECR_DB. + */ +enum qca_vendor_attr_txpower_decr_db { + QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_INVALID, + /* 8-bit unsigned value to indicate the reduction of TX power in dB for + * a virtual interface. */ + QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_MAX = + QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_AFTER_LAST - 1 +}; + +/* Attributes for data used by + * QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION and + * QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_CONFIGURATION subcommands. + */ +enum qca_wlan_vendor_attr_config { + QCA_WLAN_VENDOR_ATTR_CONFIG_INVALID = 0, + /* Unsigned 32-bit value to set the DTIM period. + * Whether the wifi chipset wakes at every dtim beacon or a multiple of + * the DTIM period. If DTIM is set to 3, the STA shall wake up every 3 + * DTIM beacons. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_DYNAMIC_DTIM = 1, + /* Unsigned 32-bit value to set the wifi_iface stats averaging factor + * used to calculate statistics like average the TSF offset or average + * number of frame leaked. + * For instance, upon Beacon frame reception: + * current_avg = ((beacon_TSF - TBTT) * factor + previous_avg * (0x10000 - factor) ) / 0x10000 + * For instance, when evaluating leaky APs: + * current_avg = ((num frame received within guard time) * factor + previous_avg * (0x10000 - factor)) / 0x10000 + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_STATS_AVG_FACTOR = 2, + /* Unsigned 32-bit value to configure guard time, i.e., when + * implementing IEEE power management based on frame control PM bit, how + * long the driver waits before shutting down the radio and after + * receiving an ACK frame for a Data frame with PM bit set. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_GUARD_TIME = 3, + /* Unsigned 32-bit value to change the FTM capability dynamically */ + QCA_WLAN_VENDOR_ATTR_CONFIG_FINE_TIME_MEASUREMENT = 4, + /* Unsigned 16-bit value to configure maximum TX rate dynamically */ + QCA_WLAN_VENDOR_ATTR_CONF_TX_RATE = 5, + /* Unsigned 32-bit value to configure the number of continuous + * Beacon Miss which shall be used by the firmware to penalize + * the RSSI. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_PENALIZE_AFTER_NCONS_BEACON_MISS = 6, + /* Unsigned 8-bit value to configure the channel avoidance indication + * behavior. Firmware to send only one indication and ignore duplicate + * indications when set to avoid multiple Apps wakeups. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_CHANNEL_AVOIDANCE_IND = 7, + /* 8-bit unsigned value to configure the maximum TX MPDU for + * aggregation. */ + QCA_WLAN_VENDOR_ATTR_CONFIG_TX_MPDU_AGGREGATION = 8, + /* 8-bit unsigned value to configure the maximum RX MPDU for + * aggregation. */ + QCA_WLAN_VENDOR_ATTR_CONFIG_RX_MPDU_AGGREGATION = 9, + /* 8-bit unsigned value to configure the Non aggregrate/11g sw + * retry threshold (0 disable, 31 max). */ + QCA_WLAN_VENDOR_ATTR_CONFIG_NON_AGG_RETRY = 10, + /* 8-bit unsigned value to configure the aggregrate sw + * retry threshold (0 disable, 31 max). */ + QCA_WLAN_VENDOR_ATTR_CONFIG_AGG_RETRY = 11, + /* 8-bit unsigned value to configure the MGMT frame + * retry threshold (0 disable, 31 max). */ + QCA_WLAN_VENDOR_ATTR_CONFIG_MGMT_RETRY = 12, + /* 8-bit unsigned value to configure the CTRL frame + * retry threshold (0 disable, 31 max). */ + QCA_WLAN_VENDOR_ATTR_CONFIG_CTRL_RETRY = 13, + /* 8-bit unsigned value to configure the propagation delay for + * 2G/5G band (0~63, units in us) */ + QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_DELAY = 14, + /* Unsigned 32-bit value to configure the number of unicast TX fail + * packet count. The peer is disconnected once this threshold is + * reached. */ + QCA_WLAN_VENDOR_ATTR_CONFIG_TX_FAIL_COUNT = 15, + /* Attribute used to set scan default IEs to the driver. + * + * These IEs can be used by scan operations that will be initiated by + * the driver/firmware. + * + * For further scan requests coming to the driver, these IEs should be + * merged with the IEs received along with scan request coming to the + * driver. If a particular IE is present in the scan default IEs but not + * present in the scan request, then that IE should be added to the IEs + * sent in the Probe Request frames for that scan request. */ + QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_DEFAULT_IES = 16, + /* Unsigned 32-bit attribute for generic commands */ + QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_COMMAND = 17, + /* Unsigned 32-bit value attribute for generic commands */ + QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_VALUE = 18, + /* Unsigned 32-bit data attribute for generic command response */ + QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA = 19, + /* Unsigned 32-bit length attribute for + * QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA */ + QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_LENGTH = 20, + /* Unsigned 32-bit flags attribute for + * QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA */ + QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_FLAGS = 21, + /* Unsigned 32-bit, defining the access policy. + * See enum qca_access_policy. Used with + * QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE_LIST. */ + QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY = 22, + /* Sets the list of full set of IEs for which a specific access policy + * has to be applied. Used along with + * QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY to control the access. + * Zero length payload can be used to clear this access constraint. */ + QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE_LIST = 23, + /* Unsigned 32-bit, specifies the interface index (netdev) for which the + * corresponding configurations are applied. If the interface index is + * not specified, the configurations are attributed to the respective + * wiphy. */ + QCA_WLAN_VENDOR_ATTR_CONFIG_IFINDEX = 24, + /* 8-bit unsigned value to trigger QPower: 1-Enable, 0-Disable */ + QCA_WLAN_VENDOR_ATTR_CONFIG_QPOWER = 25, + /* 8-bit unsigned value to configure the driver and below layers to + * ignore the assoc disallowed set by APs while connecting + * 1-Ignore, 0-Don't ignore */ + QCA_WLAN_VENDOR_ATTR_CONFIG_IGNORE_ASSOC_DISALLOWED = 26, + /* 32-bit unsigned value to trigger antenna diversity features: + * 1-Enable, 0-Disable */ + QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ENA = 27, + /* 32-bit unsigned value to configure specific chain antenna */ + QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_CHAIN = 28, + /* 32-bit unsigned value to trigger cycle selftest + * 1-Enable, 0-Disable */ + QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST = 29, + /* 32-bit unsigned to configure the cycle time of selftest + * the unit is micro-second */ + QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST_INTVL = 30, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_CONFIG_MAX = + QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_sap_config - Parameters for AP configuration + */ +enum qca_wlan_vendor_attr_sap_config { + QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_INVALID = 0, + /* 1 - reserved for QCA */ + /* List of frequencies on which AP is expected to operate. + * This is irrespective of ACS configuration. This list is a priority + * based one and is looked for before the AP is created to ensure the + * best concurrency sessions (avoid MCC and use DBS/SCC) co-exist in + * the system. + */ + QCA_WLAN_VENDOR_ATTR_SAP_MANDATORY_FREQUENCY_LIST = 2, + + QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_MAX = + QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_sap_conditional_chan_switch - Parameters for AP + * conditional channel switch + */ +enum qca_wlan_vendor_attr_sap_conditional_chan_switch { + QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_INVALID = 0, + /* Priority based frequency list (an array of u32 values in host byte + * order) */ + QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_FREQ_LIST = 1, + /* Status of the conditional switch (u32). + * 0: Success, Non-zero: Failure + */ + QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_STATUS = 2, + + QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_MAX = + QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_gpio_attr - Parameters for GPIO configuration + */ +enum qca_wlan_gpio_attr { + QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INVALID = 0, + /* Unsigned 32-bit attribute for GPIO command */ + QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND, + /* Unsigned 32-bit attribute for GPIO PIN number to configure */ + QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PINNUM, + /* Unsigned 32-bit attribute for GPIO value to configure */ + QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_VALUE, + /* Unsigned 32-bit attribute for GPIO pull type */ + QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PULL_TYPE, + /* Unsigned 32-bit attribute for GPIO interrupt mode */ + QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INTR_MODE, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_LAST, + QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_MAX = + QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_LAST - 1 +}; + +/** + * enum qca_wlan_vendor_attr_get_hw_capability - Wi-Fi hardware capability + */ +enum qca_wlan_vendor_attr_get_hw_capability { + QCA_WLAN_VENDOR_ATTR_HW_CAPABILITY_INVALID, + /* Antenna isolation + * An attribute used in the response. + * The content of this attribute is encoded in a byte array. Each byte + * value is an antenna isolation value. The array length is the number + * of antennas. + */ + QCA_WLAN_VENDOR_ATTR_ANTENNA_ISOLATION, + /* Request HW capability + * An attribute used in the request. + * The content of this attribute is a u32 array for one or more of + * hardware capabilities (attribute IDs) that are being requested. Each + * u32 value has a value from this + * enum qca_wlan_vendor_attr_get_hw_capability + * identifying which capabilities are requested. + */ + QCA_WLAN_VENDOR_ATTR_GET_HW_CAPABILITY, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_HW_CAPABILITY_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_HW_CAPABILITY_MAX = + QCA_WLAN_VENDOR_ATTR_HW_CAPABILITY_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_ll_stats_ext - Attributes for MAC layer monitoring + * offload which is an extension for LL_STATS. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_PERIOD: Monitoring period. Unit in ms. + * If MAC counters do not exceed the threshold, FW will report monitored + * link layer counters periodically as this setting. The first report is + * always triggered by this timer. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_THRESHOLD: It is a percentage (1-99). + * For each MAC layer counter, FW holds two copies. One is the current value. + * The other is the last report. Once a current counter's increment is larger + * than the threshold, FW will indicate that counter to host even if the + * monitoring timer does not expire. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_CHG: Peer STA power state change + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TID: TID of MSDU + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_NUM_MSDU: Count of MSDU with the same + * failure code. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_STATUS: TX failure code + * 1: TX packet discarded + * 2: No ACK + * 3: Postpone + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_MAC_ADDRESS: peer MAC address + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_STATE: Peer STA current state + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_GLOBAL: Global threshold. + * Threshold for all monitored parameters. If per counter dedicated threshold + * is not enabled, this threshold will take effect. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_EVENT_MODE: Indicate what triggers this + * event, PERORID_TIMEOUT == 1, THRESH_EXCEED == 0. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_ID: interface ID + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_ID: peer ID + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BITMAP: bitmap for TX counters + * Bit0: TX counter unit in MSDU + * Bit1: TX counter unit in MPDU + * Bit2: TX counter unit in PPDU + * Bit3: TX counter unit in byte + * Bit4: Dropped MSDUs + * Bit5: Dropped Bytes + * Bit6: MPDU retry counter + * Bit7: MPDU failure counter + * Bit8: PPDU failure counter + * Bit9: MPDU aggregation counter + * Bit10: MCS counter for ACKed MPDUs + * Bit11: MCS counter for Failed MPDUs + * Bit12: TX Delay counter + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BITMAP: bitmap for RX counters + * Bit0: MAC RX counter unit in MPDU + * Bit1: MAC RX counter unit in byte + * Bit2: PHY RX counter unit in PPDU + * Bit3: PHY RX counter unit in byte + * Bit4: Disorder counter + * Bit5: Retry counter + * Bit6: Duplication counter + * Bit7: Discard counter + * Bit8: MPDU aggregation size counter + * Bit9: MCS counter + * Bit10: Peer STA power state change (wake to sleep) counter + * Bit11: Peer STA power save counter, total time in PS mode + * Bit12: Probe request counter + * Bit13: Other management frames counter + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CCA_BSS_BITMAP: bitmap for CCA + * Bit0: Idle time + * Bit1: TX time + * Bit2: time RX in current bss + * Bit3: Out of current bss time + * Bit4: Wireless medium busy time + * Bit5: RX in bad condition time + * Bit6: TX in bad condition time + * Bit7: time wlan card not available + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_SIGNAL_BITMAP: bitmap for signal + * Bit0: Per channel SNR counter + * Bit1: Per channel noise floor counter + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_NUM: number of peers + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CHANNEL_NUM: number of channels + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_AC_RX_NUM: number of RX stats + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CCA_BSS: per channel BSS CCA stats + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER: container for per PEER stats + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MSDU: Number of total TX MSDUs + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MPDU: Number of total TX MPDUs + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_PPDU: Number of total TX PPDUs + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BYTES: bytes of TX data + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP: Number of dropped TX packets + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP_BYTES: Bytes dropped + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_RETRY: waiting time without an ACK + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_ACK: number of MPDU not-ACKed + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_BACK: number of PPDU not-ACKed + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_AGGR_NUM: + * aggregation stats buffer length + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_SUCC_MCS_NUM: length of mcs stats + * buffer for ACKed MPDUs. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_FAIL_MCS_NUM: length of mcs stats + * buffer for failed MPDUs. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_DELAY_ARRAY_SIZE: + * length of delay stats array. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_AGGR: TX aggregation stats + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_SUCC_MCS: MCS stats for ACKed MPDUs + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_FAIL_MCS: MCS stats for failed MPDUs + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DELAY: tx delay stats + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU: MPDUs received + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_BYTES: bytes received + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU: PPDU received + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU_BYTES: PPDU bytes received + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_LOST: packets lost + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_RETRY: number of RX packets + * flagged as retransmissions + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DUP: number of RX packets + * flagged as duplicated + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DISCARD: number of RX + * packets discarded + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_AGGR_NUM: length of RX aggregation + * stats buffer. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MCS_NUM: length of RX mcs + * stats buffer. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MCS: RX mcs stats buffer + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_AGGR: aggregation stats buffer + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_TIMES: times STAs go to sleep + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_DURATION: STAs' total sleep time + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PROBE_REQ: number of probe + * requests received + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MGMT: number of other mgmt + * frames received + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IDLE_TIME: Percentage of idle time + * there is no TX, nor RX, nor interference. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_TIME: percentage of time + * transmitting packets. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_TIME: percentage of time + * for receiving. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BUSY: percentage of time + * interference detected. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BAD: percentage of time + * receiving packets with errors. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BAD: percentage of time + * TX no-ACK. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_NO_AVAIL: percentage of time + * the chip is unable to work in normal conditions. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IN_BSS_TIME: percentage of time + * receiving packets in current BSS. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_OUT_BSS_TIME: percentage of time + * receiving packets not in current BSS. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_ANT_NUM: number of antennas + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_SIGNAL: + * This is a container for per antenna signal stats. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_SNR: per antenna SNR value + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_NF: per antenna NF value + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_RSSI_BEACON: RSSI of beacon + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_SNR_BEACON: SNR of beacon + */ +enum qca_wlan_vendor_attr_ll_stats_ext { + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_INVALID = 0, + + /* Attributes for configurations */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_PERIOD, + QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_THRESHOLD, + + /* Peer STA power state change */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_CHG, + + /* TX failure event */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TID, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_NUM_MSDU, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_STATUS, + + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_STATE, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_MAC_ADDRESS, + + /* MAC counters */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_GLOBAL, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_EVENT_MODE, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_ID, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_ID, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BITMAP, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BITMAP, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CCA_BSS_BITMAP, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_SIGNAL_BITMAP, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_NUM, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CHANNEL_NUM, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CCA_BSS, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER, + + /* Sub-attributes for PEER_AC_TX */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MSDU, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MPDU, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_PPDU, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BYTES, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP_BYTES, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_RETRY, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_ACK, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_BACK, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_AGGR_NUM, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_SUCC_MCS_NUM, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_FAIL_MCS_NUM, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_AGGR, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_SUCC_MCS, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_FAIL_MCS, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_DELAY_ARRAY_SIZE, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DELAY, + + /* Sub-attributes for PEER_AC_RX */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_BYTES, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU_BYTES, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_LOST, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_RETRY, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DUP, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DISCARD, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_AGGR_NUM, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MCS_NUM, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MCS, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_AGGR, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_TIMES, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_DURATION, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PROBE_REQ, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MGMT, + + /* Sub-attributes for CCA_BSS */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IDLE_TIME, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_TIME, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_TIME, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BUSY, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BAD, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BAD, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_NO_AVAIL, + + /* sub-attribute for BSS_RX_TIME */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IN_BSS_TIME, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_OUT_BSS_TIME, + + /* Sub-attributes for PEER_SIGNAL */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_ANT_NUM, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_SIGNAL, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_SNR, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_NF, + + /* Sub-attributes for IFACE_BSS */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_RSSI_BEACON, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_SNR_BEACON, + + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_LAST, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_MAX = + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_LAST - 1 +}; + +/* Attributes for FTM commands and events */ + +/** + * enum qca_wlan_vendor_attr_loc_capa - Indoor location capabilities + * + * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAGS: Various flags. See + * enum qca_wlan_vendor_attr_loc_capa_flags. + * @QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_SESSIONS: Maximum number + * of measurement sessions that can run concurrently. + * Default is one session (no session concurrency). + * @QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_PEERS: The total number of unique + * peers that are supported in running sessions. For example, + * if the value is 8 and maximum number of sessions is 2, you can + * have one session with 8 unique peers, or 2 sessions with 4 unique + * peers each, and so on. + * @QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_BURSTS_EXP: Maximum number + * of bursts per peer, as an exponent (2^value). Default is 0, + * meaning no multi-burst support. + * @QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_MEAS_PER_BURST: Maximum number + * of measurement exchanges allowed in a single burst. + * @QCA_WLAN_VENDOR_ATTR_AOA_CAPA_SUPPORTED_TYPES: Supported AOA measurement + * types. A bit mask (unsigned 32 bit value), each bit corresponds + * to an AOA type as defined by enum qca_vendor_attr_aoa_type. + */ +enum qca_wlan_vendor_attr_loc_capa { + QCA_WLAN_VENDOR_ATTR_LOC_CAPA_INVALID, + QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAGS, + QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_SESSIONS, + QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_PEERS, + QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_BURSTS_EXP, + QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_MEAS_PER_BURST, + QCA_WLAN_VENDOR_ATTR_AOA_CAPA_SUPPORTED_TYPES, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_LOC_CAPA_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_LOC_CAPA_MAX = + QCA_WLAN_VENDOR_ATTR_LOC_CAPA_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_loc_capa_flags: Indoor location capability flags + * + * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_FTM_RESPONDER: Set if driver + * can be configured as an FTM responder (for example, an AP that + * services FTM requests). QCA_NL80211_VENDOR_SUBCMD_FTM_CFG_RESPONDER + * will be supported if set. + * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_FTM_INITIATOR: Set if driver + * can run FTM sessions. QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION + * will be supported if set. +* @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_ASAP: Set if FTM responder + * supports immediate (ASAP) response. + * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_AOA: Set if driver supports standalone + * AOA measurement using QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS. + * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_AOA_IN_FTM: Set if driver supports + * requesting AOA measurements as part of an FTM session. + */ +enum qca_wlan_vendor_attr_loc_capa_flags { + QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_FTM_RESPONDER = 1 << 0, + QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_FTM_INITIATOR = 1 << 1, + QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_ASAP = 1 << 2, + QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_AOA = 1 << 3, + QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_AOA_IN_FTM = 1 << 4, +}; + +/** + * enum qca_wlan_vendor_attr_ftm_peer_info: Information about + * a single peer in a measurement session. + * + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MAC_ADDR: The MAC address of the peer. + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAGS: Various flags related + * to measurement. See enum qca_wlan_vendor_attr_ftm_peer_meas_flags. + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_PARAMS: Nested attribute of + * FTM measurement parameters, as specified by IEEE P802.11-REVmc/D7.0 + * 9.4.2.167. See enum qca_wlan_vendor_attr_ftm_meas_param for + * list of supported attributes. + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID: Initial token ID for + * secure measurement. + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_AOA_BURST_PERIOD: Request AOA + * measurement every bursts. If 0 or not specified, + * AOA measurements will be disabled for this peer. + */ +enum qca_wlan_vendor_attr_ftm_peer_info { + QCA_WLAN_VENDOR_ATTR_FTM_PEER_INVALID, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_MAC_ADDR, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAGS, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_PARAMS, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_AOA_BURST_PERIOD, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_FTM_PEER_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_MAX = + QCA_WLAN_VENDOR_ATTR_FTM_PEER_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_ftm_peer_meas_flags: Measurement request flags, + * per-peer + * + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_ASAP: If set, request + * immediate (ASAP) response from peer. + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_LCI: If set, request + * LCI report from peer. The LCI report includes the absolute + * location of the peer in "official" coordinates (similar to GPS). + * See IEEE P802.11-REVmc/D7.0, 11.24.6.7 for more information. + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_LCR: If set, request + * Location civic report from peer. The LCR includes the location + * of the peer in free-form format. See IEEE P802.11-REVmc/D7.0, + * 11.24.6.7 for more information. + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_SECURE: If set, + * request a secure measurement. + * QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID must also be provided. + */ +enum qca_wlan_vendor_attr_ftm_peer_meas_flags { + QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_ASAP = 1 << 0, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_LCI = 1 << 1, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_LCR = 1 << 2, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_SECURE = 1 << 3, +}; + +/** + * enum qca_wlan_vendor_attr_ftm_meas_param: Measurement parameters + * + * @QCA_WLAN_VENDOR_ATTR_FTM_PARAM_MEAS_PER_BURST: Number of measurements + * to perform in a single burst. + * @QCA_WLAN_VENDOR_ATTR_FTM_PARAM_NUM_BURSTS_EXP: Number of bursts to + * perform, specified as an exponent (2^value). + * @QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_DURATION: Duration of burst + * instance, as specified in IEEE P802.11-REVmc/D7.0, 9.4.2.167. + * @QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_PERIOD: Time between bursts, + * as specified in IEEE P802.11-REVmc/D7.0, 9.4.2.167. Must + * be larger than QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_DURATION. + */ +enum qca_wlan_vendor_attr_ftm_meas_param { + QCA_WLAN_VENDOR_ATTR_FTM_PARAM_INVALID, + QCA_WLAN_VENDOR_ATTR_FTM_PARAM_MEAS_PER_BURST, + QCA_WLAN_VENDOR_ATTR_FTM_PARAM_NUM_BURSTS_EXP, + QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_DURATION, + QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_PERIOD, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_FTM_PARAM_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_FTM_PARAM_MAX = + QCA_WLAN_VENDOR_ATTR_FTM_PARAM_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_ftm_peer_result: Per-peer results + * + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MAC_ADDR: MAC address of the reported + * peer. + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS: Status of measurement + * request for this peer. + * See enum qca_wlan_vendor_attr_ftm_peer_result_status. + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_FLAGS: Various flags related + * to measurement results for this peer. + * See enum qca_wlan_vendor_attr_ftm_peer_result_flags. + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_VALUE_SECONDS: Specified when + * request failed and peer requested not to send an additional request + * for this number of seconds. + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_LCI: LCI report when received + * from peer. In the format specified by IEEE P802.11-REVmc/D7.0, + * 9.4.2.22.10. + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_LCR: Location civic report when + * received from peer. In the format specified by IEEE P802.11-REVmc/D7.0, + * 9.4.2.22.13. + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MEAS_PARAMS: Reported when peer + * overridden some measurement request parameters. See + * enum qca_wlan_vendor_attr_ftm_meas_param. + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_AOA_MEAS: AOA measurement + * for this peer. Same contents as @QCA_WLAN_VENDOR_ATTR_AOA_MEAS_RESULT. + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MEAS: Array of measurement + * results. Each entry is a nested attribute defined + * by enum qca_wlan_vendor_attr_ftm_meas. + */ +enum qca_wlan_vendor_attr_ftm_peer_result { + QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_INVALID, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MAC_ADDR, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_FLAGS, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_VALUE_SECONDS, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_LCI, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_LCR, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MEAS_PARAMS, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_AOA_MEAS, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MEAS, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MAX = + QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_ftm_peer_result_status + * + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_OK: Request sent ok and results + * will be provided. Peer may have overridden some measurement parameters, + * in which case overridden parameters will be report by + * QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MEAS_PARAM attribute. + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_INCAPABLE: Peer is incapable + * of performing the measurement request. No more results will be sent + * for this peer in this session. + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_FAILED: Peer reported request + * failed, and requested not to send an additional request for number + * of seconds specified by QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_VALUE_SECONDS + * attribute. + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_INVALID: Request validation + * failed. Request was not sent over the air. + */ +enum qca_wlan_vendor_attr_ftm_peer_result_status { + QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_OK, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_INCAPABLE, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_FAILED, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_INVALID, +}; + +/** + * enum qca_wlan_vendor_attr_ftm_peer_result_flags: Various flags + * for measurement result, per-peer + * + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_FLAG_DONE: If set, + * measurement completed for this peer. No more results will be reported + * for this peer in this session. + */ +enum qca_wlan_vendor_attr_ftm_peer_result_flags { + QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_FLAG_DONE = 1 << 0, +}; + +/** + * enum qca_vendor_attr_loc_session_status: Session completion status code + * + * @QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_OK: Session completed + * successfully. + * @QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_ABORTED: Session aborted + * by request. + * @QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_INVALID: Session request + * was invalid and was not started. + * @QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_FAILED: Session had an error + * and did not complete normally (for example out of resources). + */ +enum qca_vendor_attr_loc_session_status { + QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_OK, + QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_ABORTED, + QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_INVALID, + QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_FAILED, +}; + +/** + * enum qca_wlan_vendor_attr_ftm_meas: Single measurement data + * + * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T1: Time of departure (TOD) of FTM packet as + * recorded by responder, in picoseconds. + * See IEEE P802.11-REVmc/D7.0, 11.24.6.4 for more information. + * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T2: Time of arrival (TOA) of FTM packet at + * initiator, in picoseconds. + * See IEEE P802.11-REVmc/D7.0, 11.24.6.4 for more information. + * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T3: TOD of ACK packet as recorded by + * initiator, in picoseconds. + * See IEEE P802.11-REVmc/D7.0, 11.24.6.4 for more information. + * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T4: TOA of ACK packet at + * responder, in picoseconds. + * See IEEE P802.11-REVmc/D7.0, 11.24.6.4 for more information. + * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_RSSI: RSSI (signal level) as recorded + * during this measurement exchange. Optional and will be provided if + * the hardware can measure it. + * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_TOD_ERR: TOD error reported by + * responder. Not always provided. + * See IEEE P802.11-REVmc/D7.0, 9.6.8.33 for more information. + * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_TOA_ERR: TOA error reported by + * responder. Not always provided. + * See IEEE P802.11-REVmc/D7.0, 9.6.8.33 for more information. + * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_INITIATOR_TOD_ERR: TOD error measured by + * initiator. Not always provided. + * See IEEE P802.11-REVmc/D7.0, 9.6.8.33 for more information. + * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_INITIATOR_TOA_ERR: TOA error measured by + * initiator. Not always provided. + * See IEEE P802.11-REVmc/D7.0, 9.6.8.33 for more information. + * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PAD: Dummy attribute for padding. + */ +enum qca_wlan_vendor_attr_ftm_meas { + QCA_WLAN_VENDOR_ATTR_FTM_MEAS_INVALID, + QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T1, + QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T2, + QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T3, + QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T4, + QCA_WLAN_VENDOR_ATTR_FTM_MEAS_RSSI, + QCA_WLAN_VENDOR_ATTR_FTM_MEAS_TOD_ERR, + QCA_WLAN_VENDOR_ATTR_FTM_MEAS_TOA_ERR, + QCA_WLAN_VENDOR_ATTR_FTM_MEAS_INITIATOR_TOD_ERR, + QCA_WLAN_VENDOR_ATTR_FTM_MEAS_INITIATOR_TOA_ERR, + QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PAD, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_FTM_MEAS_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_FTM_MEAS_MAX = + QCA_WLAN_VENDOR_ATTR_FTM_MEAS_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_aoa_type - AOA measurement type + * + * @QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE: Phase of the strongest + * CIR (channel impulse response) path for each antenna. + * @QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE_AMP: Phase and amplitude + * of the strongest CIR path for each antenna. + */ +enum qca_wlan_vendor_attr_aoa_type { + QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE, + QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE_AMP, + QCA_WLAN_VENDOR_ATTR_AOA_TYPE_MAX +}; + +/** + * enum qca_wlan_vendor_attr_encryption_test - Attributes to + * validate encryption engine + * + * @QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_NEEDS_DECRYPTION: Flag attribute. + * This will be included if the request is for decryption; if not included, + * the request is treated as a request for encryption by default. + * @QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_CIPHER: Unsigned 32-bit value + * indicating the key cipher suite. Takes same values as + * NL80211_ATTR_KEY_CIPHER. + * @QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_KEYID: Unsigned 8-bit value + * Key Id to be used for encryption + * @QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_TK: Array of 8-bit values. + * Key (TK) to be used for encryption/decryption + * @QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_PN: Array of 8-bit values. + * Packet number to be specified for encryption/decryption + * 6 bytes for TKIP/CCMP/GCMP. + * @QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_DATA: Array of 8-bit values + * representing the 802.11 packet (header + payload + FCS) that + * needs to be encrypted/decrypted. + * Encrypted/decrypted response from the driver will also be sent + * to userspace with the same attribute. + */ +enum qca_wlan_vendor_attr_encryption_test { + QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_NEEDS_DECRYPTION, + QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_CIPHER, + QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_KEYID, + QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_TK, + QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_PN, + QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_DATA, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_MAX = + QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_AFTER_LAST - 1 }; #endif /* QCA_VENDOR_H */ diff --git a/contrib/wpa/src/common/sae.c b/contrib/wpa/src/common/sae.c index 503fa1d7b9a9..9f70f036ba76 100644 --- a/contrib/wpa/src/common/sae.c +++ b/contrib/wpa/src/common/sae.c @@ -1,6 +1,6 @@ /* * Simultaneous authentication of equals - * Copyright (c) 2012-2015, Jouni Malinen + * Copyright (c) 2012-2016, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -275,8 +275,9 @@ static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed, /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */ bits = crypto_ec_prime_len_bits(sae->tmp->ec); - sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking", - prime, sae->tmp->prime_len, pwd_value, bits); + if (sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking", + 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); wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", @@ -318,11 +319,10 @@ static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed, wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN); /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */ - sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking", - sae->tmp->dh->prime, sae->tmp->prime_len, pwd_value, - bits); - if (bits % 8) - buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8); + if (sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking", + sae->tmp->dh->prime, sae->tmp->prime_len, pwd_value, + bits) < 0) + return -1; wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", pwd_value, sae->tmp->prime_len); @@ -811,11 +811,13 @@ static int sae_derive_keys(struct sae_data *sae, const u8 *k) crypto_bignum_mod(tmp, sae->tmp->order, tmp); crypto_bignum_to_bin(tmp, val, sizeof(val), sae->tmp->prime_len); wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, SAE_PMKID_LEN); - sha256_prf(keyseed, sizeof(keyseed), "SAE KCK and PMK", - val, sae->tmp->prime_len, keys, sizeof(keys)); + if (sha256_prf(keyseed, sizeof(keyseed), "SAE KCK and PMK", + val, sae->tmp->prime_len, keys, sizeof(keys)) < 0) + goto fail; os_memset(keyseed, 0, sizeof(keyseed)); os_memcpy(sae->tmp->kck, keys, SAE_KCK_LEN); os_memcpy(sae->pmk, keys + SAE_KCK_LEN, SAE_PMK_LEN); + os_memcpy(sae->pmkid, val, SAE_PMKID_LEN); os_memset(keys, 0, sizeof(keys)); wpa_hexdump_key(MSG_DEBUG, "SAE: KCK", sae->tmp->kck, SAE_KCK_LEN); wpa_hexdump_key(MSG_DEBUG, "SAE: PMK", sae->pmk, SAE_PMK_LEN); @@ -923,7 +925,7 @@ static void sae_parse_commit_token(struct sae_data *sae, const u8 **pos, const u8 *end, const u8 **token, size_t *token_len) { - if (*pos + (sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len < end) { + if ((sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len < end - *pos) { size_t tlen = end - (*pos + (sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len); wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen); @@ -946,7 +948,7 @@ static u16 sae_parse_commit_scalar(struct sae_data *sae, const u8 **pos, { struct crypto_bignum *peer_scalar; - if (*pos + sae->tmp->prime_len > end) { + if (sae->tmp->prime_len > end - *pos) { wpa_printf(MSG_DEBUG, "SAE: Not enough data for scalar"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } @@ -994,7 +996,7 @@ static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 *pos, { u8 prime[SAE_MAX_ECC_PRIME_LEN]; - if (pos + 2 * sae->tmp->prime_len > end) { + if (2 * sae->tmp->prime_len > end - pos) { wpa_printf(MSG_DEBUG, "SAE: Not enough data for " "commit-element"); return WLAN_STATUS_UNSPECIFIED_FAILURE; @@ -1040,7 +1042,7 @@ static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 *pos, struct crypto_bignum *res, *one; const u8 one_bin[1] = { 0x01 }; - if (pos + sae->tmp->prime_len > end) { + if (sae->tmp->prime_len > end - pos) { wpa_printf(MSG_DEBUG, "SAE: Not enough data for " "commit-element"); return WLAN_STATUS_UNSPECIFIED_FAILURE; @@ -1098,7 +1100,7 @@ u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, u16 res; /* Check Finite Cyclic Group */ - if (pos + 2 > end) + if (end - pos < 2) return WLAN_STATUS_UNSPECIFIED_FAILURE; res = sae_group_allowed(sae, allowed_groups, WPA_GET_LE16(pos)); if (res != WLAN_STATUS_SUCCESS) diff --git a/contrib/wpa/src/common/sae.h b/contrib/wpa/src/common/sae.h index c07026cd497c..a4270bc22d14 100644 --- a/contrib/wpa/src/common/sae.h +++ b/contrib/wpa/src/common/sae.h @@ -45,6 +45,7 @@ struct sae_data { enum { SAE_NOTHING, SAE_COMMITTED, SAE_CONFIRMED, SAE_ACCEPTED } state; u16 send_confirm; u8 pmk[SAE_PMK_LEN]; + u8 pmkid[SAE_PMKID_LEN]; struct crypto_bignum *peer_commit_scalar; int group; int sync; diff --git a/contrib/wpa/src/common/version.h b/contrib/wpa/src/common/version.h index a5cc5b7b5bcc..75e5c6e006cc 100644 --- a/contrib/wpa/src/common/version.h +++ b/contrib/wpa/src/common/version.h @@ -5,6 +5,10 @@ #define VERSION_STR_POSTFIX "" #endif /* VERSION_STR_POSTFIX */ -#define VERSION_STR "2.5" VERSION_STR_POSTFIX +#ifndef GIT_VERSION_STR_POSTFIX +#define GIT_VERSION_STR_POSTFIX "" +#endif /* GIT_VERSION_STR_POSTFIX */ + +#define VERSION_STR "2.6" VERSION_STR_POSTFIX GIT_VERSION_STR_POSTFIX #endif /* VERSION_H */ diff --git a/contrib/wpa/src/common/wpa_common.c b/contrib/wpa/src/common/wpa_common.c index e9d4248d72d4..299b8bbee031 100644 --- a/contrib/wpa/src/common/wpa_common.c +++ b/contrib/wpa/src/common/wpa_common.c @@ -292,38 +292,47 @@ static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len, pos = ie + sizeof(struct rsn_ftie); end = ie + ie_len; - while (pos + 2 <= end && pos + 2 + pos[1] <= end) { - switch (pos[0]) { + while (end - pos >= 2) { + u8 id, len; + + id = *pos++; + len = *pos++; + if (len > end - pos) + break; + + switch (id) { case FTIE_SUBELEM_R1KH_ID: - if (pos[1] != FT_R1KH_ID_LEN) { - wpa_printf(MSG_DEBUG, "FT: Invalid R1KH-ID " - "length in FTIE: %d", pos[1]); + if (len != FT_R1KH_ID_LEN) { + wpa_printf(MSG_DEBUG, + "FT: Invalid R1KH-ID length in FTIE: %d", + len); return -1; } - parse->r1kh_id = pos + 2; + parse->r1kh_id = pos; break; case FTIE_SUBELEM_GTK: - parse->gtk = pos + 2; - parse->gtk_len = pos[1]; + parse->gtk = pos; + parse->gtk_len = len; break; case FTIE_SUBELEM_R0KH_ID: - if (pos[1] < 1 || pos[1] > FT_R0KH_ID_MAX_LEN) { - wpa_printf(MSG_DEBUG, "FT: Invalid R0KH-ID " - "length in FTIE: %d", pos[1]); + if (len < 1 || len > FT_R0KH_ID_MAX_LEN) { + wpa_printf(MSG_DEBUG, + "FT: Invalid R0KH-ID length in FTIE: %d", + len); return -1; } - parse->r0kh_id = pos + 2; - parse->r0kh_id_len = pos[1]; + parse->r0kh_id = pos; + parse->r0kh_id_len = len; break; #ifdef CONFIG_IEEE80211W case FTIE_SUBELEM_IGTK: - parse->igtk = pos + 2; - parse->igtk_len = pos[1]; + parse->igtk = pos; + parse->igtk_len = len; break; #endif /* CONFIG_IEEE80211W */ } - pos += 2 + pos[1]; + pos += len; } return 0; @@ -345,11 +354,18 @@ int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, pos = ies; end = ies + ies_len; - while (pos + 2 <= end && pos + 2 + pos[1] <= end) { - switch (pos[0]) { + while (end - pos >= 2) { + u8 id, len; + + id = *pos++; + len = *pos++; + if (len > end - pos) + break; + + switch (id) { case WLAN_EID_RSN: - parse->rsn = pos + 2; - parse->rsn_len = pos[1]; + parse->rsn = pos; + parse->rsn_len = len; ret = wpa_parse_wpa_ie_rsn(parse->rsn - 2, parse->rsn_len + 2, &data); @@ -362,32 +378,32 @@ int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, parse->rsn_pmkid = data.pmkid; break; case WLAN_EID_MOBILITY_DOMAIN: - if (pos[1] < sizeof(struct rsn_mdie)) + if (len < sizeof(struct rsn_mdie)) return -1; - parse->mdie = pos + 2; - parse->mdie_len = pos[1]; + parse->mdie = pos; + parse->mdie_len = len; break; case WLAN_EID_FAST_BSS_TRANSITION: - if (pos[1] < sizeof(*ftie)) + if (len < sizeof(*ftie)) return -1; - ftie = (const struct rsn_ftie *) (pos + 2); + ftie = (const struct rsn_ftie *) pos; prot_ie_count = ftie->mic_control[1]; - if (wpa_ft_parse_ftie(pos + 2, pos[1], parse) < 0) + if (wpa_ft_parse_ftie(pos, len, parse) < 0) return -1; break; case WLAN_EID_TIMEOUT_INTERVAL: - if (pos[1] != 5) + if (len != 5) break; - parse->tie = pos + 2; - parse->tie_len = pos[1]; + parse->tie = pos; + parse->tie_len = len; break; case WLAN_EID_RIC_DATA: if (parse->ric == NULL) - parse->ric = pos; + parse->ric = pos - 2; break; } - pos += 2 + pos[1]; + pos += len; } if (prot_ie_count == 0) @@ -416,13 +432,15 @@ int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, } /* Determine the end of the RIC IE(s) */ - pos = parse->ric; - while (pos && pos + 2 <= end && pos + 2 + pos[1] <= end && - prot_ie_count) { - prot_ie_count--; - pos += 2 + pos[1]; + if (parse->ric) { + pos = parse->ric; + while (end - pos >= 2 && 2 + pos[1] <= end - pos && + prot_ie_count) { + prot_ie_count--; + pos += 2 + pos[1]; + } + parse->ric_len = pos - parse->ric; } - parse->ric_len = pos - parse->ric; if (prot_ie_count) { wpa_printf(MSG_DEBUG, "FT: %d protected IEs missing from " "frame", (int) prot_ie_count); @@ -582,8 +600,10 @@ 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); if (!wpa_cipher_valid_group(data->group_cipher)) { - wpa_printf(MSG_DEBUG, "%s: invalid group cipher 0x%x", - __func__, data->group_cipher); + wpa_printf(MSG_DEBUG, + "%s: invalid group cipher 0x%x (%08x)", + __func__, data->group_cipher, + WPA_GET_BE32(pos)); return -1; } pos += RSN_SELECTOR_LEN; @@ -671,9 +691,10 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, if (left >= 4) { data->mgmt_group_cipher = rsn_selector_to_bitfield(pos); if (!wpa_cipher_valid_mgmt_group(data->mgmt_group_cipher)) { - wpa_printf(MSG_DEBUG, "%s: Unsupported management " - "group cipher 0x%x", __func__, - data->mgmt_group_cipher); + wpa_printf(MSG_DEBUG, + "%s: Unsupported management group cipher 0x%x (%08x)", + __func__, data->mgmt_group_cipher, + WPA_GET_BE32(pos)); return -10; } pos += RSN_SELECTOR_LEN; @@ -1163,6 +1184,8 @@ const char * wpa_key_mgmt_txt(int key_mgmt, int proto) "WPA2-PSK" : "WPA-PSK"; case WPA_KEY_MGMT_NONE: return "NONE"; + case WPA_KEY_MGMT_WPA_NONE: + return "WPA-NONE"; case WPA_KEY_MGMT_IEEE8021X_NO_WPA: return "IEEE 802.1X (no WPA)"; #ifdef CONFIG_IEEE80211R @@ -1261,13 +1284,13 @@ int wpa_compare_rsn_ie(int ft_initial_assoc, #ifdef CONFIG_IEEE80211R -int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid) +int wpa_insert_pmkid(u8 *ies, size_t *ies_len, const u8 *pmkid) { u8 *start, *end, *rpos, *rend; int added = 0; start = ies; - end = ies + ies_len; + end = ies + *ies_len; while (start < end) { if (*start == WLAN_EID_RSN) @@ -1320,11 +1343,29 @@ int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid) added += 2 + PMKID_LEN; start[1] += 2 + PMKID_LEN; } else { - /* PMKID-Count was included; use it */ - if (WPA_GET_LE16(rpos) != 0) { - wpa_printf(MSG_ERROR, "FT: Unexpected PMKID " - "in RSN IE in EAPOL-Key data"); + u16 num_pmkid; + + if (rend - rpos < 2) return -1; + num_pmkid = WPA_GET_LE16(rpos); + /* PMKID-Count was included; use it */ + if (num_pmkid != 0) { + u8 *after; + + if (num_pmkid * PMKID_LEN > rend - rpos - 2) + return -1; + /* + * PMKID may have been included in RSN IE in + * (Re)Association Request frame, so remove the old + * PMKID(s) first before adding the new one. + */ + wpa_printf(MSG_DEBUG, + "FT: Remove %u old PMKID(s) from RSN IE", + num_pmkid); + after = rpos + 2 + num_pmkid * PMKID_LEN; + os_memmove(rpos + 2, after, rend - after); + start[1] -= num_pmkid * PMKID_LEN; + added -= num_pmkid * PMKID_LEN; } WPA_PUT_LE16(rpos, 1); rpos += 2; @@ -1337,7 +1378,9 @@ int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid) wpa_hexdump(MSG_DEBUG, "FT: RSN IE after modification " "(PMKID inserted)", start, 2 + start[1]); - return added; + *ies_len += added; + + return 0; } #endif /* CONFIG_IEEE80211R */ diff --git a/contrib/wpa/src/common/wpa_common.h b/contrib/wpa/src/common/wpa_common.h index a04e759373b9..1021ccb05a71 100644 --- a/contrib/wpa/src/common/wpa_common.h +++ b/contrib/wpa/src/common/wpa_common.h @@ -12,6 +12,8 @@ /* IEEE 802.11i */ #define PMKID_LEN 16 #define PMK_LEN 32 +#define PMK_LEN_SUITE_B_192 48 +#define PMK_LEN_MAX 48 #define WPA_REPLAY_COUNTER_LEN 8 #define WPA_NONCE_LEN 32 #define WPA_KEY_RSC_LEN 8 @@ -419,7 +421,7 @@ u32 wpa_akm_to_suite(int akm); int wpa_compare_rsn_ie(int ft_initial_assoc, const u8 *ie1, size_t ie1len, const u8 *ie2, size_t ie2len); -int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid); +int wpa_insert_pmkid(u8 *ies, size_t *ies_len, const u8 *pmkid); struct wpa_ft_ies { const u8 *mdie; diff --git a/contrib/wpa/src/common/wpa_ctrl.c b/contrib/wpa/src/common/wpa_ctrl.c index 5733aa605d18..623c2a768e43 100644 --- a/contrib/wpa/src/common/wpa_ctrl.c +++ b/contrib/wpa/src/common/wpa_ctrl.c @@ -532,6 +532,8 @@ retry_send: FD_ZERO(&rfds); FD_SET(ctrl->s, &rfds); res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv); + if (res < 0 && errno == EINTR) + continue; if (res < 0) return res; if (FD_ISSET(ctrl->s, &rfds)) { diff --git a/contrib/wpa/src/common/wpa_ctrl.h b/contrib/wpa/src/common/wpa_ctrl.h index 3de46823588b..4dcba81dc1a4 100644 --- a/contrib/wpa/src/common/wpa_ctrl.h +++ b/contrib/wpa/src/common/wpa_ctrl.h @@ -76,6 +76,21 @@ extern "C" { #define WPA_EVENT_SIGNAL_CHANGE "CTRL-EVENT-SIGNAL-CHANGE " /** Regulatory domain channel */ #define WPA_EVENT_REGDOM_CHANGE "CTRL-EVENT-REGDOM-CHANGE " +/** Channel switch (followed by freq= and other channel parameters) */ +#define WPA_EVENT_CHANNEL_SWITCH "CTRL-EVENT-CHANNEL-SWITCH " + +/** IP subnet status change notification + * + * When using an offloaded roaming mechanism where driver/firmware takes care + * of roaming and IP subnet validation checks post-roaming, this event can + * indicate whether IP subnet has changed. + * + * The event has a status=<0/1/2> parameter where + * 0 = unknown + * 1 = IP subnet unchanged (can continue to use the old IP address) + * 2 = IP subnet changed (need to get a new IP address) + */ +#define WPA_EVENT_SUBNET_STATUS_UPDATE "CTRL-EVENT-SUBNET-STATUS-UPDATE " /** RSN IBSS 4-way handshakes completed with specified peer */ #define IBSS_RSN_COMPLETED "IBSS-RSN-COMPLETED " @@ -174,6 +189,7 @@ extern "C" { #define P2P_EVENT_SERV_ASP_RESP "P2P-SERV-ASP-RESP " #define P2P_EVENT_INVITATION_RECEIVED "P2P-INVITATION-RECEIVED " #define P2P_EVENT_INVITATION_RESULT "P2P-INVITATION-RESULT " +#define P2P_EVENT_INVITATION_ACCEPTED "P2P-INVITATION-ACCEPTED " #define P2P_EVENT_FIND_STOPPED "P2P-FIND-STOPPED " #define P2P_EVENT_PERSISTENT_PSK_FAIL "P2P-PERSISTENT-PSK-FAIL id=" #define P2P_EVENT_PRESENCE_RESPONSE "P2P-PRESENCE-RESPONSE " @@ -212,6 +228,11 @@ extern "C" { /* parameters: */ #define ANQP_QUERY_DONE "ANQP-QUERY-DONE " +#define RX_ANQP "RX-ANQP " +#define RX_HS20_ANQP "RX-HS20-ANQP " +#define RX_HS20_ANQP_ICON "RX-HS20-ANQP-ICON " +#define RX_HS20_ICON "RX-HS20-ICON " + #define HS20_SUBSCRIPTION_REMEDIATION "HS20-SUBSCRIPTION-REMEDIATION " #define HS20_DEAUTH_IMMINENT_NOTICE "HS20-DEAUTH-IMMINENT-NOTICE " @@ -232,6 +253,7 @@ extern "C" { #define AP_STA_CONNECTED "AP-STA-CONNECTED " #define AP_STA_DISCONNECTED "AP-STA-DISCONNECTED " #define AP_STA_POSSIBLE_PSK_MISMATCH "AP-STA-POSSIBLE-PSK-MISMATCH " +#define AP_STA_POLL_OK "AP-STA-POLL-OK " #define AP_REJECTED_MAX_STA "AP-REJECTED-MAX-STA " #define AP_REJECTED_BLOCKED_STA "AP-REJECTED-BLOCKED-STA " @@ -254,9 +276,18 @@ extern "C" { #define AP_CSA_FINISHED "AP-CSA-FINISHED " +#define P2P_EVENT_LISTEN_OFFLOAD_STOP "P2P-LISTEN-OFFLOAD-STOPPED " +#define P2P_LISTEN_OFFLOAD_STOP_REASON "P2P-LISTEN-OFFLOAD-STOP-REASON " + /* BSS Transition Management Response frame received */ #define BSS_TM_RESP "BSS-TM-RESP " +/* MBO IE with cellular data connection preference received */ +#define MBO_CELL_PREFERENCE "MBO-CELL-PREFERENCE " + +/* BSS Transition Management Request received with MBO transition reason */ +#define MBO_TRANSITION_REASON "MBO-TRANSITION-REASON " + /* BSS command information masks */ #define WPA_BSS_MASK_ALL 0xFFFDFFFF @@ -300,6 +331,7 @@ enum wpa_vendor_elem_frame { VENDOR_ELEM_P2P_ASSOC_REQ = 11, VENDOR_ELEM_P2P_ASSOC_RESP = 12, VENDOR_ELEM_ASSOC_REQ = 13, + VENDOR_ELEM_PROBE_REQ = 14, NUM_VENDOR_ELEM_FRAMES }; diff --git a/contrib/wpa/src/common/wpa_helpers.c b/contrib/wpa/src/common/wpa_helpers.c index 28913b9139aa..f1594213f97f 100644 --- a/contrib/wpa/src/common/wpa_helpers.c +++ b/contrib/wpa/src/common/wpa_helpers.c @@ -172,7 +172,8 @@ int get_wpa_status(const char *ifname, const char *field, char *obuf, if (ctrl == NULL) return -1; len = sizeof(buf); - if (wpa_ctrl_request(ctrl, "STATUS", 6, buf, &len, NULL) < 0) { + if (wpa_ctrl_request(ctrl, "STATUS-NO_EVENTS", 16, buf, &len, + NULL) < 0) { wpa_ctrl_close(ctrl); return -1; } diff --git a/contrib/wpa/src/crypto/aes-cbc.c b/contrib/wpa/src/crypto/aes-cbc.c index 2833cfcc840d..0835f2cfb77e 100644 --- a/contrib/wpa/src/crypto/aes-cbc.c +++ b/contrib/wpa/src/crypto/aes-cbc.c @@ -28,6 +28,9 @@ int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) u8 *pos = data; int i, j, blocks; + if (TEST_FAIL()) + return -1; + ctx = aes_encrypt_init(key, 16); if (ctx == NULL) return -1; @@ -61,6 +64,9 @@ int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) u8 *pos = data; int i, j, blocks; + if (TEST_FAIL()) + return -1; + ctx = aes_decrypt_init(key, 16); if (ctx == NULL) return -1; diff --git a/contrib/wpa/src/crypto/aes-omac1.c b/contrib/wpa/src/crypto/aes-omac1.c index 375db5735be3..8642516340c6 100644 --- a/contrib/wpa/src/crypto/aes-omac1.c +++ b/contrib/wpa/src/crypto/aes-omac1.c @@ -48,6 +48,9 @@ int omac1_aes_vector(const u8 *key, size_t key_len, size_t num_elem, const u8 *pos, *end; size_t i, e, left, total_len; + if (TEST_FAIL()) + return -1; + ctx = aes_encrypt_init(key, key_len); if (ctx == NULL) return -1; diff --git a/contrib/wpa/src/crypto/crypto.h b/contrib/wpa/src/crypto/crypto.h index 534c4bd78654..bdc3ba6f37e0 100644 --- a/contrib/wpa/src/crypto/crypto.h +++ b/contrib/wpa/src/crypto/crypto.h @@ -79,6 +79,28 @@ int __must_check fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); +/** + * sha384_vector - SHA384 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + * Returns: 0 on success, -1 on failure + */ +int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac); + +/** + * sha512_vector - SHA512 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + * Returns: 0 on success, -1 on failure + */ +int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac); + /** * des_encrypt - Encrypt one block with DES * @clear: 8 octets (in) @@ -135,7 +157,8 @@ void aes_decrypt_deinit(void *ctx); enum crypto_hash_alg { CRYPTO_HASH_ALG_MD5, CRYPTO_HASH_ALG_SHA1, CRYPTO_HASH_ALG_HMAC_MD5, CRYPTO_HASH_ALG_HMAC_SHA1, - CRYPTO_HASH_ALG_SHA256, CRYPTO_HASH_ALG_HMAC_SHA256 + CRYPTO_HASH_ALG_SHA256, CRYPTO_HASH_ALG_HMAC_SHA256, + CRYPTO_HASH_ALG_SHA384, CRYPTO_HASH_ALG_SHA512 }; struct crypto_hash; diff --git a/contrib/wpa/src/crypto/crypto_internal.c b/contrib/wpa/src/crypto/crypto_internal.c index f3602dac346e..d391f48ab5b1 100644 --- a/contrib/wpa/src/crypto/crypto_internal.c +++ b/contrib/wpa/src/crypto/crypto_internal.c @@ -11,6 +11,8 @@ #include "common.h" #include "crypto.h" #include "sha256_i.h" +#include "sha384_i.h" +#include "sha512_i.h" #include "sha1_i.h" #include "md5_i.h" @@ -22,6 +24,12 @@ struct crypto_hash { #ifdef CONFIG_SHA256 struct sha256_state sha256; #endif /* CONFIG_SHA256 */ +#ifdef CONFIG_INTERNAL_SHA384 + struct sha384_state sha384; +#endif /* CONFIG_INTERNAL_SHA384 */ +#ifdef CONFIG_INTERNAL_SHA512 + struct sha512_state sha512; +#endif /* CONFIG_INTERNAL_SHA512 */ } u; u8 key[64]; size_t key_len; @@ -54,6 +62,16 @@ struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, sha256_init(&ctx->u.sha256); break; #endif /* CONFIG_SHA256 */ +#ifdef CONFIG_INTERNAL_SHA384 + case CRYPTO_HASH_ALG_SHA384: + sha384_init(&ctx->u.sha384); + break; +#endif /* CONFIG_INTERNAL_SHA384 */ +#ifdef CONFIG_INTERNAL_SHA512 + case CRYPTO_HASH_ALG_SHA512: + sha512_init(&ctx->u.sha512); + break; +#endif /* CONFIG_INTERNAL_SHA512 */ case CRYPTO_HASH_ALG_HMAC_MD5: if (key_len > sizeof(k_pad)) { MD5Init(&ctx->u.md5); @@ -142,6 +160,16 @@ void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len) sha256_process(&ctx->u.sha256, data, len); break; #endif /* CONFIG_SHA256 */ +#ifdef CONFIG_INTERNAL_SHA384 + case CRYPTO_HASH_ALG_SHA384: + sha384_process(&ctx->u.sha384, data, len); + break; +#endif /* CONFIG_INTERNAL_SHA384 */ +#ifdef CONFIG_INTERNAL_SHA512 + case CRYPTO_HASH_ALG_SHA512: + sha512_process(&ctx->u.sha512, data, len); + break; +#endif /* CONFIG_INTERNAL_SHA512 */ default: break; } @@ -191,6 +219,28 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) sha256_done(&ctx->u.sha256, mac); break; #endif /* CONFIG_SHA256 */ +#ifdef CONFIG_INTERNAL_SHA384 + case CRYPTO_HASH_ALG_SHA384: + if (*len < 48) { + *len = 48; + os_free(ctx); + return -1; + } + *len = 48; + sha384_done(&ctx->u.sha384, mac); + break; +#endif /* CONFIG_INTERNAL_SHA384 */ +#ifdef CONFIG_INTERNAL_SHA512 + case CRYPTO_HASH_ALG_SHA512: + if (*len < 64) { + *len = 64; + os_free(ctx); + return -1; + } + *len = 64; + sha512_done(&ctx->u.sha512, mac); + break; +#endif /* CONFIG_INTERNAL_SHA512 */ case CRYPTO_HASH_ALG_HMAC_MD5: if (*len < 16) { *len = 16; diff --git a/contrib/wpa/src/crypto/crypto_module_tests.c b/contrib/wpa/src/crypto/crypto_module_tests.c index 581005df3e39..ffd23942e32d 100644 --- a/contrib/wpa/src/crypto/crypto_module_tests.c +++ b/contrib/wpa/src/crypto/crypto_module_tests.c @@ -9,6 +9,7 @@ #include "utils/includes.h" #include "utils/common.h" +#include "utils/module_tests.h" #include "crypto/aes_siv.h" #include "crypto/aes_wrap.h" #include "crypto/aes.h" @@ -1266,7 +1267,7 @@ static int test_sha1(void) } -const struct { +static const struct { char *data; u8 hash[32]; } tests[] = { @@ -1290,7 +1291,7 @@ const struct { } }; -const struct hmac_test { +static const struct hmac_test { u8 key[80]; size_t key_len; u8 data[128]; @@ -1503,6 +1504,7 @@ static int test_sha256(void) const u8 *addr[2]; size_t len[2]; int errors = 0; + u8 *key; for (i = 0; i < ARRAY_SIZE(tests); i++) { wpa_printf(MSG_INFO, "SHA256 test case %d:", i + 1); @@ -1573,12 +1575,66 @@ static int test_sha256(void) hash, sizeof(hash)); /* TODO: add proper test case for this */ + key = os_malloc(8161); + if (key) { +#ifdef CONFIG_HMAC_SHA256_KDF + int res; + + res = hmac_sha256_kdf((u8 *) "secret", 6, "label", + (u8 *) "seed", 4, key, 8160); + if (res) { + wpa_printf(MSG_INFO, + "Unexpected hmac_sha256_kdf(outlen=8160) failure"); + errors++; + } + + res = hmac_sha256_kdf((u8 *) "secret", 6, "label", + (u8 *) "seed", 4, key, 8161); + if (res == 0) { + wpa_printf(MSG_INFO, + "Unexpected hmac_sha256_kdf(outlen=8161) success"); + errors++; + } +#endif /* CONFIG_HMAC_SHA256_KDF */ + + os_free(key); + } + if (!errors) wpa_printf(MSG_INFO, "SHA256 test cases passed"); return errors; } +static int test_fips186_2_prf(void) +{ + /* http://csrc.nist.gov/encryption/dss/Examples-1024bit.pdf */ + u8 xkey[] = { + 0xbd, 0x02, 0x9b, 0xbe, 0x7f, 0x51, 0x96, 0x0b, + 0xcf, 0x9e, 0xdb, 0x2b, 0x61, 0xf0, 0x6f, 0x0f, + 0xeb, 0x5a, 0x38, 0xb6 + }; + u8 w[] = { + 0x20, 0x70, 0xb3, 0x22, 0x3d, 0xba, 0x37, 0x2f, + 0xde, 0x1c, 0x0f, 0xfc, 0x7b, 0x2e, 0x3b, 0x49, + 0x8b, 0x26, 0x06, 0x14, 0x3c, 0x6c, 0x18, 0xba, + 0xcb, 0x0f, 0x6c, 0x55, 0xba, 0xbb, 0x13, 0x78, + 0x8e, 0x20, 0xd7, 0x37, 0xa3, 0x27, 0x51, 0x16 + }; + u8 buf[40]; + + wpa_printf(MSG_INFO, + "Testing EAP-SIM PRF (FIPS 186-2 + change notice 1)"); + if (fips186_2_prf(xkey, sizeof(xkey), buf, sizeof(buf)) < 0 || + os_memcmp(w, buf, sizeof(w)) != 0) { + wpa_printf(MSG_INFO, "fips186_2_prf failed"); + return 1; + } + + return 0; +} + + static int test_ms_funcs(void) { #ifndef CONFIG_FIPS @@ -1695,6 +1751,7 @@ int crypto_module_tests(void) test_md5() || test_sha1() || test_sha256() || + test_fips186_2_prf() || test_ms_funcs()) ret = -1; diff --git a/contrib/wpa/src/crypto/crypto_openssl.c b/contrib/wpa/src/crypto/crypto_openssl.c index 6cff75c64ae5..19e0e2be87be 100644 --- a/contrib/wpa/src/crypto/crypto_openssl.c +++ b/contrib/wpa/src/crypto/crypto_openssl.c @@ -29,11 +29,61 @@ #include "sha1.h" #include "sha256.h" #include "sha384.h" +#include "md5.h" +#include "aes_wrap.h" #include "crypto.h" +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) +/* Compatibility wrappers for older versions. */ + +static HMAC_CTX * HMAC_CTX_new(void) +{ + HMAC_CTX *ctx; + + ctx = os_zalloc(sizeof(*ctx)); + if (ctx) + HMAC_CTX_init(ctx); + return ctx; +} + + +static void HMAC_CTX_free(HMAC_CTX *ctx) +{ + if (!ctx) + return; + HMAC_CTX_cleanup(ctx); + bin_clear_free(ctx, sizeof(*ctx)); +} + + +static EVP_MD_CTX * EVP_MD_CTX_new(void) +{ + EVP_MD_CTX *ctx; + + ctx = os_zalloc(sizeof(*ctx)); + if (ctx) + EVP_MD_CTX_init(ctx); + return ctx; +} + + +static void EVP_MD_CTX_free(EVP_MD_CTX *ctx) +{ + if (!ctx) + return; + EVP_MD_CTX_cleanup(ctx); + bin_clear_free(ctx, sizeof(*ctx)); +} + +#endif /* OpenSSL version < 1.1.0 */ + static BIGNUM * get_group5_prime(void) { -#ifdef OPENSSL_IS_BORINGSSL +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) + return BN_get_rfc3526_prime_1536(NULL); +#elif !defined(OPENSSL_IS_BORINGSSL) + return get_rfc3526_prime_1536(NULL); +#else static const unsigned char RFC3526_PRIME_1536[] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0x0F,0xDA,0xA2, 0x21,0x68,0xC2,0x34,0xC4,0xC6,0x62,0x8B,0x80,0xDC,0x1C,0xD1, @@ -53,9 +103,7 @@ static BIGNUM * get_group5_prime(void) 0xCA,0x23,0x73,0x27,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, }; return BN_bin2bn(RFC3526_PRIME_1536, sizeof(RFC3526_PRIME_1536), NULL); -#else /* OPENSSL_IS_BORINGSSL */ - return get_rfc3526_prime_1536(NULL); -#endif /* OPENSSL_IS_BORINGSSL */ +#endif } #ifdef OPENSSL_NO_SHA256 @@ -65,29 +113,38 @@ static BIGNUM * get_group5_prime(void) static int openssl_digest_vector(const EVP_MD *type, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { - EVP_MD_CTX ctx; + EVP_MD_CTX *ctx; size_t i; unsigned int mac_len; - EVP_MD_CTX_init(&ctx); - if (!EVP_DigestInit_ex(&ctx, type, NULL)) { + if (TEST_FAIL()) + return -1; + + ctx = EVP_MD_CTX_new(); + if (!ctx) + return -1; + if (!EVP_DigestInit_ex(ctx, type, NULL)) { wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestInit_ex failed: %s", ERR_error_string(ERR_get_error(), NULL)); + EVP_MD_CTX_free(ctx); return -1; } for (i = 0; i < num_elem; i++) { - if (!EVP_DigestUpdate(&ctx, addr[i], len[i])) { + if (!EVP_DigestUpdate(ctx, addr[i], len[i])) { wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestUpdate " "failed: %s", ERR_error_string(ERR_get_error(), NULL)); + EVP_MD_CTX_free(ctx); return -1; } } - if (!EVP_DigestFinal(&ctx, mac, &mac_len)) { + if (!EVP_DigestFinal(ctx, mac, &mac_len)) { wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestFinal failed: %s", ERR_error_string(ERR_get_error(), NULL)); + EVP_MD_CTX_free(ctx); return -1; } + EVP_MD_CTX_free(ctx); return 0; } @@ -129,32 +186,34 @@ int rc4_skip(const u8 *key, size_t keylen, size_t skip, #ifdef OPENSSL_NO_RC4 return -1; #else /* OPENSSL_NO_RC4 */ - EVP_CIPHER_CTX ctx; + EVP_CIPHER_CTX *ctx; int outl; int res = -1; unsigned char skip_buf[16]; - EVP_CIPHER_CTX_init(&ctx); - if (!EVP_CIPHER_CTX_set_padding(&ctx, 0) || - !EVP_CipherInit_ex(&ctx, EVP_rc4(), NULL, NULL, NULL, 1) || - !EVP_CIPHER_CTX_set_key_length(&ctx, keylen) || - !EVP_CipherInit_ex(&ctx, NULL, NULL, key, NULL, 1)) + ctx = EVP_CIPHER_CTX_new(); + if (!ctx || + !EVP_CIPHER_CTX_set_padding(ctx, 0) || + !EVP_CipherInit_ex(ctx, EVP_rc4(), NULL, NULL, NULL, 1) || + !EVP_CIPHER_CTX_set_key_length(ctx, keylen) || + !EVP_CipherInit_ex(ctx, NULL, NULL, key, NULL, 1)) goto out; while (skip >= sizeof(skip_buf)) { size_t len = skip; if (len > sizeof(skip_buf)) len = sizeof(skip_buf); - if (!EVP_CipherUpdate(&ctx, skip_buf, &outl, skip_buf, len)) + if (!EVP_CipherUpdate(ctx, skip_buf, &outl, skip_buf, len)) goto out; skip -= len; } - if (EVP_CipherUpdate(&ctx, data, &outl, data, data_len)) + if (EVP_CipherUpdate(ctx, data, &outl, data, data_len)) res = 0; out: - EVP_CIPHER_CTX_cleanup(&ctx); + if (ctx) + EVP_CIPHER_CTX_free(ctx); return res; #endif /* OPENSSL_NO_RC4 */ } @@ -206,14 +265,16 @@ void * aes_encrypt_init(const u8 *key, size_t len) EVP_CIPHER_CTX *ctx; const EVP_CIPHER *type; + if (TEST_FAIL()) + return NULL; + type = aes_get_evp_cipher(len); if (type == NULL) return NULL; - ctx = os_malloc(sizeof(*ctx)); + ctx = EVP_CIPHER_CTX_new(); if (ctx == NULL) return NULL; - EVP_CIPHER_CTX_init(ctx); if (EVP_EncryptInit_ex(ctx, type, NULL, key, NULL) != 1) { os_free(ctx); return NULL; @@ -247,8 +308,7 @@ void aes_encrypt_deinit(void *ctx) wpa_printf(MSG_ERROR, "OpenSSL: Unexpected padding length %d " "in AES encrypt", len); } - EVP_CIPHER_CTX_cleanup(c); - bin_clear_free(c, sizeof(*c)); + EVP_CIPHER_CTX_free(c); } @@ -257,16 +317,18 @@ void * aes_decrypt_init(const u8 *key, size_t len) EVP_CIPHER_CTX *ctx; const EVP_CIPHER *type; + if (TEST_FAIL()) + return NULL; + type = aes_get_evp_cipher(len); if (type == NULL) return NULL; - ctx = os_malloc(sizeof(*ctx)); + ctx = EVP_CIPHER_CTX_new(); if (ctx == NULL) return NULL; - EVP_CIPHER_CTX_init(ctx); if (EVP_DecryptInit_ex(ctx, type, NULL, key, NULL) != 1) { - os_free(ctx); + EVP_CIPHER_CTX_free(ctx); return NULL; } EVP_CIPHER_CTX_set_padding(ctx, 0); @@ -298,8 +360,7 @@ void aes_decrypt_deinit(void *ctx) wpa_printf(MSG_ERROR, "OpenSSL: Unexpected padding length %d " "in AES decrypt", len); } - EVP_CIPHER_CTX_cleanup(c); - bin_clear_free(c, sizeof(*c)); + EVP_CIPHER_CTX_free(c); } @@ -338,51 +399,56 @@ int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher, int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) { - EVP_CIPHER_CTX ctx; + EVP_CIPHER_CTX *ctx; int clen, len; u8 buf[16]; + int res = -1; - EVP_CIPHER_CTX_init(&ctx); - if (EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv) != 1) + if (TEST_FAIL()) return -1; - EVP_CIPHER_CTX_set_padding(&ctx, 0); + ctx = EVP_CIPHER_CTX_new(); + if (!ctx) + return -1; clen = data_len; - if (EVP_EncryptUpdate(&ctx, data, &clen, data, data_len) != 1 || - clen != (int) data_len) - return -1; - len = sizeof(buf); - if (EVP_EncryptFinal_ex(&ctx, buf, &len) != 1 || len != 0) - return -1; - EVP_CIPHER_CTX_cleanup(&ctx); + if (EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv) == 1 && + EVP_CIPHER_CTX_set_padding(ctx, 0) == 1 && + EVP_EncryptUpdate(ctx, data, &clen, data, data_len) == 1 && + clen == (int) data_len && + EVP_EncryptFinal_ex(ctx, buf, &len) == 1 && len == 0) + res = 0; + EVP_CIPHER_CTX_free(ctx); - return 0; + return res; } int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) { - EVP_CIPHER_CTX ctx; + EVP_CIPHER_CTX *ctx; int plen, len; u8 buf[16]; + int res = -1; - EVP_CIPHER_CTX_init(&ctx); - if (EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv) != 1) + if (TEST_FAIL()) return -1; - EVP_CIPHER_CTX_set_padding(&ctx, 0); + ctx = EVP_CIPHER_CTX_new(); + if (!ctx) + return -1; plen = data_len; - if (EVP_DecryptUpdate(&ctx, data, &plen, data, data_len) != 1 || - plen != (int) data_len) - return -1; - len = sizeof(buf); - if (EVP_DecryptFinal_ex(&ctx, buf, &len) != 1 || len != 0) - return -1; - EVP_CIPHER_CTX_cleanup(&ctx); + if (EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv) == 1 && + EVP_CIPHER_CTX_set_padding(ctx, 0) == 1 && + EVP_DecryptUpdate(ctx, data, &plen, data, data_len) == 1 && + plen == (int) data_len && + EVP_DecryptFinal_ex(ctx, buf, &len) == 1 && len == 0) + res = 0; + EVP_CIPHER_CTX_free(ctx); + + return res; - return 0; } @@ -425,8 +491,8 @@ error: struct crypto_cipher { - EVP_CIPHER_CTX enc; - EVP_CIPHER_CTX dec; + EVP_CIPHER_CTX *enc; + EVP_CIPHER_CTX *dec; }; @@ -487,23 +553,25 @@ struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, return NULL; } - EVP_CIPHER_CTX_init(&ctx->enc); - EVP_CIPHER_CTX_set_padding(&ctx->enc, 0); - if (!EVP_EncryptInit_ex(&ctx->enc, cipher, NULL, NULL, NULL) || - !EVP_CIPHER_CTX_set_key_length(&ctx->enc, key_len) || - !EVP_EncryptInit_ex(&ctx->enc, NULL, NULL, key, iv)) { - EVP_CIPHER_CTX_cleanup(&ctx->enc); + if (!(ctx->enc = EVP_CIPHER_CTX_new()) || + !EVP_CIPHER_CTX_set_padding(ctx->enc, 0) || + !EVP_EncryptInit_ex(ctx->enc, cipher, NULL, NULL, NULL) || + !EVP_CIPHER_CTX_set_key_length(ctx->enc, key_len) || + !EVP_EncryptInit_ex(ctx->enc, NULL, NULL, key, iv)) { + if (ctx->enc) + EVP_CIPHER_CTX_free(ctx->enc); os_free(ctx); return NULL; } - EVP_CIPHER_CTX_init(&ctx->dec); - EVP_CIPHER_CTX_set_padding(&ctx->dec, 0); - if (!EVP_DecryptInit_ex(&ctx->dec, cipher, NULL, NULL, NULL) || - !EVP_CIPHER_CTX_set_key_length(&ctx->dec, key_len) || - !EVP_DecryptInit_ex(&ctx->dec, NULL, NULL, key, iv)) { - EVP_CIPHER_CTX_cleanup(&ctx->enc); - EVP_CIPHER_CTX_cleanup(&ctx->dec); + if (!(ctx->dec = EVP_CIPHER_CTX_new()) || + !EVP_CIPHER_CTX_set_padding(ctx->dec, 0) || + !EVP_DecryptInit_ex(ctx->dec, cipher, NULL, NULL, NULL) || + !EVP_CIPHER_CTX_set_key_length(ctx->dec, key_len) || + !EVP_DecryptInit_ex(ctx->dec, NULL, NULL, key, iv)) { + EVP_CIPHER_CTX_free(ctx->enc); + if (ctx->dec) + EVP_CIPHER_CTX_free(ctx->dec); os_free(ctx); return NULL; } @@ -516,7 +584,7 @@ int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, u8 *crypt, size_t len) { int outl; - if (!EVP_EncryptUpdate(&ctx->enc, crypt, &outl, plain, len)) + if (!EVP_EncryptUpdate(ctx->enc, crypt, &outl, plain, len)) return -1; return 0; } @@ -527,7 +595,7 @@ int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, { int outl; outl = len; - if (!EVP_DecryptUpdate(&ctx->dec, plain, &outl, crypt, len)) + if (!EVP_DecryptUpdate(ctx->dec, plain, &outl, crypt, len)) return -1; return 0; } @@ -535,19 +603,21 @@ int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, void crypto_cipher_deinit(struct crypto_cipher *ctx) { - EVP_CIPHER_CTX_cleanup(&ctx->enc); - EVP_CIPHER_CTX_cleanup(&ctx->dec); + EVP_CIPHER_CTX_free(ctx->enc); + EVP_CIPHER_CTX_free(ctx->dec); os_free(ctx); } void * dh5_init(struct wpabuf **priv, struct wpabuf **publ) { +#if OPENSSL_VERSION_NUMBER < 0x10100000L DH *dh; struct wpabuf *pubkey = NULL, *privkey = NULL; size_t publen, privlen; *priv = NULL; + wpabuf_free(*publ); *publ = NULL; dh = DH_new(); @@ -586,11 +656,63 @@ err: wpabuf_clear_free(privkey); DH_free(dh); return NULL; +#else + DH *dh; + struct wpabuf *pubkey = NULL, *privkey = NULL; + size_t publen, privlen; + BIGNUM *p = NULL, *g; + const BIGNUM *priv_key = NULL, *pub_key = NULL; + + *priv = NULL; + wpabuf_free(*publ); + *publ = NULL; + + dh = DH_new(); + if (dh == NULL) + return NULL; + + g = BN_new(); + p = get_group5_prime(); + if (!g || BN_set_word(g, 2) != 1 || !p || + DH_set0_pqg(dh, p, NULL, g) != 1) + goto err; + p = NULL; + g = NULL; + + if (DH_generate_key(dh) != 1) + goto err; + + DH_get0_key(dh, &pub_key, &priv_key); + publen = BN_num_bytes(pub_key); + pubkey = wpabuf_alloc(publen); + if (!pubkey) + goto err; + privlen = BN_num_bytes(priv_key); + privkey = wpabuf_alloc(privlen); + if (!privkey) + goto err; + + BN_bn2bin(pub_key, wpabuf_put(pubkey, publen)); + BN_bn2bin(priv_key, wpabuf_put(privkey, privlen)); + + *priv = privkey; + *publ = pubkey; + return dh; + +err: + BN_free(p); + BN_free(g); + wpabuf_clear_free(pubkey); + wpabuf_clear_free(privkey); + DH_free(dh); + return NULL; +#endif } void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ) { +#if OPENSSL_VERSION_NUMBER < 0x10100000L DH *dh; dh = DH_new(); @@ -621,6 +743,42 @@ void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ) err: DH_free(dh); return NULL; +#else + DH *dh; + BIGNUM *p = NULL, *g, *priv_key = NULL, *pub_key = NULL; + + dh = DH_new(); + if (dh == NULL) + return NULL; + + g = BN_new(); + p = get_group5_prime(); + if (!g || BN_set_word(g, 2) != 1 || !p || + DH_set0_pqg(dh, p, NULL, g) != 1) + goto err; + p = NULL; + g = NULL; + + priv_key = BN_bin2bn(wpabuf_head(priv), wpabuf_len(priv), NULL); + pub_key = BN_bin2bn(wpabuf_head(publ), wpabuf_len(publ), NULL); + if (!priv_key || !pub_key || DH_set0_key(dh, pub_key, priv_key) != 1) + goto err; + pub_key = NULL; + priv_key = NULL; + + if (DH_generate_key(dh) != 1) + goto err; + + return dh; + +err: + BN_free(p); + BN_free(g); + BN_free(pub_key); + BN_clear_free(priv_key); + DH_free(dh); + return NULL; +#endif } @@ -672,7 +830,7 @@ void dh5_free(void *ctx) struct crypto_hash { - HMAC_CTX ctx; + HMAC_CTX *ctx; }; @@ -707,16 +865,17 @@ struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, ctx = os_zalloc(sizeof(*ctx)); if (ctx == NULL) return NULL; - HMAC_CTX_init(&ctx->ctx); + ctx->ctx = HMAC_CTX_new(); + if (!ctx->ctx) { + os_free(ctx); + return NULL; + } -#if OPENSSL_VERSION_NUMBER < 0x00909000 - HMAC_Init_ex(&ctx->ctx, key, key_len, md, NULL); -#else /* openssl < 0.9.9 */ - if (HMAC_Init_ex(&ctx->ctx, key, key_len, md, NULL) != 1) { + if (HMAC_Init_ex(ctx->ctx, key, key_len, md, NULL) != 1) { + HMAC_CTX_free(ctx->ctx); bin_clear_free(ctx, sizeof(*ctx)); return NULL; } -#endif /* openssl < 0.9.9 */ return ctx; } @@ -726,7 +885,7 @@ void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len) { if (ctx == NULL) return; - HMAC_Update(&ctx->ctx, data, len); + HMAC_Update(ctx->ctx, data, len); } @@ -739,18 +898,14 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) return -2; if (mac == NULL || len == NULL) { + HMAC_CTX_free(ctx->ctx); bin_clear_free(ctx, sizeof(*ctx)); return 0; } mdlen = *len; -#if OPENSSL_VERSION_NUMBER < 0x00909000 - HMAC_Final(&ctx->ctx, mac, &mdlen); - res = 1; -#else /* openssl < 0.9.9 */ - res = HMAC_Final(&ctx->ctx, mac, &mdlen); -#endif /* openssl < 0.9.9 */ - HMAC_CTX_cleanup(&ctx->ctx); + res = HMAC_Final(ctx->ctx, mac, &mdlen); + HMAC_CTX_free(ctx->ctx); bin_clear_free(ctx, sizeof(*ctx)); if (res == 1) { @@ -767,28 +922,26 @@ static int openssl_hmac_vector(const EVP_MD *type, const u8 *key, const u8 *addr[], const size_t *len, u8 *mac, unsigned int mdlen) { - HMAC_CTX ctx; + HMAC_CTX *ctx; size_t i; int res; - HMAC_CTX_init(&ctx); -#if OPENSSL_VERSION_NUMBER < 0x00909000 - HMAC_Init_ex(&ctx, key, key_len, type, NULL); -#else /* openssl < 0.9.9 */ - if (HMAC_Init_ex(&ctx, key, key_len, type, NULL) != 1) + if (TEST_FAIL()) return -1; -#endif /* openssl < 0.9.9 */ + + ctx = HMAC_CTX_new(); + if (!ctx) + return -1; + res = HMAC_Init_ex(ctx, key, key_len, type, NULL); + if (res != 1) + goto done; for (i = 0; i < num_elem; i++) - HMAC_Update(&ctx, addr[i], len[i]); + HMAC_Update(ctx, addr[i], len[i]); -#if OPENSSL_VERSION_NUMBER < 0x00909000 - HMAC_Final(&ctx, mac, &mdlen); - res = 1; -#else /* openssl < 0.9.9 */ - res = HMAC_Final(&ctx, mac, &mdlen); -#endif /* openssl < 0.9.9 */ - HMAC_CTX_cleanup(&ctx); + res = HMAC_Final(ctx, mac, &mdlen); +done: + HMAC_CTX_free(ctx); return res == 1 ? 0 : -1; } @@ -892,6 +1045,9 @@ int omac1_aes_vector(const u8 *key, size_t key_len, size_t num_elem, int ret = -1; size_t outlen, i; + if (TEST_FAIL()) + return -1; + ctx = CMAC_CTX_new(); if (ctx == NULL) return -1; @@ -941,13 +1097,20 @@ int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac) struct crypto_bignum * crypto_bignum_init(void) { + if (TEST_FAIL()) + return NULL; return (struct crypto_bignum *) BN_new(); } struct crypto_bignum * crypto_bignum_init_set(const u8 *buf, size_t len) { - BIGNUM *bn = BN_bin2bn(buf, len, NULL); + BIGNUM *bn; + + if (TEST_FAIL()) + return NULL; + + bn = BN_bin2bn(buf, len, NULL); return (struct crypto_bignum *) bn; } @@ -966,6 +1129,9 @@ int crypto_bignum_to_bin(const struct crypto_bignum *a, { int num_bytes, offset; + if (TEST_FAIL()) + return -1; + if (padlen > buflen) return -1; @@ -1019,6 +1185,9 @@ int crypto_bignum_exptmod(const struct crypto_bignum *a, int res; BN_CTX *bnctx; + if (TEST_FAIL()) + return -1; + bnctx = BN_CTX_new(); if (bnctx == NULL) return -1; @@ -1037,6 +1206,8 @@ int crypto_bignum_inverse(const struct crypto_bignum *a, BIGNUM *res; BN_CTX *bnctx; + if (TEST_FAIL()) + return -1; bnctx = BN_CTX_new(); if (bnctx == NULL) return -1; @@ -1052,6 +1223,8 @@ int crypto_bignum_sub(const struct crypto_bignum *a, const struct crypto_bignum *b, struct crypto_bignum *c) { + if (TEST_FAIL()) + return -1; return BN_sub((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b) ? 0 : -1; } @@ -1065,6 +1238,9 @@ int crypto_bignum_div(const struct crypto_bignum *a, BN_CTX *bnctx; + if (TEST_FAIL()) + return -1; + bnctx = BN_CTX_new(); if (bnctx == NULL) return -1; @@ -1085,6 +1261,9 @@ int crypto_bignum_mulmod(const struct crypto_bignum *a, BN_CTX *bnctx; + if (TEST_FAIL()) + return -1; + bnctx = BN_CTX_new(); if (bnctx == NULL) return -1; @@ -1128,6 +1307,9 @@ int crypto_bignum_legendre(const struct crypto_bignum *a, BIGNUM *exp = NULL, *tmp = NULL; int res = -2; + if (TEST_FAIL()) + return -2; + bnctx = BN_CTX_new(); if (bnctx == NULL) return -2; @@ -1252,6 +1434,8 @@ void crypto_ec_deinit(struct crypto_ec *e) struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e) { + if (TEST_FAIL()) + return NULL; if (e == NULL) return NULL; return (struct crypto_ec_point *) EC_POINT_new(e->group); @@ -1298,6 +1482,9 @@ int crypto_ec_point_to_bin(struct crypto_ec *e, int ret = -1; int len = BN_num_bytes(e->prime); + if (TEST_FAIL()) + return -1; + x_bn = BN_new(); y_bn = BN_new(); @@ -1328,6 +1515,9 @@ struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e, EC_POINT *elem; int len = BN_num_bytes(e->prime); + if (TEST_FAIL()) + return NULL; + x = BN_bin2bn(val, len, NULL); y = BN_bin2bn(val + len, len, NULL); elem = EC_POINT_new(e->group); @@ -1355,6 +1545,8 @@ int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a, const struct crypto_ec_point *b, struct crypto_ec_point *c) { + if (TEST_FAIL()) + return -1; return EC_POINT_add(e->group, (EC_POINT *) c, (const EC_POINT *) a, (const EC_POINT *) b, e->bnctx) ? 0 : -1; } @@ -1364,6 +1556,8 @@ int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p, const struct crypto_bignum *b, struct crypto_ec_point *res) { + if (TEST_FAIL()) + return -1; return EC_POINT_mul(e->group, (EC_POINT *) res, NULL, (const EC_POINT *) p, (const BIGNUM *) b, e->bnctx) ? 0 : -1; @@ -1372,6 +1566,8 @@ int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p, int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p) { + if (TEST_FAIL()) + return -1; return EC_POINT_invert(e->group, (EC_POINT *) p, e->bnctx) ? 0 : -1; } @@ -1380,6 +1576,8 @@ int crypto_ec_point_solve_y_coord(struct crypto_ec *e, struct crypto_ec_point *p, const struct crypto_bignum *x, int y_bit) { + if (TEST_FAIL()) + return -1; if (!EC_POINT_set_compressed_coordinates_GFp(e->group, (EC_POINT *) p, (const BIGNUM *) x, y_bit, e->bnctx) || @@ -1395,6 +1593,9 @@ crypto_ec_point_compute_y_sqr(struct crypto_ec *e, { BIGNUM *tmp, *tmp2, *y_sqr = NULL; + if (TEST_FAIL()) + return NULL; + tmp = BN_new(); tmp2 = BN_new(); diff --git a/contrib/wpa/src/crypto/dh_group5.c b/contrib/wpa/src/crypto/dh_group5.c index ccdbfc812958..425c848acb83 100644 --- a/contrib/wpa/src/crypto/dh_group5.c +++ b/contrib/wpa/src/crypto/dh_group5.c @@ -15,6 +15,7 @@ void * dh5_init(struct wpabuf **priv, struct wpabuf **publ) { + wpabuf_free(*publ); *publ = dh_init(dh_groups_get(5), priv); if (*publ == NULL) return NULL; diff --git a/contrib/wpa/src/crypto/dh_groups.c b/contrib/wpa/src/crypto/dh_groups.c index 3aeb2bbc60af..7912361ff8c6 100644 --- a/contrib/wpa/src/crypto/dh_groups.c +++ b/contrib/wpa/src/crypto/dh_groups.c @@ -1218,14 +1218,19 @@ struct wpabuf * dh_init(const struct dh_group *dh, struct wpabuf **priv) pv_len = dh->prime_len; pv = wpabuf_alloc(pv_len); - if (pv == NULL) + if (pv == NULL) { + wpabuf_clear_free(*priv); + *priv = NULL; return NULL; + } if (crypto_mod_exp(dh->generator, dh->generator_len, wpabuf_head(*priv), wpabuf_len(*priv), dh->prime, dh->prime_len, wpabuf_mhead(pv), &pv_len) < 0) { wpabuf_clear_free(pv); wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed"); + wpabuf_clear_free(*priv); + *priv = NULL; return NULL; } wpabuf_put(pv, pv_len); diff --git a/contrib/wpa/src/crypto/fips_prf_openssl.c b/contrib/wpa/src/crypto/fips_prf_openssl.c index fb03efcd4ffc..4697e041093a 100644 --- a/contrib/wpa/src/crypto/fips_prf_openssl.c +++ b/contrib/wpa/src/crypto/fips_prf_openssl.c @@ -17,6 +17,19 @@ static void sha1_transform(u32 *state, const u8 data[64]) { SHA_CTX context; os_memset(&context, 0, sizeof(context)); +#if defined(OPENSSL_IS_BORINGSSL) && !defined(ANDROID) + context.h[0] = state[0]; + context.h[1] = state[1]; + context.h[2] = state[2]; + context.h[3] = state[3]; + context.h[4] = state[4]; + SHA1_Transform(&context, data); + state[0] = context.h[0]; + state[1] = context.h[1]; + state[2] = context.h[2]; + state[3] = context.h[3]; + state[4] = context.h[4]; +#else context.h0 = state[0]; context.h1 = state[1]; context.h2 = state[2]; @@ -28,6 +41,7 @@ static void sha1_transform(u32 *state, const u8 data[64]) state[2] = context.h2; state[3] = context.h3; state[4] = context.h4; +#endif } @@ -62,12 +76,11 @@ int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen) /* w_i = G(t, XVAL) */ os_memcpy(_t, t, 20); sha1_transform(_t, xkey); - _t[0] = host_to_be32(_t[0]); - _t[1] = host_to_be32(_t[1]); - _t[2] = host_to_be32(_t[2]); - _t[3] = host_to_be32(_t[3]); - _t[4] = host_to_be32(_t[4]); - os_memcpy(xpos, _t, 20); + WPA_PUT_BE32(xpos, _t[0]); + WPA_PUT_BE32(xpos + 4, _t[1]); + WPA_PUT_BE32(xpos + 8, _t[2]); + WPA_PUT_BE32(xpos + 12, _t[3]); + WPA_PUT_BE32(xpos + 16, _t[4]); /* XKEY = (1 + XKEY + w_i) mod 2^b */ carry = 1; diff --git a/contrib/wpa/src/crypto/md4-internal.c b/contrib/wpa/src/crypto/md4-internal.c index cd5e6ca8cc88..d9c737a2970b 100644 --- a/contrib/wpa/src/crypto/md4-internal.c +++ b/contrib/wpa/src/crypto/md4-internal.c @@ -31,6 +31,9 @@ int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) MD4_CTX ctx; size_t i; + if (TEST_FAIL()) + return -1; + MD4Init(&ctx); for (i = 0; i < num_elem; i++) MD4Update(&ctx, addr[i], len[i]); diff --git a/contrib/wpa/src/crypto/md5-internal.c b/contrib/wpa/src/crypto/md5-internal.c index f0a2a5d3a5af..944698a6328b 100644 --- a/contrib/wpa/src/crypto/md5-internal.c +++ b/contrib/wpa/src/crypto/md5-internal.c @@ -33,6 +33,9 @@ int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) MD5_CTX ctx; size_t i; + if (TEST_FAIL()) + return -1; + MD5Init(&ctx); for (i = 0; i < num_elem; i++) MD5Update(&ctx, addr[i], len[i]); diff --git a/contrib/wpa/src/crypto/ms_funcs.c b/contrib/wpa/src/crypto/ms_funcs.c index 053d203cb65b..d0d6a96af2bc 100644 --- a/contrib/wpa/src/crypto/ms_funcs.c +++ b/contrib/wpa/src/crypto/ms_funcs.c @@ -48,7 +48,7 @@ static int utf8_to_ucs2(const u8 *utf8_string, size_t utf8_string_len, WPA_PUT_LE16(ucs2_buffer + j, ((c & 0x1F) << 6) | (c2 & 0x3F)); j += 2; - } else if (i == utf8_string_len || + } else if (i == utf8_string_len - 1 || j >= ucs2_buffer_size - 1) { /* incomplete surrogate */ return -1; diff --git a/contrib/wpa/src/crypto/sha1-internal.c b/contrib/wpa/src/crypto/sha1-internal.c index 24bc3ffe1759..ffcba66af652 100644 --- a/contrib/wpa/src/crypto/sha1-internal.c +++ b/contrib/wpa/src/crypto/sha1-internal.c @@ -33,6 +33,9 @@ int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) SHA1_CTX ctx; size_t i; + if (TEST_FAIL()) + return -1; + SHA1Init(&ctx); for (i = 0; i < num_elem; i++) SHA1Update(&ctx, addr[i], len[i]); @@ -294,7 +297,6 @@ void SHA1Final(unsigned char digest[20], SHA1_CTX* context) 255); } /* Wipe variables */ - i = 0; os_memset(context->buffer, 0, 64); os_memset(context->state, 0, 20); os_memset(context->count, 0, 8); diff --git a/contrib/wpa/src/crypto/sha256-internal.c b/contrib/wpa/src/crypto/sha256-internal.c index 35299b0524bd..86a548ee472d 100644 --- a/contrib/wpa/src/crypto/sha256-internal.c +++ b/contrib/wpa/src/crypto/sha256-internal.c @@ -28,6 +28,9 @@ int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, struct sha256_state ctx; size_t i; + if (TEST_FAIL()) + return -1; + sha256_init(&ctx); for (i = 0; i < num_elem; i++) if (sha256_process(&ctx, addr[i], len[i])) diff --git a/contrib/wpa/src/crypto/sha256-prf.c b/contrib/wpa/src/crypto/sha256-prf.c index 79791c06cf0b..722cad6bdeb4 100644 --- a/contrib/wpa/src/crypto/sha256-prf.c +++ b/contrib/wpa/src/crypto/sha256-prf.c @@ -1,6 +1,6 @@ /* * SHA256-based PRF (IEEE 802.11r) - * Copyright (c) 2003-2013, Jouni Malinen + * Copyright (c) 2003-2016, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -22,14 +22,16 @@ * @data_len: Length of the data * @buf: Buffer for the generated pseudo-random key * @buf_len: Number of bytes of key to generate + * Returns: 0 on success, -1 on failure * * This function is used to derive new, cryptographically separate keys from a * given key. */ -void sha256_prf(const u8 *key, size_t key_len, const char *label, +int sha256_prf(const u8 *key, size_t key_len, const char *label, const u8 *data, size_t data_len, u8 *buf, size_t buf_len) { - sha256_prf_bits(key, key_len, label, data, data_len, buf, buf_len * 8); + return sha256_prf_bits(key, key_len, label, data, data_len, buf, + buf_len * 8); } @@ -42,15 +44,16 @@ void sha256_prf(const u8 *key, size_t key_len, const char *label, * @data_len: Length of the data * @buf: Buffer for the generated pseudo-random key * @buf_len: Number of bits of key to generate + * Returns: 0 on success, -1 on failure * * This function is used to derive new, cryptographically separate keys from a * given key. If the requested buf_len is not divisible by eight, the least * significant 1-7 bits of the last octet in the output are not part of the * requested output. */ -void sha256_prf_bits(const u8 *key, size_t key_len, const char *label, - const u8 *data, size_t data_len, u8 *buf, - size_t buf_len_bits) +int sha256_prf_bits(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, + size_t buf_len_bits) { u16 counter = 1; size_t pos, plen; @@ -75,11 +78,14 @@ void sha256_prf_bits(const u8 *key, size_t key_len, const char *label, plen = buf_len - pos; WPA_PUT_LE16(counter_le, counter); if (plen >= SHA256_MAC_LEN) { - hmac_sha256_vector(key, key_len, 4, addr, len, - &buf[pos]); + if (hmac_sha256_vector(key, key_len, 4, addr, len, + &buf[pos]) < 0) + return -1; pos += SHA256_MAC_LEN; } else { - hmac_sha256_vector(key, key_len, 4, addr, len, hash); + if (hmac_sha256_vector(key, key_len, 4, addr, len, + hash) < 0) + return -1; os_memcpy(&buf[pos], hash, plen); pos += plen; break; @@ -97,4 +103,6 @@ void sha256_prf_bits(const u8 *key, size_t key_len, const char *label, } os_memset(hash, 0, sizeof(hash)); + + return 0; } diff --git a/contrib/wpa/src/crypto/sha256.h b/contrib/wpa/src/crypto/sha256.h index b15f51158f32..5219022edd7d 100644 --- a/contrib/wpa/src/crypto/sha256.h +++ b/contrib/wpa/src/crypto/sha256.h @@ -1,6 +1,6 @@ /* * SHA256 hash implementation and interface functions - * Copyright (c) 2003-2014, Jouni Malinen + * Copyright (c) 2003-2016, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -15,11 +15,11 @@ int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *mac); -void sha256_prf(const u8 *key, size_t key_len, const char *label, - const u8 *data, size_t data_len, u8 *buf, size_t buf_len); -void sha256_prf_bits(const u8 *key, size_t key_len, const char *label, - const u8 *data, size_t data_len, u8 *buf, - size_t buf_len_bits); +int sha256_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len); +int sha256_prf_bits(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, + size_t buf_len_bits); void tls_prf_sha256(const u8 *secret, size_t secret_len, const char *label, const u8 *seed, size_t seed_len, u8 *out, size_t outlen); diff --git a/contrib/wpa/src/crypto/sha384-internal.c b/contrib/wpa/src/crypto/sha384-internal.c new file mode 100644 index 000000000000..646f72979c2a --- /dev/null +++ b/contrib/wpa/src/crypto/sha384-internal.c @@ -0,0 +1,92 @@ +/* + * SHA-384 hash implementation and interface functions + * Copyright (c) 2015, Pali Rohár + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha384_i.h" +#include "crypto.h" + + +/** + * sha384_vector - SHA384 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + * Returns: 0 on success, -1 of failure + */ +int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + struct sha384_state ctx; + size_t i; + + sha384_init(&ctx); + for (i = 0; i < num_elem; i++) + if (sha384_process(&ctx, addr[i], len[i])) + return -1; + if (sha384_done(&ctx, mac)) + return -1; + return 0; +} + + +/* ===== start - public domain SHA384 implementation ===== */ + +/* This is based on SHA384 implementation in LibTomCrypt that was released into + * public domain by Tom St Denis. */ + +#define CONST64(n) n ## ULL + +/** + Initialize the hash state + @param md The hash state you wish to initialize + @return CRYPT_OK if successful +*/ +void sha384_init(struct sha384_state *md) +{ + md->curlen = 0; + md->length = 0; + md->state[0] = CONST64(0xcbbb9d5dc1059ed8); + md->state[1] = CONST64(0x629a292a367cd507); + md->state[2] = CONST64(0x9159015a3070dd17); + md->state[3] = CONST64(0x152fecd8f70e5939); + md->state[4] = CONST64(0x67332667ffc00b31); + md->state[5] = CONST64(0x8eb44a8768581511); + md->state[6] = CONST64(0xdb0c2e0d64f98fa7); + md->state[7] = CONST64(0x47b5481dbefa4fa4); +} + +int sha384_process(struct sha384_state *md, const unsigned char *in, + unsigned long inlen) +{ + return sha512_process(md, in, inlen); +} + +/** + Terminate the hash to get the digest + @param md The hash state + @param out [out] The destination of the hash (48 bytes) + @return CRYPT_OK if successful +*/ +int sha384_done(struct sha384_state *md, unsigned char *out) +{ + unsigned char buf[64]; + + if (md->curlen >= sizeof(md->buf)) + return -1; + + if (sha512_done(md, buf) != 0) + return -1; + + os_memcpy(out, buf, 48); + return 0; +} + +/* ===== end - public domain SHA384 implementation ===== */ diff --git a/contrib/wpa/src/crypto/sha384_i.h b/contrib/wpa/src/crypto/sha384_i.h new file mode 100644 index 000000000000..a00253ff2cd0 --- /dev/null +++ b/contrib/wpa/src/crypto/sha384_i.h @@ -0,0 +1,23 @@ +/* + * SHA-384 internal definitions + * Copyright (c) 2015, Pali Rohár + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef SHA384_I_H +#define SHA384_I_H + +#include "sha512_i.h" + +#define SHA384_BLOCK_SIZE SHA512_BLOCK_SIZE + +#define sha384_state sha512_state + +void sha384_init(struct sha384_state *md); +int sha384_process(struct sha384_state *md, const unsigned char *in, + unsigned long inlen); +int sha384_done(struct sha384_state *md, unsigned char *out); + +#endif /* SHA384_I_H */ diff --git a/contrib/wpa/src/crypto/sha512-internal.c b/contrib/wpa/src/crypto/sha512-internal.c new file mode 100644 index 000000000000..76c4fe750b65 --- /dev/null +++ b/contrib/wpa/src/crypto/sha512-internal.c @@ -0,0 +1,264 @@ +/* + * SHA-512 hash implementation and interface functions + * Copyright (c) 2015, Pali Rohár + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha512_i.h" +#include "crypto.h" + + +/** + * sha512_vector - SHA512 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + * Returns: 0 on success, -1 of failure + */ +int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + struct sha512_state ctx; + size_t i; + + sha512_init(&ctx); + for (i = 0; i < num_elem; i++) + if (sha512_process(&ctx, addr[i], len[i])) + return -1; + if (sha512_done(&ctx, mac)) + return -1; + return 0; +} + + +/* ===== start - public domain SHA512 implementation ===== */ + +/* This is based on SHA512 implementation in LibTomCrypt that was released into + * public domain by Tom St Denis. */ + +#define CONST64(n) n ## ULL + +/* the K array */ +static const u64 K[80] = { + CONST64(0x428a2f98d728ae22), CONST64(0x7137449123ef65cd), + CONST64(0xb5c0fbcfec4d3b2f), CONST64(0xe9b5dba58189dbbc), + CONST64(0x3956c25bf348b538), CONST64(0x59f111f1b605d019), + CONST64(0x923f82a4af194f9b), CONST64(0xab1c5ed5da6d8118), + CONST64(0xd807aa98a3030242), CONST64(0x12835b0145706fbe), + CONST64(0x243185be4ee4b28c), CONST64(0x550c7dc3d5ffb4e2), + CONST64(0x72be5d74f27b896f), CONST64(0x80deb1fe3b1696b1), + CONST64(0x9bdc06a725c71235), CONST64(0xc19bf174cf692694), + CONST64(0xe49b69c19ef14ad2), CONST64(0xefbe4786384f25e3), + CONST64(0x0fc19dc68b8cd5b5), CONST64(0x240ca1cc77ac9c65), + CONST64(0x2de92c6f592b0275), CONST64(0x4a7484aa6ea6e483), + CONST64(0x5cb0a9dcbd41fbd4), CONST64(0x76f988da831153b5), + CONST64(0x983e5152ee66dfab), CONST64(0xa831c66d2db43210), + CONST64(0xb00327c898fb213f), CONST64(0xbf597fc7beef0ee4), + CONST64(0xc6e00bf33da88fc2), CONST64(0xd5a79147930aa725), + CONST64(0x06ca6351e003826f), CONST64(0x142929670a0e6e70), + CONST64(0x27b70a8546d22ffc), CONST64(0x2e1b21385c26c926), + CONST64(0x4d2c6dfc5ac42aed), CONST64(0x53380d139d95b3df), + CONST64(0x650a73548baf63de), CONST64(0x766a0abb3c77b2a8), + CONST64(0x81c2c92e47edaee6), CONST64(0x92722c851482353b), + CONST64(0xa2bfe8a14cf10364), CONST64(0xa81a664bbc423001), + CONST64(0xc24b8b70d0f89791), CONST64(0xc76c51a30654be30), + CONST64(0xd192e819d6ef5218), CONST64(0xd69906245565a910), + CONST64(0xf40e35855771202a), CONST64(0x106aa07032bbd1b8), + CONST64(0x19a4c116b8d2d0c8), CONST64(0x1e376c085141ab53), + CONST64(0x2748774cdf8eeb99), CONST64(0x34b0bcb5e19b48a8), + CONST64(0x391c0cb3c5c95a63), CONST64(0x4ed8aa4ae3418acb), + CONST64(0x5b9cca4f7763e373), CONST64(0x682e6ff3d6b2b8a3), + CONST64(0x748f82ee5defb2fc), CONST64(0x78a5636f43172f60), + CONST64(0x84c87814a1f0ab72), CONST64(0x8cc702081a6439ec), + CONST64(0x90befffa23631e28), CONST64(0xa4506cebde82bde9), + CONST64(0xbef9a3f7b2c67915), CONST64(0xc67178f2e372532b), + CONST64(0xca273eceea26619c), CONST64(0xd186b8c721c0c207), + CONST64(0xeada7dd6cde0eb1e), CONST64(0xf57d4f7fee6ed178), + CONST64(0x06f067aa72176fba), CONST64(0x0a637dc5a2c898a6), + CONST64(0x113f9804bef90dae), CONST64(0x1b710b35131c471b), + CONST64(0x28db77f523047d84), CONST64(0x32caab7b40c72493), + CONST64(0x3c9ebe0a15c9bebc), CONST64(0x431d67c49c100d4c), + CONST64(0x4cc5d4becb3e42b6), CONST64(0x597f299cfc657e2a), + CONST64(0x5fcb6fab3ad6faec), CONST64(0x6c44198c4a475817) +}; + +/* Various logical functions */ +#define Ch(x,y,z) (z ^ (x & (y ^ z))) +#define Maj(x,y,z) (((x | y) & z) | (x & y)) +#define S(x, n) ROR64c(x, n) +#define R(x, n) (((x) & CONST64(0xFFFFFFFFFFFFFFFF)) >> ((u64) n)) +#define Sigma0(x) (S(x, 28) ^ S(x, 34) ^ S(x, 39)) +#define Sigma1(x) (S(x, 14) ^ S(x, 18) ^ S(x, 41)) +#define Gamma0(x) (S(x, 1) ^ S(x, 8) ^ R(x, 7)) +#define Gamma1(x) (S(x, 19) ^ S(x, 61) ^ R(x, 6)) +#ifndef MIN +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) +#endif + +#define ROR64c(x, y) \ + ( ((((x) & CONST64(0xFFFFFFFFFFFFFFFF)) >> ((u64) (y) & CONST64(63))) | \ + ((x) << ((u64) (64 - ((y) & CONST64(63)))))) & \ + CONST64(0xFFFFFFFFFFFFFFFF)) + +/* compress 1024-bits */ +static int sha512_compress(struct sha512_state *md, unsigned char *buf) +{ + u64 S[8], W[80], t0, t1; + int i; + + /* copy state into S */ + for (i = 0; i < 8; i++) { + S[i] = md->state[i]; + } + + /* copy the state into 1024-bits into W[0..15] */ + for (i = 0; i < 16; i++) + W[i] = WPA_GET_BE64(buf + (8 * i)); + + /* fill W[16..79] */ + for (i = 16; i < 80; i++) { + W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + + W[i - 16]; + } + + /* Compress */ + for (i = 0; i < 80; i++) { + t0 = S[7] + Sigma1(S[4]) + Ch(S[4], S[5], S[6]) + K[i] + W[i]; + t1 = Sigma0(S[0]) + Maj(S[0], S[1], S[2]); + S[7] = S[6]; + S[6] = S[5]; + S[5] = S[4]; + S[4] = S[3] + t0; + S[3] = S[2]; + S[2] = S[1]; + S[1] = S[0]; + S[0] = t0 + t1; + } + + /* feedback */ + for (i = 0; i < 8; i++) { + md->state[i] = md->state[i] + S[i]; + } + + return 0; +} + + +/** + Initialize the hash state + @param md The hash state you wish to initialize + @return CRYPT_OK if successful +*/ +void sha512_init(struct sha512_state *md) +{ + md->curlen = 0; + md->length = 0; + md->state[0] = CONST64(0x6a09e667f3bcc908); + md->state[1] = CONST64(0xbb67ae8584caa73b); + md->state[2] = CONST64(0x3c6ef372fe94f82b); + md->state[3] = CONST64(0xa54ff53a5f1d36f1); + md->state[4] = CONST64(0x510e527fade682d1); + md->state[5] = CONST64(0x9b05688c2b3e6c1f); + md->state[6] = CONST64(0x1f83d9abfb41bd6b); + md->state[7] = CONST64(0x5be0cd19137e2179); +} + + +/** + Process a block of memory though the hash + @param md The hash state + @param in The data to hash + @param inlen The length of the data (octets) + @return CRYPT_OK if successful +*/ +int sha512_process(struct sha512_state *md, const unsigned char *in, + unsigned long inlen) +{ + unsigned long n; + + if (md->curlen >= sizeof(md->buf)) + return -1; + + while (inlen > 0) { + if (md->curlen == 0 && inlen >= SHA512_BLOCK_SIZE) { + if (sha512_compress(md, (unsigned char *) in) < 0) + return -1; + md->length += SHA512_BLOCK_SIZE * 8; + in += SHA512_BLOCK_SIZE; + inlen -= SHA512_BLOCK_SIZE; + } else { + n = MIN(inlen, (SHA512_BLOCK_SIZE - md->curlen)); + os_memcpy(md->buf + md->curlen, in, n); + md->curlen += n; + in += n; + inlen -= n; + if (md->curlen == SHA512_BLOCK_SIZE) { + if (sha512_compress(md, md->buf) < 0) + return -1; + md->length += 8 * SHA512_BLOCK_SIZE; + md->curlen = 0; + } + } + } + + return 0; +} + + +/** + Terminate the hash to get the digest + @param md The hash state + @param out [out] The destination of the hash (64 bytes) + @return CRYPT_OK if successful +*/ +int sha512_done(struct sha512_state *md, unsigned char *out) +{ + int i; + + if (md->curlen >= sizeof(md->buf)) + return -1; + + /* increase the length of the message */ + md->length += md->curlen * CONST64(8); + + /* append the '1' bit */ + md->buf[md->curlen++] = (unsigned char) 0x80; + + /* if the length is currently above 112 bytes we append zeros + * then compress. Then we can fall back to padding zeros and length + * encoding like normal. + */ + if (md->curlen > 112) { + while (md->curlen < 128) { + md->buf[md->curlen++] = (unsigned char) 0; + } + sha512_compress(md, md->buf); + md->curlen = 0; + } + + /* pad up to 120 bytes of zeroes + * note: that from 112 to 120 is the 64 MSB of the length. We assume + * that you won't hash > 2^64 bits of data... :-) + */ + while (md->curlen < 120) { + md->buf[md->curlen++] = (unsigned char) 0; + } + + /* store length */ + WPA_PUT_BE64(md->buf + 120, md->length); + sha512_compress(md, md->buf); + + /* copy output */ + for (i = 0; i < 8; i++) + WPA_PUT_BE64(out + (8 * i), md->state[i]); + + return 0; +} + +/* ===== end - public domain SHA512 implementation ===== */ diff --git a/contrib/wpa/src/crypto/sha512_i.h b/contrib/wpa/src/crypto/sha512_i.h new file mode 100644 index 000000000000..108958911ef1 --- /dev/null +++ b/contrib/wpa/src/crypto/sha512_i.h @@ -0,0 +1,25 @@ +/* + * SHA-512 internal definitions + * Copyright (c) 2015, Pali Rohár + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef SHA512_I_H +#define SHA512_I_H + +#define SHA512_BLOCK_SIZE 128 + +struct sha512_state { + u64 length, state[8]; + u32 curlen; + u8 buf[SHA512_BLOCK_SIZE]; +}; + +void sha512_init(struct sha512_state *md); +int sha512_process(struct sha512_state *md, const unsigned char *in, + unsigned long inlen); +int sha512_done(struct sha512_state *md, unsigned char *out); + +#endif /* SHA512_I_H */ diff --git a/contrib/wpa/src/crypto/tls.h b/contrib/wpa/src/crypto/tls.h index 2e562339cc5c..11d504a97fc0 100644 --- a/contrib/wpa/src/crypto/tls.h +++ b/contrib/wpa/src/crypto/tls.h @@ -95,6 +95,8 @@ struct tls_config { #define TLS_CONN_DISABLE_TLSv1_2 BIT(6) #define TLS_CONN_EAP_FAST BIT(7) #define TLS_CONN_DISABLE_TLSv1_0 BIT(8) +#define TLS_CONN_EXT_CERT_CHECK BIT(9) +#define TLS_CONN_REQUIRE_OCSP_ALL BIT(10) /** * struct tls_connection_params - Parameters for TLS connection @@ -139,6 +141,9 @@ struct tls_config { * @flags: Parameter options (TLS_CONN_*) * @ocsp_stapling_response: DER encoded file with cached OCSP stapling response * or %NULL if OCSP is not enabled + * @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 * * TLS connection parameters to be configured with tls_connection_set_params() * and tls_global_set_params(). @@ -179,6 +184,7 @@ struct tls_connection_params { unsigned int flags; const char *ocsp_stapling_response; + const char *ocsp_stapling_response_multi; }; @@ -330,29 +336,36 @@ int __must_check tls_connection_get_random(void *tls_ctx, struct tls_random *data); /** - * tls_connection_prf - Use TLS-PRF to derive keying material + * tls_connection_export_key - Derive keying material from a TLS connection * @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 - * @server_random_first: seed is 0 = client_random|server_random, - * 1 = server_random|client_random - * @skip_keyblock: Skip TLS key block from the beginning of PRF output * @out: Buffer for output data from TLS-PRF * @out_len: Length of the output buffer * Returns: 0 on success, -1 on failure * - * tls_connection_prf() is required so that further keying material can be - * derived from the master secret. Example implementation of this function is in - * tls_prf_sha1_md5() when it is called with seed set to - * client_random|server_random (or server_random|client_random). For TLSv1.2 and - * newer, a different PRF is needed, though. + * Exports keying material using the mechanism described in RFC 5705. */ -int __must_check tls_connection_prf(void *tls_ctx, - struct tls_connection *conn, - const char *label, - int server_random_first, - int skip_keyblock, - u8 *out, size_t out_len); +int __must_check tls_connection_export_key(void *tls_ctx, + struct tls_connection *conn, + const char *label, + u8 *out, size_t out_len); + +/** + * tls_connection_get_eap_fast_key - Derive key material for EAP-FAST + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @out: Buffer for output data from TLS-PRF + * @out_len: Length of the output buffer + * Returns: 0 on success, -1 on failure + * + * Exports key material after the normal TLS key block for use with + * EAP-FAST. Most callers will want tls_connection_export_key(), but EAP-FAST + * uses a different legacy mechanism. + */ +int __must_check tls_connection_get_eap_fast_key(void *tls_ctx, + struct tls_connection *conn, + u8 *out, size_t out_len); /** * tls_connection_handshake - Process TLS handshake (client side) @@ -455,7 +468,9 @@ enum { TLS_CIPHER_RC4_SHA /* 0x0005 */, TLS_CIPHER_AES128_SHA /* 0x002f */, TLS_CIPHER_RSA_DHE_AES128_SHA /* 0x0031 */, - TLS_CIPHER_ANON_DH_AES128_SHA /* 0x0034 */ + TLS_CIPHER_ANON_DH_AES128_SHA /* 0x0034 */, + TLS_CIPHER_RSA_DHE_AES256_SHA /* 0x0039 */, + TLS_CIPHER_AES256_SHA /* 0x0035 */, }; /** diff --git a/contrib/wpa/src/crypto/tls_gnutls.c b/contrib/wpa/src/crypto/tls_gnutls.c index f994379b16b2..200f0eda931a 100644 --- a/contrib/wpa/src/crypto/tls_gnutls.c +++ b/contrib/wpa/src/crypto/tls_gnutls.c @@ -37,6 +37,8 @@ struct tls_global { union tls_event_data *data); void *cb_ctx; int cert_in_cb; + + char *ocsp_stapling_response; }; struct tls_connection { @@ -133,6 +135,7 @@ void tls_deinit(void *ssl_ctx) if (global->params_set) gnutls_certificate_free_credentials(global->xcred); os_free(global->session_data); + os_free(global->ocsp_stapling_response); os_free(global); } @@ -347,6 +350,18 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, if (conn == NULL || params == NULL) return -1; + if (params->flags & TLS_CONN_REQUIRE_OCSP_ALL) { + wpa_printf(MSG_INFO, + "GnuTLS: ocsp=3 not supported"); + return -1; + } + + if (params->flags & TLS_CONN_EXT_CERT_CHECK) { + wpa_printf(MSG_INFO, + "GnuTLS: tls_ext_cert_check=1 not supported"); + return -1; + } + if (params->subject_match) { wpa_printf(MSG_INFO, "GnuTLS: subject_match not supported"); return -1; @@ -596,6 +611,44 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, } +#if GNUTLS_VERSION_NUMBER >= 0x030103 +static int server_ocsp_status_req(gnutls_session_t session, void *ptr, + gnutls_datum_t *resp) +{ + struct tls_global *global = ptr; + char *cached; + size_t len; + + if (!global->ocsp_stapling_response) { + wpa_printf(MSG_DEBUG, "GnuTLS: OCSP status callback - no response configured"); + return GNUTLS_E_NO_CERTIFICATE_STATUS; + } + + cached = os_readfile(global->ocsp_stapling_response, &len); + if (!cached) { + wpa_printf(MSG_DEBUG, + "GnuTLS: OCSP status callback - could not read response file (%s)", + global->ocsp_stapling_response); + return GNUTLS_E_NO_CERTIFICATE_STATUS; + } + + wpa_printf(MSG_DEBUG, + "GnuTLS: OCSP status callback - send cached response"); + resp->data = gnutls_malloc(len); + if (!resp->data) { + os_free(resp); + return GNUTLS_E_MEMORY_ERROR; + } + + os_memcpy(resp->data, cached, len); + resp->size = len; + os_free(cached); + + return GNUTLS_E_SUCCESS; +} +#endif /* 3.1.3 */ + + int tls_global_set_params(void *tls_ctx, const struct tls_connection_params *params) { @@ -690,6 +743,17 @@ int tls_global_set_params(void *tls_ctx, } } +#if GNUTLS_VERSION_NUMBER >= 0x030103 + os_free(global->ocsp_stapling_response); + if (params->ocsp_stapling_response) + global->ocsp_stapling_response = + os_strdup(params->ocsp_stapling_response); + else + global->ocsp_stapling_response = NULL; + gnutls_certificate_set_ocsp_status_request_function( + global->xcred, server_ocsp_status_req, global); +#endif /* 3.1.3 */ + global->params_set = 1; return 0; @@ -746,15 +810,22 @@ int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn, } -int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, - const char *label, int server_random_first, - int skip_keyblock, u8 *out, size_t out_len) +int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn, + const char *label, u8 *out, size_t out_len) { - if (conn == NULL || conn->session == NULL || skip_keyblock) + if (conn == NULL || conn->session == NULL) return -1; return gnutls_prf(conn->session, os_strlen(label), label, - server_random_first, 0, NULL, out_len, (char *) out); + 0 /* client_random first */, 0, NULL, out_len, + (char *) out); +} + + +int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn, + u8 *out, size_t out_len) +{ + return -1; } diff --git a/contrib/wpa/src/crypto/tls_internal.c b/contrib/wpa/src/crypto/tls_internal.c index 704751d308fc..c7cb5ded331f 100644 --- a/contrib/wpa/src/crypto/tls_internal.c +++ b/contrib/wpa/src/crypto/tls_internal.c @@ -23,6 +23,11 @@ struct tls_global { int server; struct tlsv1_credentials *server_cred; int check_crl; + + void (*event_cb)(void *ctx, enum tls_event ev, + union tls_event_data *data); + void *cb_ctx; + int cert_in_cb; }; struct tls_connection { @@ -51,6 +56,11 @@ void * tls_init(const struct tls_config *conf) global = os_zalloc(sizeof(*global)); if (global == NULL) return NULL; + if (conf) { + global->event_cb = conf->event_cb; + global->cb_ctx = conf->cb_ctx; + global->cert_in_cb = conf->cert_in_cb; + } return global; } @@ -64,10 +74,12 @@ void tls_deinit(void *ssl_ctx) tlsv1_client_global_deinit(); #endif /* CONFIG_TLS_INTERNAL_CLIENT */ #ifdef CONFIG_TLS_INTERNAL_SERVER - tlsv1_cred_free(global->server_cred); tlsv1_server_global_deinit(); #endif /* CONFIG_TLS_INTERNAL_SERVER */ } +#ifdef CONFIG_TLS_INTERNAL_SERVER + tlsv1_cred_free(global->server_cred); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ os_free(global); } @@ -95,6 +107,8 @@ struct tls_connection * tls_connection_init(void *tls_ctx) os_free(conn); return NULL; } + tlsv1_client_set_cb(conn->client, global->event_cb, + global->cb_ctx, global->cert_in_cb); } #endif /* CONFIG_TLS_INTERNAL_CLIENT */ #ifdef CONFIG_TLS_INTERNAL_SERVER @@ -186,6 +200,12 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, if (conn->client == NULL) return -1; + if (params->flags & TLS_CONN_EXT_CERT_CHECK) { + wpa_printf(MSG_INFO, + "TLS: tls_ext_cert_check=1 not supported"); + return -1; + } + cred = tlsv1_cred_alloc(); if (cred == NULL) return -1; @@ -259,8 +279,7 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; } - tlsv1_client_set_time_checks( - conn->client, !(params->flags & TLS_CONN_DISABLE_TIME_CHECKS)); + tlsv1_client_set_flags(conn->client, params->flags); return 0; #else /* CONFIG_TLS_INTERNAL_CLIENT */ @@ -312,6 +331,13 @@ int tls_global_set_params(void *tls_ctx, return -1; } + if (params->ocsp_stapling_response) + cred->ocsp_stapling_response = + os_strdup(params->ocsp_stapling_response); + if (params->ocsp_stapling_response_multi) + cred->ocsp_stapling_response_multi = + os_strdup(params->ocsp_stapling_response_multi); + return 0; #else /* CONFIG_TLS_INTERNAL_SERVER */ return -1; @@ -368,9 +394,9 @@ static int tls_get_keyblock_size(struct tls_connection *conn) } -int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, - const char *label, int server_random_first, - int skip_keyblock, u8 *out, size_t out_len) +static int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, + const char *label, int server_random_first, + int skip_keyblock, u8 *out, size_t out_len) { int ret = -1, skip = 0; u8 *tmp_out = NULL; @@ -390,14 +416,14 @@ int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, if (conn->client) { ret = tlsv1_client_prf(conn->client, label, server_random_first, - _out, out_len); + _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, - _out, out_len); + _out, skip + out_len); } #endif /* CONFIG_TLS_INTERNAL_SERVER */ if (ret == 0 && skip_keyblock) @@ -408,6 +434,21 @@ 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) +{ + return tls_connection_prf(tls_ctx, conn, label, 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); +} + + struct wpabuf * tls_connection_handshake(void *tls_ctx, struct tls_connection *conn, const struct wpabuf *in_data, @@ -621,7 +662,12 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, int tls_get_version(void *ssl_ctx, struct tls_connection *conn, char *buf, size_t buflen) { - /* TODO */ + if (conn == NULL) + return -1; +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (conn->client) + return tlsv1_client_get_version(conn->client, buf, buflen); +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ return -1; } diff --git a/contrib/wpa/src/crypto/tls_none.c b/contrib/wpa/src/crypto/tls_none.c index ae392ad8aa0f..dd5681e9ca3c 100644 --- a/contrib/wpa/src/crypto/tls_none.c +++ b/contrib/wpa/src/crypto/tls_none.c @@ -86,9 +86,15 @@ int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn, } -int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, - const char *label, int server_random_first, - int skip_keyblock, u8 *out, size_t out_len) +int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn, + const char *label, u8 *out, size_t out_len) +{ + return -1; +} + + +int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn, + u8 *out, size_t out_len) { return -1; } diff --git a/contrib/wpa/src/crypto/tls_openssl.c b/contrib/wpa/src/crypto/tls_openssl.c index 8b7b47bc256d..23ac64b48cd9 100644 --- a/contrib/wpa/src/crypto/tls_openssl.c +++ b/contrib/wpa/src/crypto/tls_openssl.c @@ -18,6 +18,7 @@ #include #include +#include #include #include #ifndef OPENSSL_NO_ENGINE @@ -35,12 +36,12 @@ #include "sha1.h" #include "sha256.h" #include "tls.h" +#include "tls_openssl.h" -#if OPENSSL_VERSION_NUMBER < 0x10000000L -/* ERR_remove_thread_state replaces ERR_remove_state and the latter is - * deprecated. However, OpenSSL 0.9.8 doesn't include - * ERR_remove_thread_state. */ -#define ERR_remove_thread_state(tid) ERR_remove_state(0) +#if !defined(CONFIG_FIPS) && \ + (defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || \ + defined(EAP_SERVER_FAST)) +#define OPENSSL_NEED_EAP_FAST_PRF #endif #if defined(OPENSSL_IS_BORINGSSL) @@ -57,6 +58,51 @@ typedef int stack_index_t; #endif /* OPENSSL_NO_TLSEXT */ #endif /* SSL_set_tlsext_status_type */ +#if (OPENSSL_VERSION_NUMBER < 0x10100000L || \ + defined(LIBRESSL_VERSION_NUMBER)) && \ + !defined(BORINGSSL_API_VERSION) +/* + * SSL_get_client_random() and SSL_get_server_random() were added in OpenSSL + * 1.1.0 and newer BoringSSL revisions. Provide compatibility wrappers for + * older versions. + */ + +static size_t SSL_get_client_random(const SSL *ssl, unsigned char *out, + size_t outlen) +{ + if (!ssl->s3 || outlen < SSL3_RANDOM_SIZE) + return 0; + os_memcpy(out, ssl->s3->client_random, SSL3_RANDOM_SIZE); + return SSL3_RANDOM_SIZE; +} + + +static size_t SSL_get_server_random(const SSL *ssl, unsigned char *out, + size_t outlen) +{ + if (!ssl->s3 || outlen < SSL3_RANDOM_SIZE) + return 0; + os_memcpy(out, ssl->s3->server_random, SSL3_RANDOM_SIZE); + return SSL3_RANDOM_SIZE; +} + + +#ifdef OPENSSL_NEED_EAP_FAST_PRF +static size_t SSL_SESSION_get_master_key(const SSL_SESSION *session, + unsigned char *out, size_t outlen) +{ + if (!session || session->master_key_length < 0 || + (size_t) session->master_key_length > outlen) + return 0; + if ((size_t) session->master_key_length < outlen) + outlen = session->master_key_length; + os_memcpy(out, session->master_key, outlen); + return outlen; +} +#endif /* OPENSSL_NEED_EAP_FAST_PRF */ + +#endif + #ifdef ANDROID #include #include @@ -71,6 +117,66 @@ static BIO * BIO_from_keystore(const char *key) free(value); return bio; } + + +static int tls_add_ca_from_keystore(X509_STORE *ctx, const char *key_alias) +{ + BIO *bio = BIO_from_keystore(key_alias); + STACK_OF(X509_INFO) *stack = NULL; + stack_index_t i; + + if (bio) { + stack = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL); + BIO_free(bio); + } + + if (!stack) { + wpa_printf(MSG_WARNING, "TLS: Failed to parse certificate: %s", + key_alias); + return -1; + } + + for (i = 0; i < sk_X509_INFO_num(stack); ++i) { + X509_INFO *info = sk_X509_INFO_value(stack, i); + + if (info->x509) + X509_STORE_add_cert(ctx, info->x509); + if (info->crl) + X509_STORE_add_crl(ctx, info->crl); + } + + sk_X509_INFO_pop_free(stack, X509_INFO_free); + + return 0; +} + + +static int tls_add_ca_from_keystore_encoded(X509_STORE *ctx, + const char *encoded_key_alias) +{ + int rc = -1; + int len = os_strlen(encoded_key_alias); + unsigned char *decoded_alias; + + if (len & 1) { + wpa_printf(MSG_WARNING, "Invalid hex-encoded alias: %s", + encoded_key_alias); + return rc; + } + + decoded_alias = os_malloc(len / 2 + 1); + if (decoded_alias) { + if (!hexstr2bin(encoded_key_alias, decoded_alias, len / 2)) { + decoded_alias[len / 2] = '\0'; + rc = tls_add_ca_from_keystore( + ctx, (const char *) decoded_alias); + } + os_free(decoded_alias); + } + + return rc; +} + #endif /* ANDROID */ static int tls_openssl_ref_count = 0; @@ -97,7 +203,7 @@ struct tls_connection { SSL_CTX *ssl_ctx; SSL *ssl; BIO *ssl_in, *ssl_out; -#ifndef OPENSSL_NO_ENGINE +#if defined(ANDROID) || !defined(OPENSSL_NO_ENGINE) ENGINE *engine; /* functional reference to the engine */ EVP_PKEY *private_key; /* the private key if using engine */ #endif /* OPENSSL_NO_ENGINE */ @@ -125,10 +231,8 @@ struct tls_connection { X509 *peer_issuer; X509 *peer_issuer_issuer; -#if OPENSSL_VERSION_NUMBER >= 0x10100000L unsigned char client_random[SSL3_RANDOM_SIZE]; unsigned char server_random[SSL3_RANDOM_SIZE]; -#endif }; @@ -526,7 +630,8 @@ static int tls_cryptoapi_ca_cert(SSL_CTX *ssl_ctx, SSL *ssl, const char *name) wpa_printf(MSG_DEBUG, "OpenSSL: Loaded CA certificate for " "system certificate store: subject='%s'", buf); - if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) { + if (!X509_STORE_add_cert(SSL_CTX_get_cert_store(ssl_ctx), + cert)) { tls_show_errors(MSG_WARNING, __func__, "Failed to add ca_cert to OpenSSL " "certificate store"); @@ -624,10 +729,16 @@ static int tls_engine_load_dynamic_generic(const char *pre[], engine = ENGINE_by_id(id); if (engine) { - ENGINE_free(engine); wpa_printf(MSG_DEBUG, "ENGINE: engine '%s' is already " "available", id); - return 0; + /* + * If it was auto-loaded by ENGINE_by_id() we might still + * need to tell it which PKCS#11 module to use in legacy + * (non-p11-kit) environments. Do so now; even if it was + * properly initialised before, setting it again will be + * harmless. + */ + goto found; } ERR_clear_error(); @@ -664,7 +775,7 @@ static int tls_engine_load_dynamic_generic(const char *pre[], id, ERR_error_string(ERR_get_error(), NULL)); return -1; } - + found: while (post && post[0]) { wpa_printf(MSG_DEBUG, "ENGINE: '%s' '%s'", post[0], post[1]); if (ENGINE_ctrl_cmd_string(engine, post[0], post[1], 0) == 0) { @@ -808,6 +919,7 @@ void * tls_init(const struct tls_config *conf) } #endif /* OPENSSL_FIPS */ #endif /* CONFIG_FIPS */ +#if OPENSSL_VERSION_NUMBER < 0x10100000L SSL_load_error_strings(); SSL_library_init(); #ifndef OPENSSL_NO_SHA256 @@ -829,6 +941,7 @@ void * tls_init(const struct tls_config *conf) #endif /* OPENSSL_NO_RC2 */ PKCS12_PBE_add(); #endif /* PKCS12_FUNCS */ +#endif /* < 1.1.0 */ } else { context = tls_context_new(conf); if (context == NULL) @@ -849,6 +962,7 @@ void * tls_init(const struct tls_config *conf) os_free(tls_global); tls_global = NULL; } + os_free(data); return NULL; } data->ssl = ssl; @@ -929,6 +1043,7 @@ void tls_deinit(void *ssl_ctx) tls_openssl_ref_count--; if (tls_openssl_ref_count == 0) { +#if OPENSSL_VERSION_NUMBER < 0x10100000L #ifndef OPENSSL_NO_ENGINE ENGINE_cleanup(); #endif /* OPENSSL_NO_ENGINE */ @@ -936,6 +1051,7 @@ void tls_deinit(void *ssl_ctx) ERR_remove_thread_state(NULL); ERR_free_strings(); EVP_cleanup(); +#endif /* < 1.1.0 */ os_free(tls_global->ocsp_stapling_response); tls_global->ocsp_stapling_response = NULL; os_free(tls_global); @@ -967,10 +1083,32 @@ static int tls_is_pin_error(unsigned int err) #endif /* OPENSSL_NO_ENGINE */ +#ifdef ANDROID +/* EVP_PKEY_from_keystore comes from system/security/keystore-engine. */ +EVP_PKEY * EVP_PKEY_from_keystore(const char *key_id); +#endif /* ANDROID */ + static int tls_engine_init(struct tls_connection *conn, const char *engine_id, const char *pin, const char *key_id, const char *cert_id, const char *ca_cert_id) { +#if defined(ANDROID) && defined(OPENSSL_IS_BORINGSSL) +#if !defined(OPENSSL_NO_ENGINE) +#error "This code depends on OPENSSL_NO_ENGINE being defined by BoringSSL." +#endif + if (!key_id) + return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; + conn->engine = NULL; + conn->private_key = EVP_PKEY_from_keystore(key_id); + if (!conn->private_key) { + wpa_printf(MSG_ERROR, + "ENGINE: cannot load private key with id '%s' [%s]", + key_id, + ERR_error_string(ERR_get_error(), NULL)); + return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; + } +#endif /* ANDROID && OPENSSL_IS_BORINGSSL */ + #ifndef OPENSSL_NO_ENGINE int ret = -1; if (engine_id == NULL) { @@ -1068,17 +1206,19 @@ err: static void tls_engine_deinit(struct tls_connection *conn) { -#ifndef OPENSSL_NO_ENGINE +#if defined(ANDROID) || !defined(OPENSSL_NO_ENGINE) wpa_printf(MSG_DEBUG, "ENGINE: engine deinit"); if (conn->private_key) { EVP_PKEY_free(conn->private_key); conn->private_key = NULL; } if (conn->engine) { +#if !defined(OPENSSL_IS_BORINGSSL) ENGINE_finish(conn->engine); +#endif /* !OPENSSL_IS_BORINGSSL */ conn->engine = NULL; } -#endif /* OPENSSL_NO_ENGINE */ +#endif /* ANDROID || !OPENSSL_NO_ENGINE */ } @@ -1097,14 +1237,83 @@ int tls_get_errors(void *ssl_ctx) } +static const char * openssl_content_type(int content_type) +{ + switch (content_type) { + case 20: + return "change cipher spec"; + case 21: + return "alert"; + case 22: + return "handshake"; + case 23: + return "application data"; + case 24: + return "heartbeat"; + case 256: + return "TLS header info"; /* pseudo content type */ + default: + return "?"; + } +} + + +static const char * openssl_handshake_type(int content_type, const u8 *buf, + size_t len) +{ + if (content_type != 22 || !buf || len == 0) + return ""; + switch (buf[0]) { + case 0: + return "hello request"; + case 1: + return "client hello"; + case 2: + return "server hello"; + case 4: + return "new session ticket"; + case 11: + return "certificate"; + case 12: + return "server key exchange"; + case 13: + return "certificate request"; + case 14: + return "server hello done"; + case 15: + return "certificate verify"; + case 16: + return "client key exchange"; + case 20: + return "finished"; + case 21: + return "certificate url"; + case 22: + return "certificate status"; + default: + return "?"; + } +} + + static void tls_msg_cb(int write_p, int version, int content_type, const void *buf, size_t len, SSL *ssl, void *arg) { struct tls_connection *conn = arg; const u8 *pos = buf; - wpa_printf(MSG_DEBUG, "OpenSSL: %s ver=0x%x content_type=%d", - write_p ? "TX" : "RX", version, content_type); + if (write_p == 2) { + wpa_printf(MSG_DEBUG, + "OpenSSL: session ver=0x%x content_type=%d", + version, content_type); + wpa_hexdump_key(MSG_MSGDUMP, "OpenSSL: Data", buf, len); + return; + } + + wpa_printf(MSG_DEBUG, "OpenSSL: %s ver=0x%x content_type=%d (%s/%s)", + write_p ? "TX" : "RX", version, content_type, + openssl_content_type(content_type), + openssl_handshake_type(content_type, buf, len)); wpa_hexdump_key(MSG_MSGDUMP, "OpenSSL: Message", buf, len); if (content_type == 24 && len >= 3 && pos[0] == 1) { size_t payload_len = WPA_GET_BE16(pos + 1); @@ -1234,6 +1443,8 @@ static int tls_match_altsubject_component(X509 *cert, int type, found++; } + sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free); + return found; } @@ -1346,9 +1557,11 @@ static int tls_match_suffix(X509 *cert, const char *match, int full) 1) { wpa_printf(MSG_DEBUG, "TLS: %s in dNSName found", full ? "Match" : "Suffix match"); + sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free); return 1; } } + sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free); if (dns_name) { wpa_printf(MSG_DEBUG, "TLS: None of the dNSName(s) matched"); @@ -1489,7 +1702,8 @@ static void openssl_tls_cert_event(struct tls_connection *conn, return; os_memset(&ev, 0, sizeof(ev)); - if (conn->cert_probe || context->cert_in_cb) { + if (conn->cert_probe || (conn->flags & TLS_CONN_EXT_CERT_CHECK) || + context->cert_in_cb) { cert = get_x509_cert(err_cert); ev.peer_cert.cert = cert; } @@ -1544,6 +1758,7 @@ static void openssl_tls_cert_event(struct tls_connection *conn, pos += gen->d.ia5->length; *pos = '\0'; } + sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free); for (alt = 0; alt < num_altsubject; alt++) ev.peer_cert.altsubject[alt] = altsubject[alt]; @@ -1701,7 +1916,33 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) TLS_FAIL_SERVER_CHAIN_PROBE); } - if (preverify_ok && context->event_cb != NULL) +#ifdef OPENSSL_IS_BORINGSSL + if (depth == 0 && (conn->flags & TLS_CONN_REQUEST_OCSP) && + preverify_ok) { + enum ocsp_result res; + + res = check_ocsp_resp(conn->ssl_ctx, conn->ssl, err_cert, + conn->peer_issuer, + conn->peer_issuer_issuer); + if (res == OCSP_REVOKED) { + preverify_ok = 0; + openssl_tls_fail_event(conn, err_cert, err, depth, buf, + "certificate revoked", + TLS_FAIL_REVOKED); + if (err == X509_V_OK) + X509_STORE_CTX_set_error( + x509_ctx, X509_V_ERR_CERT_REVOKED); + } else if (res != OCSP_GOOD && + (conn->flags & TLS_CONN_REQUIRE_OCSP)) { + preverify_ok = 0; + openssl_tls_fail_event(conn, err_cert, err, depth, buf, + "bad certificate status response", + TLS_FAIL_UNSPECIFIED); + } + } +#endif /* OPENSSL_IS_BORINGSSL */ + + if (depth == 0 && preverify_ok && context->event_cb != NULL) context->event_cb(context->cb_ctx, TLS_CERT_CHAIN_SUCCESS, NULL); @@ -1837,30 +2078,40 @@ static int tls_connection_ca_cert(struct tls_data *data, } #ifdef ANDROID + /* Single alias */ if (ca_cert && os_strncmp("keystore://", ca_cert, 11) == 0) { - BIO *bio = BIO_from_keystore(&ca_cert[11]); - STACK_OF(X509_INFO) *stack = NULL; - stack_index_t i; - - if (bio) { - stack = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL); - BIO_free(bio); - } - if (!stack) + if (tls_add_ca_from_keystore(SSL_CTX_get_cert_store(ssl_ctx), + &ca_cert[11]) < 0) return -1; + SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); + return 0; + } - for (i = 0; i < sk_X509_INFO_num(stack); ++i) { - X509_INFO *info = sk_X509_INFO_value(stack, i); - if (info->x509) { - X509_STORE_add_cert(ssl_ctx->cert_store, - info->x509); - } - if (info->crl) { - X509_STORE_add_crl(ssl_ctx->cert_store, - info->crl); + /* Multiple aliases separated by space */ + if (ca_cert && os_strncmp("keystores://", ca_cert, 12) == 0) { + char *aliases = os_strdup(&ca_cert[12]); + const char *delim = " "; + int rc = 0; + char *savedptr; + char *alias; + + if (!aliases) + return -1; + alias = strtok_r(aliases, delim, &savedptr); + for (; alias; alias = strtok_r(NULL, delim, &savedptr)) { + if (tls_add_ca_from_keystore_encoded( + SSL_CTX_get_cert_store(ssl_ctx), alias)) { + wpa_printf(MSG_WARNING, + "OpenSSL: %s - Failed to add ca_cert %s from keystore", + __func__, alias); + rc = -1; + break; } } - sk_X509_INFO_pop_free(stack, X509_INFO_free); + os_free(aliases); + if (rc) + return rc; + SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); return 0; } @@ -2082,6 +2333,17 @@ static int tls_connection_client_cert(struct tls_connection *conn, if (client_cert == NULL && client_cert_blob == NULL) return 0; +#ifdef PKCS12_FUNCS +#if OPENSSL_VERSION_NUMBER < 0x10002000L + /* + * Clear previously set extra chain certificates, if any, from PKCS#12 + * processing in tls_parse_pkcs12() to allow OpenSSL to build a new + * chain properly. + */ + SSL_CTX_clear_extra_chain_certs(conn->ssl_ctx); +#endif /* OPENSSL_VERSION_NUMBER < 0x10002000L */ +#endif /* PKCS12_FUNCS */ + if (client_cert_blob && SSL_use_certificate_ASN1(conn->ssl, (u8 *) client_cert_blob, client_cert_blob_len) == 1) { @@ -2229,28 +2491,42 @@ static int tls_parse_pkcs12(struct tls_data *data, SSL *ssl, PKCS12 *p12, } if (certs) { -#if OPENSSL_VERSION_NUMBER >= 0x10002000L - SSL_clear_chain_certs(ssl); +#if OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(LIBRESSL_VERSION_NUMBER) + if (ssl) + SSL_clear_chain_certs(ssl); + else + SSL_CTX_clear_chain_certs(data->ssl); while ((cert = sk_X509_pop(certs)) != NULL) { X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf)); wpa_printf(MSG_DEBUG, "TLS: additional certificate" " from PKCS12: subject='%s'", buf); - if (SSL_add1_chain_cert(ssl, cert) != 1) { + if ((ssl && SSL_add1_chain_cert(ssl, cert) != 1) || + (!ssl && SSL_CTX_add1_chain_cert(data->ssl, + cert) != 1)) { tls_show_errors(MSG_DEBUG, __func__, "Failed to add additional certificate"); res = -1; + X509_free(cert); break; } + X509_free(cert); } if (!res) { /* Try to continue anyway */ } - sk_X509_free(certs); + sk_X509_pop_free(certs, X509_free); #ifndef OPENSSL_IS_BORINGSSL - res = SSL_build_cert_chain(ssl, - SSL_BUILD_CHAIN_FLAG_CHECK | - SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR); + if (ssl) + res = SSL_build_cert_chain( + ssl, + SSL_BUILD_CHAIN_FLAG_CHECK | + SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR); + else + res = SSL_CTX_build_cert_chain( + data->ssl, + SSL_BUILD_CHAIN_FLAG_CHECK | + SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR); if (!res) { tls_show_errors(MSG_DEBUG, __func__, "Failed to build certificate chain"); @@ -2265,9 +2541,7 @@ static int tls_parse_pkcs12(struct tls_data *data, SSL *ssl, PKCS12 *p12, */ res = 0; #else /* OPENSSL_VERSION_NUMBER >= 0x10002000L */ -#if OPENSSL_VERSION_NUMBER >= 0x10001000L SSL_CTX_clear_extra_chain_certs(data->ssl); -#endif /* OPENSSL_VERSION_NUMBER >= 0x10001000L */ while ((cert = sk_X509_pop(certs)) != NULL) { X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf)); @@ -2279,11 +2553,12 @@ static int tls_parse_pkcs12(struct tls_data *data, SSL *ssl, PKCS12 *p12, */ if (SSL_CTX_add_extra_chain_cert(data->ssl, cert) != 1) { + X509_free(cert); res = -1; break; } } - sk_X509_free(certs); + sk_X509_pop_free(certs, X509_free); #endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */ } @@ -2463,7 +2738,7 @@ static int tls_connection_engine_ca_cert(struct tls_data *data, static int tls_connection_engine_private_key(struct tls_connection *conn) { -#ifndef OPENSSL_NO_ENGINE +#if defined(ANDROID) || !defined(OPENSSL_NO_ENGINE) if (SSL_use_PrivateKey(conn->ssl, conn->private_key) != 1) { tls_show_errors(MSG_ERROR, __func__, "ENGINE: cannot use private key for TLS"); @@ -2812,16 +3087,6 @@ int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn, if (conn == NULL || keys == NULL) return -1; ssl = conn->ssl; -#if OPENSSL_VERSION_NUMBER < 0x10100000L - if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL) - return -1; - - os_memset(keys, 0, sizeof(*keys)); - keys->client_random = ssl->s3->client_random; - keys->client_random_len = SSL3_RANDOM_SIZE; - keys->server_random = ssl->s3->server_random; - keys->server_random_len = SSL3_RANDOM_SIZE; -#else if (ssl == NULL) return -1; @@ -2832,16 +3097,15 @@ int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn, keys->server_random = conn->server_random; keys->server_random_len = SSL_get_server_random( ssl, conn->server_random, sizeof(conn->server_random)); -#endif return 0; } -#ifndef CONFIG_FIPS +#ifdef OPENSSL_NEED_EAP_FAST_PRF static int openssl_get_keyblock_size(SSL *ssl) { -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) const EVP_CIPHER *c; const EVP_MD *h; int md_size; @@ -2851,17 +3115,11 @@ static int openssl_get_keyblock_size(SSL *ssl) return -1; c = ssl->enc_read_ctx->cipher; -#if OPENSSL_VERSION_NUMBER >= 0x00909000L h = EVP_MD_CTX_md(ssl->read_hash); -#else - h = ssl->read_hash; -#endif if (h) md_size = EVP_MD_size(h); -#if OPENSSL_VERSION_NUMBER >= 0x10000000L else if (ssl->s3) md_size = ssl->s3->tmp.new_mac_secret_size; -#endif else return -1; @@ -2899,86 +3157,24 @@ static int openssl_get_keyblock_size(SSL *ssl) EVP_CIPHER_iv_length(c)); #endif } -#endif /* CONFIG_FIPS */ +#endif /* OPENSSL_NEED_EAP_FAST_PRF */ -static int openssl_tls_prf(struct tls_connection *conn, - const char *label, int server_random_first, - int skip_keyblock, u8 *out, size_t out_len) +int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn, + const char *label, u8 *out, size_t out_len) { -#ifdef CONFIG_FIPS - wpa_printf(MSG_ERROR, "OpenSSL: TLS keys cannot be exported in FIPS " - "mode"); - return -1; -#else /* CONFIG_FIPS */ -#if OPENSSL_VERSION_NUMBER < 0x10100000L - SSL *ssl; - u8 *rnd; - int ret = -1; - int skip = 0; - u8 *tmp_out = NULL; - u8 *_out = out; - const char *ver; - - /* - * TLS library did not support key generation, so get the needed TLS - * session parameters and use an internal implementation of TLS PRF to - * derive the key. - */ - - if (conn == NULL) + if (!conn || + SSL_export_keying_material(conn->ssl, out, out_len, label, + os_strlen(label), NULL, 0, 0) != 1) return -1; - ssl = conn->ssl; - if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL || - ssl->session->master_key_length <= 0) - return -1; - ver = SSL_get_version(ssl); + return 0; +} - if (skip_keyblock) { - skip = openssl_get_keyblock_size(ssl); - if (skip < 0) - return -1; - tmp_out = os_malloc(skip + out_len); - if (!tmp_out) - return -1; - _out = tmp_out; - } - rnd = os_malloc(2 * SSL3_RANDOM_SIZE); - if (!rnd) { - os_free(tmp_out); - return -1; - } - - if (server_random_first) { - os_memcpy(rnd, ssl->s3->server_random, SSL3_RANDOM_SIZE); - os_memcpy(rnd + SSL3_RANDOM_SIZE, ssl->s3->client_random, - SSL3_RANDOM_SIZE); - } else { - os_memcpy(rnd, ssl->s3->client_random, SSL3_RANDOM_SIZE); - os_memcpy(rnd + SSL3_RANDOM_SIZE, ssl->s3->server_random, - SSL3_RANDOM_SIZE); - } - - if (os_strcmp(ver, "TLSv1.2") == 0) { - tls_prf_sha256(ssl->session->master_key, - ssl->session->master_key_length, - label, rnd, 2 * SSL3_RANDOM_SIZE, - _out, skip + out_len); - ret = 0; - } else if (tls_prf_sha1_md5(ssl->session->master_key, - ssl->session->master_key_length, - label, rnd, 2 * SSL3_RANDOM_SIZE, - _out, skip + out_len) == 0) { - ret = 0; - } - os_free(rnd); - if (ret == 0 && skip_keyblock) - os_memcpy(out, _out + skip, out_len); - bin_clear_free(tmp_out, skip); - - return ret; -#else +int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn, + u8 *out, size_t out_len) +{ +#ifdef OPENSSL_NEED_EAP_FAST_PRF SSL *ssl; SSL_SESSION *sess; u8 *rnd; @@ -2993,9 +3189,9 @@ static int openssl_tls_prf(struct tls_connection *conn, const char *ver; /* - * TLS library did not support key generation, so get the needed TLS - * session parameters and use an internal implementation of TLS PRF to - * derive the key. + * TLS library did not support EAP-FAST key generation, so get the + * needed TLS session parameters and use an internal implementation of + * TLS PRF to derive the key. */ if (conn == NULL) @@ -3008,15 +3204,13 @@ static int openssl_tls_prf(struct tls_connection *conn, if (!ver || !sess) return -1; - if (skip_keyblock) { - skip = openssl_get_keyblock_size(ssl); - if (skip < 0) - return -1; - tmp_out = os_malloc(skip + out_len); - if (!tmp_out) - return -1; - _out = tmp_out; - } + skip = openssl_get_keyblock_size(ssl); + if (skip < 0) + return -1; + tmp_out = os_malloc(skip + out_len); + if (!tmp_out) + return -1; + _out = tmp_out; rnd = os_malloc(2 * SSL3_RANDOM_SIZE); if (!rnd) { @@ -3029,59 +3223,31 @@ static int openssl_tls_prf(struct tls_connection *conn, master_key_len = SSL_SESSION_get_master_key(sess, master_key, sizeof(master_key)); - if (server_random_first) { - os_memcpy(rnd, server_random, SSL3_RANDOM_SIZE); - os_memcpy(rnd + SSL3_RANDOM_SIZE, client_random, - SSL3_RANDOM_SIZE); - } else { - os_memcpy(rnd, client_random, SSL3_RANDOM_SIZE); - os_memcpy(rnd + SSL3_RANDOM_SIZE, server_random, - SSL3_RANDOM_SIZE); - } + os_memcpy(rnd, server_random, SSL3_RANDOM_SIZE); + os_memcpy(rnd + SSL3_RANDOM_SIZE, client_random, SSL3_RANDOM_SIZE); if (os_strcmp(ver, "TLSv1.2") == 0) { tls_prf_sha256(master_key, master_key_len, - label, rnd, 2 * SSL3_RANDOM_SIZE, + "key expansion", rnd, 2 * SSL3_RANDOM_SIZE, _out, skip + out_len); ret = 0; } else if (tls_prf_sha1_md5(master_key, master_key_len, - label, rnd, 2 * SSL3_RANDOM_SIZE, + "key expansion", rnd, 2 * SSL3_RANDOM_SIZE, _out, skip + out_len) == 0) { ret = 0; } os_memset(master_key, 0, sizeof(master_key)); os_free(rnd); - if (ret == 0 && skip_keyblock) + if (ret == 0) os_memcpy(out, _out + skip, out_len); bin_clear_free(tmp_out, skip); return ret; -#endif -#endif /* CONFIG_FIPS */ -} - - -int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, - const char *label, int server_random_first, - int skip_keyblock, u8 *out, size_t out_len) -{ -#if OPENSSL_VERSION_NUMBER >= 0x10001000L - SSL *ssl; - if (conn == NULL) - return -1; - if (server_random_first || skip_keyblock) - return openssl_tls_prf(conn, label, - server_random_first, skip_keyblock, - out, out_len); - ssl = conn->ssl; - if (SSL_export_keying_material(ssl, out, out_len, label, - os_strlen(label), NULL, 0, 0) == 1) { - wpa_printf(MSG_DEBUG, "OpenSSL: Using internal PRF"); - return 0; - } -#endif - return openssl_tls_prf(conn, label, server_random_first, - skip_keyblock, out, out_len); +#else /* OPENSSL_NEED_EAP_FAST_PRF */ + wpa_printf(MSG_ERROR, + "OpenSSL: EAP-FAST keys cannot be exported in FIPS mode"); + return -1; +#endif /* OPENSSL_NEED_EAP_FAST_PRF */ } @@ -3340,18 +3506,14 @@ struct wpabuf * tls_connection_decrypt(void *tls_ctx, int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn) { -#if OPENSSL_VERSION_NUMBER >= 0x10001000L return conn ? SSL_cache_hit(conn->ssl) : 0; -#else - return conn ? conn->ssl->hit : 0; -#endif } int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, u8 *ciphers) { - char buf[100], *pos, *end; + char buf[500], *pos, *end; u8 *c; int ret; @@ -3379,6 +3541,12 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, case TLS_CIPHER_ANON_DH_AES128_SHA: suite = "ADH-AES128-SHA"; break; + case TLS_CIPHER_RSA_DHE_AES256_SHA: + suite = "DHE-RSA-AES256-SHA"; + break; + case TLS_CIPHER_AES256_SHA: + suite = "AES256-SHA"; + break; default: wpa_printf(MSG_DEBUG, "TLS: Unsupported " "cipher selection: %d", *c); @@ -3394,7 +3562,7 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, wpa_printf(MSG_DEBUG, "OpenSSL: cipher suites: %s", buf + 1); -#if OPENSSL_VERSION_NUMBER >= 0x10100000L +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) #if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) if (os_strstr(buf, ":ADH-")) { /* @@ -3687,10 +3855,12 @@ static int ocsp_resp_cb(SSL *s, void *arg) wpa_printf(MSG_INFO, "OpenSSL: Could not find current server certificate from OCSP response%s", (conn->flags & TLS_CONN_REQUIRE_OCSP) ? "" : " (OCSP not required)"); + OCSP_CERTID_free(id); OCSP_BASICRESP_free(basic); OCSP_RESPONSE_free(rsp); return (conn->flags & TLS_CONN_REQUIRE_OCSP) ? 0 : 1; } + OCSP_CERTID_free(id); if (!OCSP_check_validity(this_update, next_update, 5 * 60, -1)) { tls_show_errors(MSG_INFO, __func__, @@ -3769,6 +3939,12 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, if (conn == NULL) return -1; + if (params->flags & TLS_CONN_REQUIRE_OCSP_ALL) { + wpa_printf(MSG_INFO, + "OpenSSL: ocsp=3 not supported"); + return -1; + } + /* * If the engine isn't explicitly configured, and any of the * cert/key fields are actually PKCS#11 URIs, then automatically @@ -3879,6 +4055,11 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, tls_set_conn_flags(conn->ssl, params->flags); +#ifdef OPENSSL_IS_BORINGSSL + if (params->flags & TLS_CONN_REQUEST_OCSP) { + SSL_enable_ocsp_stapling(conn->ssl); + } +#else /* OPENSSL_IS_BORINGSSL */ #ifdef HAVE_OCSP if (params->flags & TLS_CONN_REQUEST_OCSP) { SSL_CTX *ssl_ctx = data->ssl; @@ -3897,6 +4078,7 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, "OpenSSL: No OCSP support included - allow optional OCSP case to continue"); } #endif /* HAVE_OCSP */ +#endif /* OPENSSL_IS_BORINGSSL */ conn->flags = params->flags; @@ -3964,7 +4146,7 @@ int tls_global_set_params(void *tls_ctx, * commented out unless explicitly needed for EAP-FAST in order to be able to * build this file with unmodified openssl. */ -#ifdef OPENSSL_IS_BORINGSSL +#if (defined(OPENSSL_IS_BORINGSSL) || OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER) static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, const SSL_CIPHER **cipher, void *arg) @@ -3977,7 +4159,7 @@ static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len, struct tls_connection *conn = arg; int ret; -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) if (conn == NULL || conn->session_ticket_cb == NULL) return 0; @@ -4072,9 +4254,15 @@ int tls_connection_set_session_ticket_cb(void *tls_ctx, int tls_get_library_version(char *buf, size_t buf_len) { +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) + return os_snprintf(buf, buf_len, "OpenSSL build=%s run=%s", + OPENSSL_VERSION_TEXT, + OpenSSL_version(OPENSSL_VERSION)); +#else return os_snprintf(buf, buf_len, "OpenSSL build=%s run=%s", OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION)); +#endif } diff --git a/contrib/wpa/src/crypto/tls_openssl.h b/contrib/wpa/src/crypto/tls_openssl.h new file mode 100644 index 000000000000..2a62d5c5d019 --- /dev/null +++ b/contrib/wpa/src/crypto/tls_openssl.h @@ -0,0 +1,19 @@ +/* + * SSL/TLS interface functions for OpenSSL + * Copyright (c) 2004-2015, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef TLS_OPENSSL_H +#define TLS_OPENSSL_H + +enum ocsp_result { + OCSP_GOOD, OCSP_REVOKED, OCSP_NO_RESPONSE, OCSP_INVALID +}; + +enum ocsp_result check_ocsp_resp(SSL_CTX *ssl_ctx, SSL *ssl, X509 *cert, + X509 *issuer, X509 *issuer_issuer); + +#endif /* TLS_OPENSSL_H */ diff --git a/contrib/wpa/src/crypto/tls_openssl_ocsp.c b/contrib/wpa/src/crypto/tls_openssl_ocsp.c new file mode 100644 index 000000000000..8b37b34e7890 --- /dev/null +++ b/contrib/wpa/src/crypto/tls_openssl_ocsp.c @@ -0,0 +1,846 @@ +/* + * SSL/TLS interface functions for OpenSSL - BoringSSL OCSP + * Copyright (c) 2004-2015, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include +#include +#include +#ifdef OPENSSL_IS_BORINGSSL +#include +#include +#endif /* OPENSSL_IS_BORINGSSL */ + +#include "common.h" +#include "tls_openssl.h" + + +#ifdef OPENSSL_IS_BORINGSSL + +static void tls_show_errors(int level, const char *func, const char *txt) +{ + unsigned long err; + + wpa_printf(level, "OpenSSL: %s - %s %s", + func, txt, ERR_error_string(ERR_get_error(), NULL)); + + while ((err = ERR_get_error())) { + wpa_printf(MSG_INFO, "OpenSSL: pending error: %s", + ERR_error_string(err, NULL)); + } +} + + +/* + * CertID ::= SEQUENCE { + * hashAlgorithm AlgorithmIdentifier, + * issuerNameHash OCTET STRING, -- Hash of Issuer's DN + * issuerKeyHash OCTET STRING, -- Hash of Issuer's public key + * serialNumber CertificateSerialNumber } + */ +typedef struct { + X509_ALGOR *hashAlgorithm; + ASN1_OCTET_STRING *issuerNameHash; + ASN1_OCTET_STRING *issuerKeyHash; + ASN1_INTEGER *serialNumber; +} CertID; + +/* + * ResponseBytes ::= SEQUENCE { + * responseType OBJECT IDENTIFIER, + * response OCTET STRING } + */ +typedef struct { + ASN1_OBJECT *responseType; + ASN1_OCTET_STRING *response; +} ResponseBytes; + +/* + * OCSPResponse ::= SEQUENCE { + * responseStatus OCSPResponseStatus, + * responseBytes [0] EXPLICIT ResponseBytes OPTIONAL } + */ +typedef struct { + ASN1_ENUMERATED *responseStatus; + ResponseBytes *responseBytes; +} OCSPResponse; + +ASN1_SEQUENCE(ResponseBytes) = { + ASN1_SIMPLE(ResponseBytes, responseType, ASN1_OBJECT), + ASN1_SIMPLE(ResponseBytes, response, ASN1_OCTET_STRING) +} ASN1_SEQUENCE_END(ResponseBytes); + +ASN1_SEQUENCE(OCSPResponse) = { + ASN1_SIMPLE(OCSPResponse, responseStatus, ASN1_ENUMERATED), + ASN1_EXP_OPT(OCSPResponse, responseBytes, ResponseBytes, 0) +} ASN1_SEQUENCE_END(OCSPResponse); + +IMPLEMENT_ASN1_FUNCTIONS(OCSPResponse); + +/* + * ResponderID ::= CHOICE { + * byName [1] Name, + * byKey [2] KeyHash } + */ +typedef struct { + int type; + union { + X509_NAME *byName; + ASN1_OCTET_STRING *byKey; + } value; +} ResponderID; + +/* + * RevokedInfo ::= SEQUENCE { + * revocationTime GeneralizedTime, + * revocationReason [0] EXPLICIT CRLReason OPTIONAL } + */ +typedef struct { + ASN1_GENERALIZEDTIME *revocationTime; + ASN1_ENUMERATED *revocationReason; +} RevokedInfo; + +/* + * CertStatus ::= CHOICE { + * good [0] IMPLICIT NULL, + * revoked [1] IMPLICIT RevokedInfo, + * unknown [2] IMPLICIT UnknownInfo } + */ +typedef struct { + int type; + union { + ASN1_NULL *good; + RevokedInfo *revoked; + ASN1_NULL *unknown; + } value; +} CertStatus; + +/* + * SingleResponse ::= SEQUENCE { + * certID CertID, + * certStatus CertStatus, + * thisUpdate GeneralizedTime, + * nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL, + * singleExtensions [1] EXPLICIT Extensions OPTIONAL } + */ +typedef struct { + CertID *certID; + CertStatus *certStatus; + ASN1_GENERALIZEDTIME *thisUpdate; + ASN1_GENERALIZEDTIME *nextUpdate; + STACK_OF(X509_EXTENSION) *singleExtensions; +} SingleResponse; + +/* + * ResponseData ::= SEQUENCE { + * version [0] EXPLICIT Version DEFAULT v1, + * responderID ResponderID, + * producedAt GeneralizedTime, + * responses SEQUENCE OF SingleResponse, + * responseExtensions [1] EXPLICIT Extensions OPTIONAL } + */ +typedef struct { + ASN1_INTEGER *version; + ResponderID *responderID; + ASN1_GENERALIZEDTIME *producedAt; + STACK_OF(SingleResponse) *responses; + STACK_OF(X509_EXTENSION) *responseExtensions; +} ResponseData; + +/* + * BasicOCSPResponse ::= SEQUENCE { + * tbsResponseData ResponseData, + * signatureAlgorithm AlgorithmIdentifier, + * signature BIT STRING, + * certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } + */ +typedef struct { + ResponseData *tbsResponseData; + X509_ALGOR *signatureAlgorithm; + ASN1_BIT_STRING *signature; + STACK_OF(X509) *certs; +} BasicOCSPResponse; + +ASN1_SEQUENCE(CertID) = { + ASN1_SIMPLE(CertID, hashAlgorithm, X509_ALGOR), + ASN1_SIMPLE(CertID, issuerNameHash, ASN1_OCTET_STRING), + ASN1_SIMPLE(CertID, issuerKeyHash, ASN1_OCTET_STRING), + ASN1_SIMPLE(CertID, serialNumber, ASN1_INTEGER) +} ASN1_SEQUENCE_END(CertID); + +ASN1_CHOICE(ResponderID) = { + ASN1_EXP(ResponderID, value.byName, X509_NAME, 1), + ASN1_EXP(ResponderID, value.byKey, ASN1_OCTET_STRING, 2) +} ASN1_CHOICE_END(ResponderID); + +ASN1_SEQUENCE(RevokedInfo) = { + ASN1_SIMPLE(RevokedInfo, revocationTime, ASN1_GENERALIZEDTIME), + ASN1_EXP_OPT(RevokedInfo, revocationReason, ASN1_ENUMERATED, 0) +} ASN1_SEQUENCE_END(RevokedInfo); + +ASN1_CHOICE(CertStatus) = { + ASN1_IMP(CertStatus, value.good, ASN1_NULL, 0), + ASN1_IMP(CertStatus, value.revoked, RevokedInfo, 1), + ASN1_IMP(CertStatus, value.unknown, ASN1_NULL, 2) +} ASN1_CHOICE_END(CertStatus); + +ASN1_SEQUENCE(SingleResponse) = { + ASN1_SIMPLE(SingleResponse, certID, CertID), + ASN1_SIMPLE(SingleResponse, certStatus, CertStatus), + ASN1_SIMPLE(SingleResponse, thisUpdate, ASN1_GENERALIZEDTIME), + ASN1_EXP_OPT(SingleResponse, nextUpdate, ASN1_GENERALIZEDTIME, 0), + ASN1_EXP_SEQUENCE_OF_OPT(SingleResponse, singleExtensions, + X509_EXTENSION, 1) +} ASN1_SEQUENCE_END(SingleResponse); + +ASN1_SEQUENCE(ResponseData) = { + ASN1_EXP_OPT(ResponseData, version, ASN1_INTEGER, 0), + ASN1_SIMPLE(ResponseData, responderID, ResponderID), + ASN1_SIMPLE(ResponseData, producedAt, ASN1_GENERALIZEDTIME), + ASN1_SEQUENCE_OF(ResponseData, responses, SingleResponse), + ASN1_EXP_SEQUENCE_OF_OPT(ResponseData, responseExtensions, + X509_EXTENSION, 1) +} ASN1_SEQUENCE_END(ResponseData); + +ASN1_SEQUENCE(BasicOCSPResponse) = { + ASN1_SIMPLE(BasicOCSPResponse, tbsResponseData, ResponseData), + ASN1_SIMPLE(BasicOCSPResponse, signatureAlgorithm, X509_ALGOR), + ASN1_SIMPLE(BasicOCSPResponse, signature, ASN1_BIT_STRING), + ASN1_EXP_SEQUENCE_OF_OPT(BasicOCSPResponse, certs, X509, 0) +} ASN1_SEQUENCE_END(BasicOCSPResponse); + +IMPLEMENT_ASN1_FUNCTIONS(BasicOCSPResponse); + +#define sk_SingleResponse_num(sk) \ +sk_num(CHECKED_CAST(_STACK *, STACK_OF(SingleResponse) *, sk)) + +#define sk_SingleResponse_value(sk, i) \ + ((SingleResponse *) \ + sk_value(CHECKED_CAST(_STACK *, STACK_OF(SingleResponse) *, sk), (i))) + + +static char * mem_bio_to_str(BIO *out) +{ + char *txt; + size_t rlen; + int res; + + rlen = BIO_ctrl_pending(out); + txt = os_malloc(rlen + 1); + if (!txt) { + BIO_free(out); + return NULL; + } + + res = BIO_read(out, txt, rlen); + BIO_free(out); + if (res < 0) { + os_free(txt); + return NULL; + } + + txt[res] = '\0'; + return txt; +} + + +static char * generalizedtime_str(ASN1_GENERALIZEDTIME *t) +{ + BIO *out; + + out = BIO_new(BIO_s_mem()); + if (!out) + return NULL; + + if (!ASN1_GENERALIZEDTIME_print(out, t)) { + BIO_free(out); + return NULL; + } + + return mem_bio_to_str(out); +} + + +static char * responderid_str(ResponderID *rid) +{ + BIO *out; + + out = BIO_new(BIO_s_mem()); + if (!out) + return NULL; + + switch (rid->type) { + case 0: + X509_NAME_print_ex(out, rid->value.byName, 0, XN_FLAG_ONELINE); + break; + case 1: + i2a_ASN1_STRING(out, rid->value.byKey, V_ASN1_OCTET_STRING); + break; + default: + BIO_free(out); + return NULL; + } + + return mem_bio_to_str(out); +} + + +static char * octet_string_str(ASN1_OCTET_STRING *o) +{ + BIO *out; + + out = BIO_new(BIO_s_mem()); + if (!out) + return NULL; + + i2a_ASN1_STRING(out, o, V_ASN1_OCTET_STRING); + return mem_bio_to_str(out); +} + + +static char * integer_str(ASN1_INTEGER *i) +{ + BIO *out; + + out = BIO_new(BIO_s_mem()); + if (!out) + return NULL; + + i2a_ASN1_INTEGER(out, i); + return mem_bio_to_str(out); +} + + +static char * algor_str(X509_ALGOR *alg) +{ + BIO *out; + + out = BIO_new(BIO_s_mem()); + if (!out) + return NULL; + + i2a_ASN1_OBJECT(out, alg->algorithm); + return mem_bio_to_str(out); +} + + +static char * extensions_str(const char *title, STACK_OF(X509_EXTENSION) *ext) +{ + BIO *out; + + if (!ext) + return NULL; + + out = BIO_new(BIO_s_mem()); + if (!out) + return NULL; + + if (!X509V3_extensions_print(out, title, ext, 0, 0)) { + BIO_free(out); + return NULL; + } + return mem_bio_to_str(out); +} + + +static int ocsp_resp_valid(ASN1_GENERALIZEDTIME *thisupd, + ASN1_GENERALIZEDTIME *nextupd) +{ + time_t now, tmp; + + if (!ASN1_GENERALIZEDTIME_check(thisupd)) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Invalid OCSP response thisUpdate"); + return 0; + } + + time(&now); + tmp = now + 5 * 60; /* allow five minute clock difference */ + if (X509_cmp_time(thisupd, &tmp) > 0) { + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response not yet valid"); + return 0; + } + + if (!nextupd) + return 1; /* OK - no limit on response age */ + + if (!ASN1_GENERALIZEDTIME_check(nextupd)) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Invalid OCSP response nextUpdate"); + return 0; + } + + tmp = now - 5 * 60; /* allow five minute clock difference */ + if (X509_cmp_time(nextupd, &tmp) < 0) { + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response expired"); + return 0; + } + + if (ASN1_STRING_cmp(nextupd, thisupd) < 0) { + wpa_printf(MSG_DEBUG, + "OpenSSL: OCSP response nextUpdate before thisUpdate"); + return 0; + } + + /* Both thisUpdate and nextUpdate are valid */ + return -1; +} + + +static int issuer_match(X509 *cert, X509 *issuer, CertID *certid) +{ + X509_NAME *iname; + ASN1_BIT_STRING *ikey; + const EVP_MD *dgst; + unsigned int len; + unsigned char md[EVP_MAX_MD_SIZE]; + ASN1_OCTET_STRING *hash; + char *txt; + + dgst = EVP_get_digestbyobj(certid->hashAlgorithm->algorithm); + if (!dgst) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Could not find matching hash algorithm for OCSP"); + return -1; + } + + iname = X509_get_issuer_name(cert); + if (!X509_NAME_digest(iname, dgst, md, &len)) + return -1; + hash = ASN1_OCTET_STRING_new(); + if (!hash) + return -1; + if (!ASN1_OCTET_STRING_set(hash, md, len)) { + ASN1_OCTET_STRING_free(hash); + return -1; + } + + txt = octet_string_str(hash); + if (txt) { + wpa_printf(MSG_DEBUG, "OpenSSL: calculated issuerNameHash: %s", + txt); + os_free(txt); + } + + if (ASN1_OCTET_STRING_cmp(certid->issuerNameHash, hash)) { + ASN1_OCTET_STRING_free(hash); + return -1; + } + + ikey = X509_get0_pubkey_bitstr(issuer); + if (!ikey || + !EVP_Digest(ikey->data, ikey->length, md, &len, dgst, NULL) || + !ASN1_OCTET_STRING_set(hash, md, len)) { + ASN1_OCTET_STRING_free(hash); + return -1; + } + + txt = octet_string_str(hash); + if (txt) { + wpa_printf(MSG_DEBUG, "OpenSSL: calculated issuerKeyHash: %s", + txt); + os_free(txt); + } + + if (ASN1_OCTET_STRING_cmp(certid->issuerKeyHash, hash)) { + ASN1_OCTET_STRING_free(hash); + return -1; + } + + ASN1_OCTET_STRING_free(hash); + return 0; +} + + +static X509 * ocsp_find_signer(STACK_OF(X509) *certs, ResponderID *rid) +{ + unsigned int i; + unsigned char hash[SHA_DIGEST_LENGTH]; + + if (rid->type == 0) { + /* byName */ + return X509_find_by_subject(certs, rid->value.byName); + } + + /* byKey */ + if (rid->value.byKey->length != SHA_DIGEST_LENGTH) + return NULL; + for (i = 0; i < sk_X509_num(certs); i++) { + X509 *x = sk_X509_value(certs, i); + + X509_pubkey_digest(x, EVP_sha1(), hash, NULL); + if (os_memcmp(rid->value.byKey->data, hash, + SHA_DIGEST_LENGTH) == 0) + return x; + } + + return NULL; +} + + +enum ocsp_result check_ocsp_resp(SSL_CTX *ssl_ctx, SSL *ssl, X509 *cert, + X509 *issuer, X509 *issuer_issuer) +{ + const uint8_t *resp_data; + size_t resp_len; + OCSPResponse *resp; + int status; + ResponseBytes *bytes; + const u8 *basic_data; + size_t basic_len; + BasicOCSPResponse *basic; + ResponseData *rd; + char *txt; + int i, num; + unsigned int j, num_resp; + SingleResponse *matching_resp = NULL, *cmp_sresp; + enum ocsp_result result = OCSP_INVALID; + X509_STORE *store; + STACK_OF(X509) *untrusted = NULL, *certs = NULL, *chain = NULL; + X509_STORE_CTX ctx; + X509 *signer, *tmp_cert; + int signer_trusted = 0; + EVP_PKEY *skey; + int ret; + char buf[256]; + + txt = integer_str(X509_get_serialNumber(cert)); + if (txt) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Searching OCSP response for peer certificate serialNumber: %s", txt); + os_free(txt); + } + + SSL_get0_ocsp_response(ssl, &resp_data, &resp_len); + if (resp_data == NULL || resp_len == 0) { + wpa_printf(MSG_DEBUG, "OpenSSL: No OCSP response received"); + return OCSP_NO_RESPONSE; + } + + wpa_hexdump(MSG_DEBUG, "OpenSSL: OCSP response", resp_data, resp_len); + + resp = d2i_OCSPResponse(NULL, &resp_data, resp_len); + if (!resp) { + wpa_printf(MSG_INFO, "OpenSSL: Failed to parse OCSPResponse"); + return OCSP_INVALID; + } + + status = ASN1_ENUMERATED_get(resp->responseStatus); + if (status != 0) { + wpa_printf(MSG_INFO, "OpenSSL: OCSP responder error %d", + status); + return OCSP_INVALID; + } + + bytes = resp->responseBytes; + + if (!bytes || + OBJ_obj2nid(bytes->responseType) != NID_id_pkix_OCSP_basic) { + wpa_printf(MSG_INFO, + "OpenSSL: Could not find BasicOCSPResponse"); + return OCSP_INVALID; + } + + basic_data = ASN1_STRING_data(bytes->response); + basic_len = ASN1_STRING_length(bytes->response); + wpa_hexdump(MSG_DEBUG, "OpenSSL: BasicOCSPResponse", + basic_data, basic_len); + + basic = d2i_BasicOCSPResponse(NULL, &basic_data, basic_len); + if (!basic) { + wpa_printf(MSG_INFO, + "OpenSSL: Could not parse BasicOCSPResponse"); + OCSPResponse_free(resp); + return OCSP_INVALID; + } + + rd = basic->tbsResponseData; + + if (basic->certs) { + untrusted = sk_X509_dup(basic->certs); + if (!untrusted) + goto fail; + + num = sk_X509_num(basic->certs); + for (i = 0; i < num; i++) { + X509 *extra_cert; + + extra_cert = sk_X509_value(basic->certs, i); + X509_NAME_oneline(X509_get_subject_name(extra_cert), + buf, sizeof(buf)); + wpa_printf(MSG_DEBUG, + "OpenSSL: BasicOCSPResponse cert %s", buf); + + if (!sk_X509_push(untrusted, extra_cert)) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Could not add certificate to the untrusted stack"); + } + } + } + + store = SSL_CTX_get_cert_store(ssl_ctx); + if (issuer) { + if (X509_STORE_add_cert(store, issuer) != 1) { + tls_show_errors(MSG_INFO, __func__, + "OpenSSL: Could not add issuer to certificate store"); + } + certs = sk_X509_new_null(); + if (certs) { + tmp_cert = X509_dup(issuer); + if (tmp_cert && !sk_X509_push(certs, tmp_cert)) { + tls_show_errors( + MSG_INFO, __func__, + "OpenSSL: Could not add issuer to OCSP responder trust store"); + X509_free(tmp_cert); + sk_X509_free(certs); + certs = NULL; + } + if (certs && issuer_issuer) { + tmp_cert = X509_dup(issuer_issuer); + if (tmp_cert && + !sk_X509_push(certs, tmp_cert)) { + tls_show_errors( + MSG_INFO, __func__, + "OpenSSL: Could not add issuer's issuer to OCSP responder trust store"); + X509_free(tmp_cert); + } + } + } + } + + signer = ocsp_find_signer(certs, rd->responderID); + if (!signer) + signer = ocsp_find_signer(untrusted, rd->responderID); + else + signer_trusted = 1; + if (!signer) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Could not find OCSP signer certificate"); + goto fail; + } + + skey = X509_get_pubkey(signer); + if (!skey) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Could not get OCSP signer public key"); + goto fail; + } + if (ASN1_item_verify(ASN1_ITEM_rptr(ResponseData), + basic->signatureAlgorithm, basic->signature, + basic->tbsResponseData, skey) <= 0) { + wpa_printf(MSG_DEBUG, + "OpenSSL: BasicOCSPResponse signature is invalid"); + goto fail; + } + + X509_NAME_oneline(X509_get_subject_name(signer), buf, sizeof(buf)); + wpa_printf(MSG_DEBUG, + "OpenSSL: Found OCSP signer certificate %s and verified BasicOCSPResponse signature", + buf); + + if (!X509_STORE_CTX_init(&ctx, store, signer, untrusted)) + goto fail; + X509_STORE_CTX_set_purpose(&ctx, X509_PURPOSE_OCSP_HELPER); + ret = X509_verify_cert(&ctx); + chain = X509_STORE_CTX_get1_chain(&ctx); + X509_STORE_CTX_cleanup(&ctx); + if (ret <= 0) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Could not validate OCSP signer certificate"); + goto fail; + } + + if (!chain || sk_X509_num(chain) <= 0) { + wpa_printf(MSG_DEBUG, "OpenSSL: No OCSP signer chain found"); + goto fail; + } + + if (!signer_trusted) { + X509_check_purpose(signer, -1, 0); + if ((signer->ex_flags & EXFLAG_XKUSAGE) && + (signer->ex_xkusage & XKU_OCSP_SIGN)) { + wpa_printf(MSG_DEBUG, + "OpenSSL: OCSP signer certificate delegation OK"); + } else { + tmp_cert = sk_X509_value(chain, sk_X509_num(chain) - 1); + if (X509_check_trust(tmp_cert, NID_OCSP_sign, 0) != + X509_TRUST_TRUSTED) { + wpa_printf(MSG_DEBUG, + "OpenSSL: OCSP signer certificate not trusted"); + result = OCSP_NO_RESPONSE; + goto fail; + } + } + } + + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP version: %lu", + ASN1_INTEGER_get(rd->version)); + + txt = responderid_str(rd->responderID); + if (txt) { + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP responderID: %s", + txt); + os_free(txt); + } + + txt = generalizedtime_str(rd->producedAt); + if (txt) { + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP producedAt: %s", + txt); + os_free(txt); + } + + num_resp = sk_SingleResponse_num(rd->responses); + if (num_resp == 0) { + wpa_printf(MSG_DEBUG, + "OpenSSL: No OCSP SingleResponse within BasicOCSPResponse"); + result = OCSP_NO_RESPONSE; + goto fail; + } + cmp_sresp = sk_SingleResponse_value(rd->responses, 0); + for (j = 0; j < num_resp; j++) { + SingleResponse *sresp; + CertID *cid1, *cid2; + + sresp = sk_SingleResponse_value(rd->responses, j); + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP SingleResponse %u/%u", + j + 1, num_resp); + + txt = algor_str(sresp->certID->hashAlgorithm); + if (txt) { + wpa_printf(MSG_DEBUG, + "OpenSSL: certID hashAlgorithm: %s", txt); + os_free(txt); + } + + txt = octet_string_str(sresp->certID->issuerNameHash); + if (txt) { + wpa_printf(MSG_DEBUG, + "OpenSSL: certID issuerNameHash: %s", txt); + os_free(txt); + } + + txt = octet_string_str(sresp->certID->issuerKeyHash); + if (txt) { + wpa_printf(MSG_DEBUG, + "OpenSSL: certID issuerKeyHash: %s", txt); + os_free(txt); + } + + txt = integer_str(sresp->certID->serialNumber); + if (txt) { + wpa_printf(MSG_DEBUG, + "OpenSSL: certID serialNumber: %s", txt); + os_free(txt); + } + + switch (sresp->certStatus->type) { + case 0: + wpa_printf(MSG_DEBUG, "OpenSSL: certStatus: good"); + break; + case 1: + wpa_printf(MSG_DEBUG, "OpenSSL: certStatus: revoked"); + break; + default: + wpa_printf(MSG_DEBUG, "OpenSSL: certStatus: unknown"); + break; + } + + txt = generalizedtime_str(sresp->thisUpdate); + if (txt) { + wpa_printf(MSG_DEBUG, "OpenSSL: thisUpdate: %s", txt); + os_free(txt); + } + + if (sresp->nextUpdate) { + txt = generalizedtime_str(sresp->nextUpdate); + if (txt) { + wpa_printf(MSG_DEBUG, "OpenSSL: nextUpdate: %s", + txt); + os_free(txt); + } + } + + txt = extensions_str("singleExtensions", + sresp->singleExtensions); + if (txt) { + wpa_printf(MSG_DEBUG, "OpenSSL: %s", txt); + os_free(txt); + } + + cid1 = cmp_sresp->certID; + cid2 = sresp->certID; + if (j > 0 && + (OBJ_cmp(cid1->hashAlgorithm->algorithm, + cid2->hashAlgorithm->algorithm) != 0 || + ASN1_OCTET_STRING_cmp(cid1->issuerNameHash, + cid2->issuerNameHash) != 0 || + ASN1_OCTET_STRING_cmp(cid1->issuerKeyHash, + cid2->issuerKeyHash) != 0)) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Different OCSP response issuer information between SingleResponse values within BasicOCSPResponse"); + goto fail; + } + + if (!matching_resp && issuer && + ASN1_INTEGER_cmp(sresp->certID->serialNumber, + X509_get_serialNumber(cert)) == 0 && + issuer_match(cert, issuer, sresp->certID) == 0) { + wpa_printf(MSG_DEBUG, + "OpenSSL: This response matches peer certificate"); + matching_resp = sresp; + } + } + + txt = extensions_str("responseExtensions", rd->responseExtensions); + if (txt) { + wpa_printf(MSG_DEBUG, "OpenSSL: %s", txt); + os_free(txt); + } + + if (!matching_resp) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Could not find OCSP response that matches the peer certificate"); + result = OCSP_NO_RESPONSE; + goto fail; + } + + if (!ocsp_resp_valid(matching_resp->thisUpdate, + matching_resp->nextUpdate)) { + wpa_printf(MSG_DEBUG, + "OpenSSL: OCSP response not valid at this time"); + goto fail; + } + + if (matching_resp->certStatus->type == 1) { + wpa_printf(MSG_DEBUG, + "OpenSSL: OCSP response indicated that the peer certificate has been revoked"); + result = OCSP_REVOKED; + goto fail; + } + + if (matching_resp->certStatus->type != 0) { + wpa_printf(MSG_DEBUG, + "OpenSSL: OCSP response did not indicate good status"); + result = OCSP_NO_RESPONSE; + goto fail; + } + + /* OCSP response indicated the certificate is good. */ + result = OCSP_GOOD; +fail: + sk_X509_pop_free(chain, X509_free); + sk_X509_free(untrusted); + sk_X509_pop_free(certs, X509_free); + BasicOCSPResponse_free(basic); + OCSPResponse_free(resp); + + return result; +} + +#endif /* OPENSSL_IS_BORINGSSL */ diff --git a/contrib/wpa/src/drivers/driver.h b/contrib/wpa/src/drivers/driver.h index 3cdab5a7a87d..a449cc934735 100644 --- a/contrib/wpa/src/drivers/driver.h +++ b/contrib/wpa/src/drivers/driver.h @@ -45,6 +45,22 @@ #define HOSTAPD_CHAN_INDOOR_ONLY 0x00010000 #define HOSTAPD_CHAN_GO_CONCURRENT 0x00020000 +#define HOSTAPD_CHAN_VHT_10_150 0x00100000 +#define HOSTAPD_CHAN_VHT_30_130 0x00200000 +#define HOSTAPD_CHAN_VHT_50_110 0x00400000 +#define HOSTAPD_CHAN_VHT_70_90 0x00800000 +#define HOSTAPD_CHAN_VHT_90_70 0x01000000 +#define HOSTAPD_CHAN_VHT_110_50 0x02000000 +#define HOSTAPD_CHAN_VHT_130_30 0x04000000 +#define HOSTAPD_CHAN_VHT_150_10 0x08000000 + +/* Filter gratuitous ARP */ +#define WPA_DATA_FRAME_FILTER_FLAG_ARP BIT(0) +/* Filter unsolicited Neighbor Advertisement */ +#define WPA_DATA_FRAME_FILTER_FLAG_NA BIT(1) +/* Filter unicast IP packets encrypted using the GTK */ +#define WPA_DATA_FRAME_FILTER_FLAG_GTK BIT(2) + /** * enum reg_change_initiator - Regulatory change initiator */ @@ -283,6 +299,18 @@ struct wpa_interface_info { #define WPAS_MAX_SCAN_SSIDS 16 +/** + * struct wpa_driver_scan_ssid - SSIDs to scan for + * @ssid - specific SSID to scan for (ProbeReq) + * %NULL or zero-length SSID is used to indicate active scan + * with wildcard SSID. + * @ssid_len - Length of the SSID in octets + */ +struct wpa_driver_scan_ssid { + const u8 *ssid; + size_t ssid_len; +}; + /** * struct wpa_driver_scan_params - Scan parameters * Data for struct wpa_driver_ops::scan2(). @@ -291,18 +319,7 @@ struct wpa_driver_scan_params { /** * ssids - SSIDs to scan for */ - struct wpa_driver_scan_ssid { - /** - * ssid - specific SSID to scan for (ProbeReq) - * %NULL or zero-length SSID is used to indicate active scan - * with wildcard SSID. - */ - const u8 *ssid; - /** - * ssid_len: Length of the SSID in octets - */ - size_t ssid_len; - } ssids[WPAS_MAX_SCAN_SSIDS]; + struct wpa_driver_scan_ssid ssids[WPAS_MAX_SCAN_SSIDS]; /** * num_ssids - Number of entries in ssids array @@ -407,6 +424,37 @@ struct wpa_driver_scan_params { */ const u8 *mac_addr_mask; + /** + * sched_scan_plans - Scan plans for scheduled scan + * + * Each scan plan consists of the number of iterations to scan and the + * interval between scans. When a scan plan finishes (i.e., it was run + * for the specified number of iterations), the next scan plan is + * executed. The scan plans are executed in the order they appear in + * the array (lower index first). The last scan plan will run infinitely + * (until requested to stop), thus must not specify the number of + * iterations. All other scan plans must specify the number of + * iterations. + */ + struct sched_scan_plan { + u32 interval; /* In seconds */ + u32 iterations; /* Zero to run infinitely */ + } *sched_scan_plans; + + /** + * sched_scan_plans_num - Number of scan plans in sched_scan_plans array + */ + unsigned int sched_scan_plans_num; + + /** + * bssid - Specific BSSID to scan for + * + * This optional parameter can be used to replace the default wildcard + * BSSID with a specific BSSID to scan for if results are needed from + * only a single BSS. + */ + const u8 *bssid; + /* * NOTE: Whenever adding new parameters here, please make sure * wpa_scan_clone_params() and wpa_scan_free_params() get updated with @@ -828,6 +876,12 @@ struct wpa_driver_associate_params { * RRM (Radio Resource Measurements) */ int rrm_used; + + /** + * pbss - If set, connect to a PCP in a PBSS. Otherwise, connect to an + * AP as usual. Valid for DMG network only. + */ + int pbss; }; enum hide_ssid { @@ -1055,16 +1109,28 @@ struct wpa_driver_ap_params { * reenable - Whether this is to re-enable beaconing */ int reenable; + + /** + * pbss - Whether to start a PCP (in PBSS) instead of an AP in + * infrastructure BSS. Valid only for DMG network. + */ + int pbss; }; struct wpa_driver_mesh_bss_params { -#define WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS 0x00000001 +#define WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS 0x00000001 +#define WPA_DRIVER_MESH_CONF_FLAG_PEER_LINK_TIMEOUT 0x00000002 +#define WPA_DRIVER_MESH_CONF_FLAG_MAX_PEER_LINKS 0x00000004 +#define WPA_DRIVER_MESH_CONF_FLAG_HT_OP_MODE 0x00000008 /* * TODO: Other mesh configuration parameters would go here. * See NL80211_MESHCONF_* for all the mesh config parameters. */ unsigned int flags; + int auto_plinks; int peer_link_timeout; + int max_peer_links; + u16 ht_opmode; }; struct wpa_driver_mesh_join_params { @@ -1075,7 +1141,7 @@ struct wpa_driver_mesh_join_params { int ie_len; struct hostapd_freq_params freq; int beacon_int; - int max_peer_links; + int dtim_period; struct wpa_driver_mesh_bss_params conf; #define WPA_DRIVER_MESH_FLAG_USER_MPM 0x00000001 #define WPA_DRIVER_MESH_FLAG_DRIVER_MPM 0x00000002 @@ -1214,8 +1280,17 @@ struct wpa_driver_capa { #define WPA_DRIVER_FLAGS_VHT_IBSS 0x0000002000000000ULL /** Driver supports automatic band selection */ #define WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY 0x0000004000000000ULL +/** Driver supports simultaneous off-channel operations */ +#define WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS 0x0000008000000000ULL +/** Driver supports full AP client state */ +#define WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE 0x0000010000000000ULL +/** Driver supports P2P Listen offload */ +#define WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD 0x0000020000000000ULL u64 flags; +#define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \ + (drv_flags & WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE) + #define WPA_DRIVER_SMPS_MODE_STATIC 0x00000001 #define WPA_DRIVER_SMPS_MODE_DYNAMIC 0x00000002 unsigned int smps_modes; @@ -1231,6 +1306,15 @@ struct wpa_driver_capa { /** Maximum number of supported active probe SSIDs for sched_scan */ int max_sched_scan_ssids; + /** Maximum number of supported scan plans for scheduled scan */ + unsigned int max_sched_scan_plans; + + /** Maximum interval in a scan plan. In seconds */ + u32 max_sched_scan_plan_interval; + + /** Maximum number of iterations in a single scan plan */ + u32 max_sched_scan_plan_iterations; + /** Whether sched_scan (offloaded scanning) is supported */ int sched_scan_supported; @@ -1296,6 +1380,12 @@ struct wpa_driver_capa { * offset, namely the 6th byte in the Action frame body. */ #define WPA_DRIVER_FLAGS_TX_POWER_INSERTION 0x00000008 +/** + * Driver supports RRM. With this support, the driver will accept to use RRM in + * (Re)Association Request frames, without supporting quiet period. + */ +#define WPA_DRIVER_FLAGS_SUPPORT_RRM 0x00000010 + u32 rrm_flags; /* Driver concurrency capabilities */ @@ -1304,13 +1394,18 @@ struct wpa_driver_capa { unsigned int max_conc_chan_2_4; /* Maximum number of concurrent channels on 5 GHz */ unsigned int max_conc_chan_5_0; + + /* Maximum number of supported CSA counters */ + u16 max_csa_counters; }; struct hostapd_data; struct hostap_sta_driver_data { - unsigned long rx_packets, tx_packets, rx_bytes, tx_bytes; + unsigned long rx_packets, tx_packets; + unsigned long long rx_bytes, tx_bytes; + int bytes_64bit; /* whether 64-bit byte counters are supported */ unsigned long current_tx_rate; unsigned long inactive_msec; unsigned long flags; @@ -1336,6 +1431,7 @@ struct hostapd_sta_add_params { u32 flags_mask; /* unset bits in flags */ #ifdef CONFIG_MESH enum mesh_plink_state plink_state; + u16 peer_aid; #endif /* CONFIG_MESH */ int set; /* Set STA parameters instead of add */ u8 qosinfo; @@ -1345,6 +1441,7 @@ struct hostapd_sta_add_params { size_t supp_channels_len; const u8 *supp_oper_classes; size_t supp_oper_classes_len; + int support_p2p_ps; }; struct mac_address { @@ -1450,6 +1547,7 @@ struct wpa_bss_params { #define WPA_STA_MFP BIT(3) #define WPA_STA_TDLS_PEER BIT(4) #define WPA_STA_AUTHENTICATED BIT(5) +#define WPA_STA_ASSOCIATED BIT(6) enum tdls_oper { TDLS_DISCOVERY_REQ, @@ -1554,8 +1652,8 @@ struct csa_settings { struct beacon_data beacon_csa; struct beacon_data beacon_after; - u16 counter_offset_beacon; - u16 counter_offset_presp; + u16 counter_offset_beacon[2]; + u16 counter_offset_presp[2]; }; /* TDLS peer capabilities for send_tdls_mgmt() */ @@ -1882,6 +1980,14 @@ struct wpa_driver_ops { */ void (*poll)(void *priv); + /** + * get_ifindex - Get interface index + * @priv: private driver interface data + * + * Returns: Interface index + */ + unsigned int (*get_ifindex)(void *priv); + /** * get_ifname - Get interface name * @priv: private driver interface data @@ -1960,10 +2066,13 @@ struct wpa_driver_ops { * @noack: Do not wait for this frame to be acked (disable retries) * @freq: Frequency (in MHz) to send the frame on, or 0 to let the * driver decide + * @csa_offs: Array of CSA offsets or %NULL + * @csa_offs_len: Number of elements in csa_offs * Returns: 0 on success, -1 on failure */ int (*send_mlme)(void *priv, const u8 *data, size_t data_len, - int noack, unsigned int freq); + int noack, unsigned int freq, const u16 *csa_offs, + size_t csa_offs_len); /** * update_ft_ies - Update FT (IEEE 802.11r) IEs @@ -2013,6 +2122,7 @@ struct wpa_driver_ops { /** * global_init - Global driver initialization + * @ctx: wpa_global pointer * Returns: Pointer to private data (global), %NULL on failure * * This optional function is called to initialize the driver wrapper @@ -2022,7 +2132,7 @@ struct wpa_driver_ops { * use init2() function instead of init() to get the pointer to global * data available to per-interface initializer. */ - void * (*global_init)(void); + void * (*global_init)(void *ctx); /** * global_deinit - Global driver deinitialization @@ -2308,12 +2418,17 @@ struct wpa_driver_ops { * @params: Station parameters * Returns: 0 on success, -1 on failure * - * This function is used to add a station entry to the driver once the - * station has completed association. This is only used if the driver + * This function is used to add or set (params->set 1) a station + * entry in the driver. Adding STA entries is used only if the driver * does not take care of association processing. * - * With TDLS, this function is also used to add or set (params->set 1) - * TDLS peer entries. + * With drivers that don't support full AP client state, this function + * is used to add a station entry to the driver once the station has + * completed association. + * + * With TDLS, this function is used to add or set (params->set 1) + * TDLS peer entries (even with drivers that do not support full AP + * client state). */ int (*sta_add)(void *priv, struct hostapd_sta_add_params *params); @@ -2399,12 +2514,13 @@ struct wpa_driver_ops { * change interface address) * @bridge: Bridge interface to use or %NULL if no bridge configured * @use_existing: Whether to allow existing interface to be used + * @setup_ap: Whether to setup AP for %WPA_IF_AP_BSS interfaces * Returns: 0 on success, -1 on failure */ int (*if_add)(void *priv, enum wpa_driver_if_type type, const char *ifname, const u8 *addr, void *bss_ctx, void **drv_priv, char *force_ifname, u8 *if_addr, - const char *bridge, int use_existing); + const char *bridge, int use_existing, int setup_ap); /** * if_remove - Remove a virtual interface @@ -2986,7 +3102,6 @@ struct wpa_driver_ops { * sched_scan - Request the driver to initiate scheduled scan * @priv: Private driver interface data * @params: Scan parameters - * @interval: Interval between scan cycles in milliseconds * Returns: 0 on success, -1 on failure * * This operation should be used for scheduled scan offload to @@ -2997,8 +3112,7 @@ struct wpa_driver_ops { * and if not provided or if it returns -1, we fall back to * normal host-scheduled scans. */ - int (*sched_scan)(void *priv, struct wpa_driver_scan_params *params, - u32 interval); + int (*sched_scan)(void *priv, struct wpa_driver_scan_params *params); /** * stop_sched_scan - Request the driver to stop a scheduled scan @@ -3203,11 +3317,9 @@ struct wpa_driver_ops { * set_current_cipher_suite - Set current cipher suite * @priv: Private driver interface data * @cs: EUI64 identifier - * @cs_len: Length of the cs buffer in octets * Returns: 0 on success, -1 on failure (or if not supported) */ - int (*set_current_cipher_suite)(void *priv, const u8 *cs, - size_t cs_len); + int (*set_current_cipher_suite)(void *priv, u64 cs); /** * enable_controlled_port - Set controlled port status @@ -3439,6 +3551,78 @@ struct wpa_driver_ops { * on. Local device is assuming P2P Client role. */ int (*set_prob_oper_freq)(void *priv, unsigned int freq); + + /** + * abort_scan - Request the driver to abort an ongoing scan + * @priv: Private driver interface data + * Returns 0 on success, -1 on failure + */ + int (*abort_scan)(void *priv); + + /** + * configure_data_frame_filters - Request to configure frame filters + * @priv: Private driver interface data + * @filter_flags: The type of frames to filter (bitfield of + * WPA_DATA_FRAME_FILTER_FLAG_*) + * Returns: 0 on success or -1 on failure + */ + int (*configure_data_frame_filters)(void *priv, u32 filter_flags); + + /** + * get_ext_capab - Get extended capabilities for the specified interface + * @priv: Private driver interface data + * @type: Interface type for which to get extended capabilities + * @ext_capab: Extended capabilities fetched + * @ext_capab_mask: Extended capabilities mask + * @ext_capab_len: Length of the extended capabilities + * Returns: 0 on success or -1 on failure + */ + int (*get_ext_capab)(void *priv, enum wpa_driver_if_type type, + const u8 **ext_capab, const u8 **ext_capab_mask, + unsigned int *ext_capab_len); + + /** + * p2p_lo_start - Start offloading P2P listen to device + * @priv: Private driver interface data + * @freq: Listening frequency (MHz) for P2P listen + * @period: Length of the listen operation in milliseconds + * @interval: Interval for running the listen operation in milliseconds + * @count: Number of times to run the listen operation + * @device_types: Device primary and secondary types + * @dev_types_len: Number of bytes for device_types + * @ies: P2P IE and WSC IE for Probe Response frames + * @ies_len: Length of ies in bytes + * Returns: 0 on success or -1 on failure + */ + int (*p2p_lo_start)(void *priv, unsigned int freq, + unsigned int period, unsigned int interval, + unsigned int count, + const u8 *device_types, size_t dev_types_len, + const u8 *ies, size_t ies_len); + + /** + * p2p_lo_stop - Stop P2P listen offload + * @priv: Private driver interface data + * Returns: 0 on success or -1 on failure + */ + int (*p2p_lo_stop)(void *priv); + + /** + * set_default_scan_ies - Set default scan IEs + * @priv: Private driver interface data + * @ies: Scan default IEs buffer + * @ies_len: Length of IEs in bytes + * Returns: 0 on success or -1 on failure + * + * The driver can use these by default when there are no scan IEs coming + * in the subsequent scan requests. Also in case of one or more of IEs + * given in set_default_scan_ies() are missing in the subsequent scan + * request, the driver should merge the missing scan IEs in the scan + * request from the IEs set by set_default_scan_ies() in the Probe + * Request frames sent. + */ + int (*set_default_scan_ies)(void *priv, const u8 *ies, size_t ies_len); + }; @@ -3923,6 +4107,11 @@ enum wpa_event_type { * on a DFS frequency by a driver that supports DFS Offload. */ EVENT_DFS_CAC_STARTED, + + /** + * EVENT_P2P_LO_STOP - Notify that P2P listen offload is stopped + */ + EVENT_P2P_LO_STOP, }; @@ -4098,6 +4287,12 @@ union wpa_event_data { * ptk_kek_len - The length of ptk_kek */ size_t ptk_kek_len; + + /** + * subnet_status - The subnet status: + * 0 = unknown, 1 = unchanged, 2 = changed + */ + u8 subnet_status; } assoc_info; /** @@ -4174,6 +4369,7 @@ union wpa_event_data { * struct interface_status - Data for EVENT_INTERFACE_STATUS */ struct interface_status { + unsigned int ifindex; char ifname[100]; enum { EVENT_INTERFACE_ADDED, EVENT_INTERFACE_REMOVED @@ -4301,6 +4497,12 @@ union wpa_event_data { * status_code - Status Code from (Re)association Response */ u16 status_code; + + /** + * timed_out - Whether failure is due to timeout (etc.) rather + * than explicit rejection response from the AP. + */ + int timed_out; } assoc_reject; struct timeout_event { @@ -4381,6 +4583,9 @@ union wpa_event_data { * @ssids: Scanned SSIDs (%NULL or zero-length SSID indicates wildcard * SSID) * @num_ssids: Number of entries in ssids array + * @external_scan: Whether the scan info is for an external scan + * @nl_scan_event: 1 if the source of this scan event is a normal scan, + * 0 if the source of the scan event is a vendor scan */ struct scan_info { int aborted; @@ -4388,6 +4593,8 @@ union wpa_event_data { size_t num_freqs; struct wpa_driver_scan_ssid ssids[WPAS_MAX_SCAN_SSIDS]; size_t num_ssids; + int external_scan; + int nl_scan_event; } scan_info; /** @@ -4630,6 +4837,27 @@ union wpa_event_data { u16 ch_width; enum hostapd_hw_mode hw_mode; } acs_selected_channels; + + /** + * struct p2p_lo_stop - Reason code for P2P Listen offload stop event + * @reason_code: Reason for stopping offload + * P2P_LO_STOPPED_REASON_COMPLETE: Listen offload finished as + * scheduled. + * P2P_LO_STOPPED_REASON_RECV_STOP_CMD: Host requested offload to + * be stopped. + * P2P_LO_STOPPED_REASON_INVALID_PARAM: Invalid listen offload + * parameters. + * P2P_LO_STOPPED_REASON_NOT_SUPPORTED: Listen offload not + * supported by device. + */ + struct p2p_lo_stop { + enum { + P2P_LO_STOPPED_REASON_COMPLETE = 0, + P2P_LO_STOPPED_REASON_RECV_STOP_CMD, + P2P_LO_STOPPED_REASON_INVALID_PARAM, + P2P_LO_STOPPED_REASON_NOT_SUPPORTED, + } reason_code; + } p2p_lo_stop; }; /** @@ -4645,6 +4873,18 @@ union wpa_event_data { void wpa_supplicant_event(void *ctx, enum wpa_event_type event, union wpa_event_data *data); +/** + * wpa_supplicant_event_global - Report a driver event for wpa_supplicant + * @ctx: Context pointer (wpa_s); this is the ctx variable registered + * with struct wpa_driver_ops::init() + * @event: event type (defined above) + * @data: possible extra data for the event + * + * Same as wpa_supplicant_event(), but we search for the interface in + * wpa_global. + */ +void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event, + union wpa_event_data *data); /* * The following inline functions are provided for convenience to simplify @@ -4697,8 +4937,52 @@ int vht_supported(const struct hostapd_hw_modes *mode); struct wowlan_triggers * wpa_get_wowlan_triggers(const char *wowlan_triggers, const struct wpa_driver_capa *capa); +/* Convert driver flag to string */ +const char * driver_flag_to_string(u64 flag); /* NULL terminated array of linked in driver wrappers */ extern const struct wpa_driver_ops *const wpa_drivers[]; + +/* Available drivers */ + +#ifdef CONFIG_DRIVER_WEXT +extern const struct wpa_driver_ops wpa_driver_wext_ops; /* driver_wext.c */ +#endif /* CONFIG_DRIVER_WEXT */ +#ifdef CONFIG_DRIVER_NL80211 +/* driver_nl80211.c */ +extern const struct wpa_driver_ops wpa_driver_nl80211_ops; +#endif /* CONFIG_DRIVER_NL80211 */ +#ifdef CONFIG_DRIVER_HOSTAP +extern const struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */ +#endif /* CONFIG_DRIVER_HOSTAP */ +#ifdef CONFIG_DRIVER_BSD +extern const struct wpa_driver_ops wpa_driver_bsd_ops; /* driver_bsd.c */ +#endif /* CONFIG_DRIVER_BSD */ +#ifdef CONFIG_DRIVER_OPENBSD +/* driver_openbsd.c */ +extern const struct wpa_driver_ops wpa_driver_openbsd_ops; +#endif /* CONFIG_DRIVER_OPENBSD */ +#ifdef CONFIG_DRIVER_NDIS +extern struct wpa_driver_ops wpa_driver_ndis_ops; /* driver_ndis.c */ +#endif /* CONFIG_DRIVER_NDIS */ +#ifdef CONFIG_DRIVER_WIRED +extern const struct wpa_driver_ops wpa_driver_wired_ops; /* driver_wired.c */ +#endif /* CONFIG_DRIVER_WIRED */ +#ifdef CONFIG_DRIVER_MACSEC_QCA +/* driver_macsec_qca.c */ +extern const struct wpa_driver_ops wpa_driver_macsec_qca_ops; +#endif /* CONFIG_DRIVER_MACSEC_QCA */ +#ifdef CONFIG_DRIVER_ROBOSWITCH +/* driver_roboswitch.c */ +extern const struct wpa_driver_ops wpa_driver_roboswitch_ops; +#endif /* CONFIG_DRIVER_ROBOSWITCH */ +#ifdef CONFIG_DRIVER_ATHEROS +/* driver_atheros.c */ +extern const struct wpa_driver_ops wpa_driver_atheros_ops; +#endif /* CONFIG_DRIVER_ATHEROS */ +#ifdef CONFIG_DRIVER_NONE +extern const struct wpa_driver_ops wpa_driver_none_ops; /* driver_none.c */ +#endif /* CONFIG_DRIVER_NONE */ + #endif /* DRIVER_H */ diff --git a/contrib/wpa/src/drivers/driver_bsd.c b/contrib/wpa/src/drivers/driver_bsd.c index 73f455f36ba3..65282228d4f9 100644 --- a/contrib/wpa/src/drivers/driver_bsd.c +++ b/contrib/wpa/src/drivers/driver_bsd.c @@ -47,14 +47,25 @@ #include "l2_packet/l2_packet.h" +struct bsd_driver_global { + void *ctx; + int sock; /* socket for 802.11 ioctls */ + int route; /* routing socket for events */ + char *event_buf; + size_t event_buf_len; + struct dl_list ifaces; /* list of interfaces */ +}; + struct bsd_driver_data { + struct dl_list list; + struct bsd_driver_global *global; struct hostapd_data *hapd; /* back pointer */ - int sock; /* open socket for 802.11 ioctls */ struct l2_packet_data *sock_xmit;/* raw packet xmit socket */ - int route; /* routing socket for events */ char ifname[IFNAMSIZ+1]; /* interface name */ + int flags; unsigned int ifindex; /* interface index */ + int if_removed; /* has the interface been removed? */ void *ctx; struct wpa_driver_capa capa; /* driver capability */ int is_ap; /* Access point mode */ @@ -62,17 +73,46 @@ struct bsd_driver_data { int prev_privacy; /* privacy state to restore on deinit */ int prev_wpa; /* wpa state to restore on deinit */ enum ieee80211_opmode opmode; /* operation mode */ - char *event_buf; - size_t event_buf_len; }; /* Generic functions for hostapd and wpa_supplicant */ +static struct bsd_driver_data * +bsd_get_drvindex(void *priv, unsigned int ifindex) +{ + struct bsd_driver_global *global = priv; + struct bsd_driver_data *drv; + + dl_list_for_each(drv, &global->ifaces, struct bsd_driver_data, list) { + if (drv->ifindex == ifindex) + return drv; + } + return NULL; +} + +#ifndef HOSTAPD +static struct bsd_driver_data * +bsd_get_drvname(void *priv, const char *ifname) +{ + struct bsd_driver_global *global = priv; + struct bsd_driver_data *drv; + + dl_list_for_each(drv, &global->ifaces, struct bsd_driver_data, list) { + if (os_strcmp(drv->ifname, ifname) == 0) + return drv; + } + return NULL; +} +#endif /* HOSTAPD */ + static int bsd_set80211(void *priv, int op, int val, const void *arg, int arg_len) { struct bsd_driver_data *drv = priv; struct ieee80211req ireq; + if (drv->ifindex == 0 || drv->if_removed) + return -1; + os_memset(&ireq, 0, sizeof(ireq)); os_strlcpy(ireq.i_name, drv->ifname, sizeof(ireq.i_name)); ireq.i_type = op; @@ -80,7 +120,7 @@ bsd_set80211(void *priv, int op, int val, const void *arg, int arg_len) ireq.i_data = (void *) arg; ireq.i_len = arg_len; - if (ioctl(drv->sock, SIOCS80211, &ireq) < 0) { + if (ioctl(drv->global->sock, SIOCS80211, &ireq) < 0) { wpa_printf(MSG_ERROR, "ioctl[SIOCS80211, op=%u, val=%u, " "arg_len=%u]: %s", op, val, arg_len, strerror(errno)); @@ -101,7 +141,7 @@ bsd_get80211(void *priv, struct ieee80211req *ireq, int op, void *arg, ireq->i_len = arg_len; ireq->i_data = arg; - if (ioctl(drv->sock, SIOCG80211, ireq) < 0) { + if (ioctl(drv->global->sock, SIOCG80211, ireq) < 0) { wpa_printf(MSG_ERROR, "ioctl[SIOCS80211, op=%u, " "arg_len=%u]: %s", op, arg_len, strerror(errno)); return -1; @@ -142,7 +182,7 @@ bsd_get_ssid(void *priv, u8 *ssid, int len) os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); ifr.ifr_data = (void *)&nwid; - if (ioctl(drv->sock, SIOCG80211NWID, &ifr) < 0 || + if (ioctl(drv->global->sock, SIOCG80211NWID, &ifr) < 0 || nwid.i_len > IEEE80211_NWID_LEN) return -1; os_memcpy(ssid, nwid.i_nwid, nwid.i_len); @@ -165,7 +205,7 @@ bsd_set_ssid(void *priv, const u8 *ssid, int ssid_len) os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); ifr.ifr_data = (void *)&nwid; - return ioctl(drv->sock, SIOCS80211NWID, &ifr); + return ioctl(drv->global->sock, SIOCS80211NWID, &ifr); #else return set80211var(drv, IEEE80211_IOC_SSID, ssid, ssid_len); #endif @@ -180,7 +220,7 @@ bsd_get_if_media(void *priv) os_memset(&ifmr, 0, sizeof(ifmr)); os_strlcpy(ifmr.ifm_name, drv->ifname, sizeof(ifmr.ifm_name)); - if (ioctl(drv->sock, SIOCGIFMEDIA, &ifmr) < 0) { + if (ioctl(drv->global->sock, SIOCGIFMEDIA, &ifmr) < 0) { wpa_printf(MSG_ERROR, "%s: SIOCGIFMEDIA %s", __func__, strerror(errno)); return -1; @@ -199,7 +239,7 @@ bsd_set_if_media(void *priv, int media) os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); ifr.ifr_media = media; - if (ioctl(drv->sock, SIOCSIFMEDIA, &ifr) < 0) { + if (ioctl(drv->global->sock, SIOCSIFMEDIA, &ifr) < 0) { wpa_printf(MSG_ERROR, "%s: SIOCSIFMEDIA %s", __func__, strerror(errno)); return -1; @@ -262,11 +302,12 @@ bsd_ctrl_iface(void *priv, int enable) os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); - if (ioctl(drv->sock, SIOCGIFFLAGS, &ifr) < 0) { + if (ioctl(drv->global->sock, SIOCGIFFLAGS, &ifr) < 0) { wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s", strerror(errno)); return -1; } + drv->flags = ifr.ifr_flags; if (enable) { if (ifr.ifr_flags & IFF_UP) @@ -278,12 +319,13 @@ bsd_ctrl_iface(void *priv, int enable) ifr.ifr_flags &= ~IFF_UP; } - if (ioctl(drv->sock, SIOCSIFFLAGS, &ifr) < 0) { + if (ioctl(drv->global->sock, SIOCSIFFLAGS, &ifr) < 0) { wpa_printf(MSG_ERROR, "ioctl[SIOCSIFFLAGS]: %s", strerror(errno)); return -1; } + drv->flags = ifr.ifr_flags; return 0; } @@ -575,7 +617,7 @@ bsd_set_freq(void *priv, struct hostapd_freq_params *freq) os_memset(&creq, 0, sizeof(creq)); os_strlcpy(creq.i_name, drv->ifname, sizeof(creq.i_name)); creq.i_channel = (u_int16_t)channel; - return ioctl(drv->sock, SIOCS80211CHANNEL, &creq); + return ioctl(drv->global->sock, SIOCS80211CHANNEL, &creq); #else /* SIOCS80211CHANNEL */ return set80211param(priv, IEEE80211_IOC_CHANNEL, channel); #endif /* SIOCS80211CHANNEL */ @@ -729,7 +771,8 @@ bsd_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, static void bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx) { - struct bsd_driver_data *drv = ctx; + struct bsd_driver_global *global = sock_ctx; + struct bsd_driver_data *drv; struct if_announcemsghdr *ifan; struct rt_msghdr *rtm; struct ieee80211_michael_event *mic; @@ -738,7 +781,7 @@ bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx) int n; union wpa_event_data data; - n = read(sock, drv->event_buf, drv->event_buf_len); + n = read(sock, global->event_buf, global->event_buf_len); if (n < 0) { if (errno != EINTR && errno != EAGAIN) wpa_printf(MSG_ERROR, "%s read() failed: %s", @@ -746,15 +789,18 @@ bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx) return; } - rtm = (struct rt_msghdr *) drv->event_buf; + rtm = (struct rt_msghdr *) global->event_buf; if (rtm->rtm_version != RTM_VERSION) { wpa_printf(MSG_DEBUG, "Invalid routing message version=%d", rtm->rtm_version); return; } - ifan = (struct if_announcemsghdr *) rtm; switch (rtm->rtm_type) { case RTM_IEEE80211: + ifan = (struct if_announcemsghdr *) rtm; + drv = bsd_get_drvindex(global, ifan->ifan_index); + if (drv == NULL) + return; switch (ifan->ifan_what) { case RTM_IEEE80211_ASSOC: case RTM_IEEE80211_REASSOC: @@ -810,21 +856,15 @@ bsd_init(struct hostapd_data *hapd, struct wpa_init_params *params) return NULL; } - drv->event_buf_len = rtbuf_len(); - - drv->event_buf = os_malloc(drv->event_buf_len); - if (drv->event_buf == NULL) { - wpa_printf(MSG_ERROR, "%s: os_malloc() failed", __func__); + drv->ifindex = if_nametoindex(params->ifname); + if (drv->ifindex == 0) { + wpa_printf(MSG_DEBUG, "%s: interface %s does not exist", + __func__, params->ifname); goto bad; } drv->hapd = hapd; - drv->sock = socket(PF_INET, SOCK_DGRAM, 0); - if (drv->sock < 0) { - wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s", - strerror(errno)); - goto bad; - } + drv->global = params->global_priv; os_strlcpy(drv->ifname, params->ifname, sizeof(drv->ifname)); drv->sock_xmit = l2_packet_init(drv->ifname, NULL, ETH_P_EAPOL, @@ -838,28 +878,18 @@ bsd_init(struct hostapd_data *hapd, struct wpa_init_params *params) if (bsd_ctrl_iface(drv, 0) < 0) goto bad; - drv->route = socket(PF_ROUTE, SOCK_RAW, 0); - if (drv->route < 0) { - wpa_printf(MSG_ERROR, "socket(PF_ROUTE,SOCK_RAW): %s", - strerror(errno)); - goto bad; - } - eloop_register_read_sock(drv->route, bsd_wireless_event_receive, drv, - NULL); - if (bsd_set_mediaopt(drv, IFM_OMASK, IFM_IEEE80211_HOSTAP) < 0) { wpa_printf(MSG_ERROR, "%s: failed to set operation mode", __func__); goto bad; } + dl_list_add(&drv->global->ifaces, &drv->list); + return drv; bad: if (drv->sock_xmit != NULL) l2_packet_deinit(drv->sock_xmit); - if (drv->sock >= 0) - close(drv->sock); - os_free(drv->event_buf); os_free(drv); return NULL; } @@ -870,16 +900,10 @@ bsd_deinit(void *priv) { struct bsd_driver_data *drv = priv; - if (drv->route >= 0) { - eloop_unregister_read_sock(drv->route); - close(drv->route); - } - bsd_ctrl_iface(drv, 0); - if (drv->sock >= 0) - close(drv->sock); + if (drv->ifindex != 0) + bsd_ctrl_iface(drv, 0); if (drv->sock_xmit != NULL) l2_packet_deinit(drv->sock_xmit); - os_free(drv->event_buf); os_free(drv); } @@ -931,7 +955,7 @@ wpa_driver_bsd_get_bssid(void *priv, u8 *bssid) struct ieee80211_bssid bs; os_strlcpy(bs.i_name, drv->ifname, sizeof(bs.i_name)); - if (ioctl(drv->sock, SIOCG80211BSSID, &bs) < 0) + if (ioctl(drv->global->sock, SIOCG80211BSSID, &bs) < 0) return -1; os_memcpy(bssid, bs.i_bssid, sizeof(bs.i_bssid)); return 0; @@ -965,7 +989,7 @@ wpa_driver_bsd_set_wpa_internal(void *priv, int wpa, int privacy) int ret = 0; wpa_printf(MSG_DEBUG, "%s: wpa=%d privacy=%d", - __FUNCTION__, wpa, privacy); + __func__, wpa, privacy); if (!wpa && wpa_driver_bsd_set_wpa_ie(priv, NULL, 0) < 0) ret = -1; @@ -980,7 +1004,7 @@ wpa_driver_bsd_set_wpa_internal(void *priv, int wpa, int privacy) static int wpa_driver_bsd_set_wpa(void *priv, int enabled) { - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); return wpa_driver_bsd_set_wpa_internal(priv, enabled ? 3 : 0, enabled); } @@ -1199,7 +1223,8 @@ wpa_driver_bsd_scan(void *priv, struct wpa_driver_scan_params *params) static void wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx) { - struct bsd_driver_data *drv = sock_ctx; + struct bsd_driver_global *global = sock_ctx; + struct bsd_driver_data *drv; struct if_announcemsghdr *ifan; struct if_msghdr *ifm; struct rt_msghdr *rtm; @@ -1209,7 +1234,7 @@ wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx) struct ieee80211_join_event *join; int n; - n = read(sock, drv->event_buf, drv->event_buf_len); + n = read(sock, global->event_buf, global->event_buf_len); if (n < 0) { if (errno != EINTR && errno != EAGAIN) wpa_printf(MSG_ERROR, "%s read() failed: %s", @@ -1217,7 +1242,7 @@ wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx) return; } - rtm = (struct rt_msghdr *) drv->event_buf; + rtm = (struct rt_msghdr *) global->event_buf; if (rtm->rtm_version != RTM_VERSION) { wpa_printf(MSG_DEBUG, "Invalid routing message version=%d", rtm->rtm_version); @@ -1227,53 +1252,79 @@ wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx) switch (rtm->rtm_type) { case RTM_IFANNOUNCE: ifan = (struct if_announcemsghdr *) rtm; - if (ifan->ifan_index != drv->ifindex) - break; - os_strlcpy(event.interface_status.ifname, drv->ifname, - sizeof(event.interface_status.ifname)); switch (ifan->ifan_what) { case IFAN_DEPARTURE: + drv = bsd_get_drvindex(global, ifan->ifan_index); + if (drv) + drv->if_removed = 1; event.interface_status.ievent = EVENT_INTERFACE_REMOVED; + break; + case IFAN_ARRIVAL: + drv = bsd_get_drvname(global, ifan->ifan_name); + if (drv) { + drv->ifindex = ifan->ifan_index; + drv->if_removed = 0; + } + event.interface_status.ievent = EVENT_INTERFACE_ADDED; + break; default: + wpa_printf(MSG_DEBUG, "RTM_IFANNOUNCE: unknown action"); return; } wpa_printf(MSG_DEBUG, "RTM_IFANNOUNCE: Interface '%s' %s", - event.interface_status.ifname, + ifan->ifan_name, ifan->ifan_what == IFAN_DEPARTURE ? "removed" : "added"); - wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event); + os_strlcpy(event.interface_status.ifname, ifan->ifan_name, + sizeof(event.interface_status.ifname)); + if (drv) { + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, + &event); + /* + * Set ifindex to zero after sending the event as the + * event might query the driver to ensure a match. + */ + if (ifan->ifan_what == IFAN_DEPARTURE) + drv->ifindex = 0; + } else { + wpa_supplicant_event_global(global->ctx, + EVENT_INTERFACE_STATUS, + &event); + } break; case RTM_IEEE80211: ifan = (struct if_announcemsghdr *) rtm; - if (ifan->ifan_index != drv->ifindex) - break; + drv = bsd_get_drvindex(global, ifan->ifan_index); + if (drv == NULL) + return; switch (ifan->ifan_what) { case RTM_IEEE80211_ASSOC: case RTM_IEEE80211_REASSOC: if (drv->is_ap) break; - wpa_supplicant_event(ctx, EVENT_ASSOC, NULL); + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL); break; case RTM_IEEE80211_DISASSOC: if (drv->is_ap) break; - wpa_supplicant_event(ctx, EVENT_DISASSOC, NULL); + wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); break; case RTM_IEEE80211_SCAN: if (drv->is_ap) break; - wpa_supplicant_event(ctx, EVENT_SCAN_RESULTS, NULL); + wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, + NULL); break; case RTM_IEEE80211_LEAVE: leave = (struct ieee80211_leave_event *) &ifan[1]; - drv_event_disassoc(ctx, leave->iev_addr); + drv_event_disassoc(drv->ctx, leave->iev_addr); break; case RTM_IEEE80211_JOIN: #ifdef RTM_IEEE80211_REJOIN case RTM_IEEE80211_REJOIN: #endif join = (struct ieee80211_join_event *) &ifan[1]; - bsd_new_sta(drv, ctx, join->iev_addr); + bsd_new_sta(drv, drv->ctx, join->iev_addr); break; case RTM_IEEE80211_REPLAY: /* ignore */ @@ -1288,23 +1339,30 @@ wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx) os_memset(&event, 0, sizeof(event)); event.michael_mic_failure.unicast = !IEEE80211_IS_MULTICAST(mic->iev_dst); - wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, - &event); + wpa_supplicant_event(drv->ctx, + EVENT_MICHAEL_MIC_FAILURE, &event); break; } break; case RTM_IFINFO: ifm = (struct if_msghdr *) rtm; - if (ifm->ifm_index != drv->ifindex) - break; - if ((rtm->rtm_flags & RTF_UP) == 0) { - os_strlcpy(event.interface_status.ifname, drv->ifname, - sizeof(event.interface_status.ifname)); - event.interface_status.ievent = EVENT_INTERFACE_REMOVED; + drv = bsd_get_drvindex(global, ifm->ifm_index); + if (drv == NULL) + return; + if ((ifm->ifm_flags & IFF_UP) == 0 && + (drv->flags & IFF_UP) != 0) { wpa_printf(MSG_DEBUG, "RTM_IFINFO: Interface '%s' DOWN", - event.interface_status.ifname); - wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event); + drv->ifname); + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED, + NULL); + } else if ((ifm->ifm_flags & IFF_UP) != 0 && + (drv->flags & IFF_UP) == 0) { + wpa_printf(MSG_DEBUG, "RTM_IFINFO: Interface '%s' UP", + drv->ifname); + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED, + NULL); } + drv->flags = ifm->ifm_flags; break; } } @@ -1331,11 +1389,16 @@ wpa_driver_bsd_add_scan_entry(struct wpa_scan_results *res, result->caps = sr->isr_capinfo; result->qual = sr->isr_rssi; result->noise = sr->isr_noise; + +#ifdef __FreeBSD__ /* * the rssi value reported by the kernel is in 0.5dB steps relative to * the reported noise floor. see ieee80211_node.h for details. */ result->level = sr->isr_rssi / 2 + sr->isr_noise; +#else + result->level = sr->isr_rssi; +#endif pos = (u8 *)(result + 1); @@ -1502,7 +1565,7 @@ get80211opmode(struct bsd_driver_data *drv) (void) memset(&ifmr, 0, sizeof(ifmr)); (void) os_strlcpy(ifmr.ifm_name, drv->ifname, sizeof(ifmr.ifm_name)); - if (ioctl(drv->sock, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) { + if (ioctl(drv->global->sock, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) { if (ifmr.ifm_current & IFM_IEEE80211_ADHOC) { if (ifmr.ifm_current & IFM_FLAG0) return IEEE80211_M_AHDEMO; @@ -1524,7 +1587,7 @@ get80211opmode(struct bsd_driver_data *drv) } static void * -wpa_driver_bsd_init(void *ctx, const char *ifname) +wpa_driver_bsd_init(void *ctx, const char *ifname, void *priv) { #define GETPARAM(drv, param, v) \ (((v) = get80211param(drv, param)) != -1) @@ -1534,14 +1597,6 @@ wpa_driver_bsd_init(void *ctx, const char *ifname) if (drv == NULL) return NULL; - drv->event_buf_len = rtbuf_len(); - - drv->event_buf = os_malloc(drv->event_buf_len); - if (drv->event_buf == NULL) { - wpa_printf(MSG_ERROR, "%s: os_malloc() failed", __func__); - goto fail1; - } - /* * NB: We require the interface name be mappable to an index. * This implies we do not support having wpa_supplicant @@ -1552,24 +1607,12 @@ wpa_driver_bsd_init(void *ctx, const char *ifname) if (drv->ifindex == 0) { wpa_printf(MSG_DEBUG, "%s: interface %s does not exist", __func__, ifname); - goto fail1; + goto fail; } - drv->sock = socket(PF_INET, SOCK_DGRAM, 0); - if (drv->sock < 0) - goto fail1; - - os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); - /* Down interface during setup. */ - if (bsd_ctrl_iface(drv, 0) < 0) - goto fail; - - drv->route = socket(PF_ROUTE, SOCK_RAW, 0); - if (drv->route < 0) - goto fail; - eloop_register_read_sock(drv->route, - wpa_driver_bsd_event_receive, ctx, drv); drv->ctx = ctx; + drv->global = priv; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); if (!GETPARAM(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming)) { wpa_printf(MSG_DEBUG, "%s: failed to get roaming state: %s", @@ -1590,13 +1633,15 @@ wpa_driver_bsd_init(void *ctx, const char *ifname) if (wpa_driver_bsd_capa(drv)) goto fail; + /* Down interface during setup. */ + if (bsd_ctrl_iface(drv, 0) < 0) + goto fail; + drv->opmode = get80211opmode(drv); + dl_list_add(&drv->global->ifaces, &drv->list); return drv; fail: - close(drv->sock); -fail1: - os_free(drv->event_buf); os_free(drv); return NULL; #undef GETPARAM @@ -1607,22 +1652,25 @@ wpa_driver_bsd_deinit(void *priv) { struct bsd_driver_data *drv = priv; - wpa_driver_bsd_set_wpa(drv, 0); - eloop_unregister_read_sock(drv->route); + if (drv->ifindex != 0 && !drv->if_removed) { + wpa_driver_bsd_set_wpa(drv, 0); - /* NB: mark interface down */ - bsd_ctrl_iface(drv, 0); + /* NB: mark interface down */ + bsd_ctrl_iface(drv, 0); - wpa_driver_bsd_set_wpa_internal(drv, drv->prev_wpa, drv->prev_privacy); - if (set80211param(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming) < 0) - wpa_printf(MSG_DEBUG, "%s: failed to restore roaming state", - __func__); + wpa_driver_bsd_set_wpa_internal(drv, drv->prev_wpa, + drv->prev_privacy); + + if (set80211param(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming) + < 0) + wpa_printf(MSG_DEBUG, + "%s: failed to restore roaming state", + __func__); + } if (drv->sock_xmit != NULL) l2_packet_deinit(drv->sock_xmit); - (void) close(drv->route); /* ioctl socket */ - (void) close(drv->sock); /* event socket */ - os_free(drv->event_buf); + dl_list_del(&drv->list); os_free(drv); } @@ -1636,10 +1684,74 @@ wpa_driver_bsd_get_capa(void *priv, struct wpa_driver_capa *capa) } #endif /* HOSTAPD */ +static void * +bsd_global_init(void *ctx) +{ + struct bsd_driver_global *global; + + global = os_zalloc(sizeof(*global)); + if (global == NULL) + return NULL; + + global->ctx = ctx; + dl_list_init(&global->ifaces); + + global->sock = socket(PF_INET, SOCK_DGRAM, 0); + if (global->sock < 0) { + wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s", + strerror(errno)); + goto fail1; + } + + global->route = socket(PF_ROUTE, SOCK_RAW, 0); + if (global->route < 0) { + wpa_printf(MSG_ERROR, "socket[PF_ROUTE,SOCK_RAW]: %s", + strerror(errno)); + goto fail; + } + + global->event_buf_len = rtbuf_len(); + global->event_buf = os_malloc(global->event_buf_len); + if (global->event_buf == NULL) { + wpa_printf(MSG_ERROR, "%s: os_malloc() failed", __func__); + goto fail; + } + +#ifdef HOSTAPD + eloop_register_read_sock(global->route, bsd_wireless_event_receive, + NULL, global); + +#else /* HOSTAPD */ + eloop_register_read_sock(global->route, wpa_driver_bsd_event_receive, + NULL, global); +#endif /* HOSTAPD */ + + return global; + +fail: + close(global->sock); +fail1: + os_free(global); + return NULL; +} + +static void +bsd_global_deinit(void *priv) +{ + struct bsd_driver_global *global = priv; + + eloop_unregister_read_sock(global->route); + (void) close(global->route); + (void) close(global->sock); + os_free(global); +} + const struct wpa_driver_ops wpa_driver_bsd_ops = { .name = "bsd", .desc = "BSD 802.11 support", + .global_init = bsd_global_init, + .global_deinit = bsd_global_deinit, #ifdef HOSTAPD .hapd_init = bsd_init, .hapd_deinit = bsd_deinit, @@ -1652,7 +1764,7 @@ const struct wpa_driver_ops wpa_driver_bsd_ops = { .sta_set_flags = bsd_set_sta_authorized, .commit = bsd_commit, #else /* HOSTAPD */ - .init = wpa_driver_bsd_init, + .init2 = wpa_driver_bsd_init, .deinit = wpa_driver_bsd_deinit, .get_bssid = wpa_driver_bsd_get_bssid, .get_ssid = wpa_driver_bsd_get_ssid, diff --git a/contrib/wpa/src/drivers/driver_common.c b/contrib/wpa/src/drivers/driver_common.c index aebea8cf64e3..c7107ba899b0 100644 --- a/contrib/wpa/src/drivers/driver_common.c +++ b/contrib/wpa/src/drivers/driver_common.c @@ -80,6 +80,7 @@ const char * event_to_string(enum wpa_event_type event) E2S(NEW_PEER_CANDIDATE); E2S(ACS_CHANNEL_SELECTED); E2S(DFS_CAC_STARTED); + E2S(P2P_LO_STOP); } return "UNKNOWN"; @@ -183,12 +184,12 @@ wpa_get_wowlan_triggers(const char *wowlan_triggers, start = buf; while (*start != '\0') { - while (isblank(*start)) + while (isblank((unsigned char) *start)) start++; if (*start == '\0') break; end = start; - while (!isblank(*end) && *end != '\0') + while (!isblank((unsigned char) *end) && *end != '\0') end++; last = *end == '\0'; *end = '\0'; @@ -218,3 +219,55 @@ out: os_free(buf); return triggers; } + + +const char * driver_flag_to_string(u64 flag) +{ +#define DF2S(x) case WPA_DRIVER_FLAGS_ ## x: return #x + switch (flag) { + DF2S(DRIVER_IE); + DF2S(SET_KEYS_AFTER_ASSOC); + DF2S(DFS_OFFLOAD); + DF2S(4WAY_HANDSHAKE); + DF2S(WIRED); + DF2S(SME); + DF2S(AP); + DF2S(SET_KEYS_AFTER_ASSOC_DONE); + DF2S(HT_2040_COEX); + DF2S(P2P_CONCURRENT); + DF2S(P2P_DEDICATED_INTERFACE); + DF2S(P2P_CAPABLE); + DF2S(AP_TEARDOWN_SUPPORT); + DF2S(P2P_MGMT_AND_NON_P2P); + DF2S(SANE_ERROR_CODES); + DF2S(OFFCHANNEL_TX); + DF2S(EAPOL_TX_STATUS); + DF2S(DEAUTH_TX_STATUS); + DF2S(BSS_SELECTION); + DF2S(TDLS_SUPPORT); + DF2S(TDLS_EXTERNAL_SETUP); + DF2S(PROBE_RESP_OFFLOAD); + DF2S(AP_UAPSD); + DF2S(INACTIVITY_TIMER); + DF2S(AP_MLME); + DF2S(SAE); + DF2S(OBSS_SCAN); + DF2S(IBSS); + DF2S(RADAR); + DF2S(DEDICATED_P2P_DEVICE); + DF2S(QOS_MAPPING); + DF2S(AP_CSA); + DF2S(MESH); + DF2S(ACS_OFFLOAD); + DF2S(KEY_MGMT_OFFLOAD); + DF2S(TDLS_CHANNEL_SWITCH); + DF2S(HT_IBSS); + DF2S(VHT_IBSS); + DF2S(SUPPORT_HW_MODE_ANY); + DF2S(OFFCHANNEL_SIMULTANEOUS); + DF2S(FULL_AP_CLIENT_STATE); + DF2S(P2P_LISTEN_OFFLOAD); + } + return "UNKNOWN"; +#undef DF2S +} diff --git a/contrib/wpa/src/drivers/driver_macsec_qca.c b/contrib/wpa/src/drivers/driver_macsec_qca.c index 3eae2f89d20e..826d3cc62133 100644 --- a/contrib/wpa/src/drivers/driver_macsec_qca.c +++ b/contrib/wpa/src/drivers/driver_macsec_qca.c @@ -11,6 +11,7 @@ #include "includes.h" #include #include +#include #ifdef __linux__ #include #include @@ -485,15 +486,12 @@ static int macsec_qca_set_replay_protect(void *priv, Boolean enabled, } -static int macsec_qca_set_current_cipher_suite(void *priv, const u8 *cs, - size_t cs_len) +static int macsec_qca_set_current_cipher_suite(void *priv, u64 cs) { - u8 default_cs_id[] = CS_ID_GCM_AES_128; - - if (cs_len != CS_ID_LEN || - os_memcmp(cs, default_cs_id, cs_len) != 0) { - wpa_hexdump(MSG_ERROR, "macsec: NOT supported CipherSuite", - cs, cs_len); + if (cs != CS_ID_GCM_AES_128) { + wpa_printf(MSG_ERROR, + "%s: NOT supported CipherSuite: %016" PRIx64, + __func__, cs); return -1; } diff --git a/contrib/wpa/src/drivers/driver_ndis.c b/contrib/wpa/src/drivers/driver_ndis.c index 2f5ade68194e..7c86edd3d145 100644 --- a/contrib/wpa/src/drivers/driver_ndis.c +++ b/contrib/wpa/src/drivers/driver_ndis.c @@ -35,6 +35,7 @@ int close(int fd); #include "driver.h" #include "eloop.h" #include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" #include "driver_ndis.h" int wpa_driver_register_event_cb(struct wpa_driver_ndis_data *drv); @@ -781,20 +782,7 @@ static int wpa_driver_ndis_scan(void *priv, static const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie) { - const u8 *end, *pos; - - pos = (const u8 *) (res + 1); - end = pos + res->ie_len; - - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) - break; - if (pos[0] == ie) - return pos; - pos += 2 + pos[1]; - } - - return NULL; + return get_ie((const u8 *) (res + 1), res->ie_len, ie); } diff --git a/contrib/wpa/src/drivers/driver_nl80211.h b/contrib/wpa/src/drivers/driver_nl80211.h index 5c21e0faf55c..d0ec48c9f973 100644 --- a/contrib/wpa/src/drivers/driver_nl80211.h +++ b/contrib/wpa/src/drivers/driver_nl80211.h @@ -25,6 +25,7 @@ #endif /* CONFIG_LIBNL20 */ struct nl80211_global { + void *ctx; struct dl_list interfaces; int if_add_ifindex; u64 if_add_wdevid; @@ -84,6 +85,7 @@ struct wpa_driver_nl80211_data { struct dl_list list; struct dl_list wiphy_list; char phyname[32]; + unsigned int wiphy_idx; u8 perm_addr[ETH_ALEN]; void *ctx; int ifindex; @@ -94,6 +96,13 @@ struct wpa_driver_nl80211_data { struct wpa_driver_capa capa; u8 *extended_capa, *extended_capa_mask; unsigned int extended_capa_len; + struct drv_nl80211_ext_capa { + enum nl80211_iftype iftype; + u8 *ext_capa, *ext_capa_mask; + unsigned int ext_capa_len; + } iface_ext_capa[NL80211_IFTYPE_MAX]; + unsigned int num_iface_ext_capa; + int has_capability; int operstate; @@ -148,9 +157,16 @@ struct wpa_driver_nl80211_data { unsigned int setband_vendor_cmd_avail:1; unsigned int get_pref_freq_list:1; unsigned int set_prob_oper_freq:1; + unsigned int scan_vendor_cmd_avail:1; + unsigned int connect_reassoc:1; + unsigned int set_wifi_conf_vendor_cmd_avail:1; + u64 vendor_scan_cookie; u64 remain_on_chan_cookie; u64 send_action_cookie; +#define MAX_SEND_ACTION_COOKIES 20 + u64 send_action_cookies[MAX_SEND_ACTION_COOKIES]; + unsigned int num_send_action_cookies; unsigned int last_mgmt_freq; @@ -166,7 +182,10 @@ struct wpa_driver_nl80211_data { struct nl_handle *rtnl_sk; /* nl_sock for NETLINK_ROUTE */ int default_if_indices[16]; + /* the AP/AP_VLAN iface that is in this bridge */ + int default_if_indices_reason[16]; int *if_indices; + int *if_indices_reason; int num_if_indices; /* From failed authentication command */ @@ -182,6 +201,13 @@ struct wpa_driver_nl80211_data { int auth_wep_tx_keyidx; int auth_local_state_change; int auth_p2p; + + /* + * Tells whether the last scan issued from wpa_supplicant was a normal + * scan (NL80211_CMD_TRIGGER_SCAN) or a vendor scan + * (NL80211_CMD_VENDOR). 0 if no pending scan request. + */ + int last_scan_cmd; }; struct nl_msg; @@ -233,6 +259,8 @@ nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags); int process_global_event(struct nl_msg *msg, void *arg); int process_bss_event(struct nl_msg *msg, void *arg); +const char * nl80211_iftype_str(enum nl80211_iftype mode); + #ifdef ANDROID int android_nl_socket_set_nonblocking(struct nl_handle *handle); int android_pno_start(struct i802_bss *bss, @@ -267,11 +295,13 @@ void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx); int wpa_driver_nl80211_scan(struct i802_bss *bss, struct wpa_driver_scan_params *params); int wpa_driver_nl80211_sched_scan(void *priv, - struct wpa_driver_scan_params *params, - u32 interval); + struct wpa_driver_scan_params *params); int wpa_driver_nl80211_stop_sched_scan(void *priv); struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv); void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv); -const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie); +int wpa_driver_nl80211_abort_scan(void *priv); +int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss, + struct wpa_driver_scan_params *params); +int nl80211_set_default_scan_ies(void *priv, const u8 *ies, size_t ies_len); #endif /* DRIVER_NL80211_H */ diff --git a/contrib/wpa/src/drivers/driver_nl80211_capa.c b/contrib/wpa/src/drivers/driver_nl80211_capa.c index 4cf31238aeb7..6adc3f6d33dc 100644 --- a/contrib/wpa/src/drivers/driver_nl80211_capa.c +++ b/contrib/wpa/src/drivers/driver_nl80211_capa.c @@ -66,7 +66,6 @@ struct wiphy_info_data { unsigned int device_ap_sme:1; unsigned int poll_command_supported:1; unsigned int data_tx_status:1; - unsigned int monitor_supported:1; unsigned int auth_supported:1; unsigned int connect_supported:1; unsigned int p2p_go_supported:1; @@ -129,9 +128,6 @@ static void wiphy_info_supported_iftypes(struct wiphy_info_data *info, case NL80211_IFTYPE_P2P_CLIENT: info->p2p_client_supported = 1; break; - case NL80211_IFTYPE_MONITOR: - info->monitor_supported = 1; - break; } } } @@ -352,13 +348,20 @@ static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info, struct nlattr *tb) { struct wpa_driver_capa *capa = info->capa; + u8 *ext_features; + int len; if (tb == NULL) return; - if (ext_feature_isset(nla_data(tb), nla_len(tb), - NL80211_EXT_FEATURE_VHT_IBSS)) + ext_features = nla_data(tb); + len = nla_len(tb); + + if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_VHT_IBSS)) capa->flags |= WPA_DRIVER_FLAGS_VHT_IBSS; + + if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_RRM)) + capa->rrm_flags |= WPA_DRIVER_FLAGS_SUPPORT_RRM; } @@ -428,6 +431,9 @@ static void wiphy_info_feature_flags(struct wiphy_info_data *info, if (flags & NL80211_FEATURE_HT_IBSS) capa->flags |= WPA_DRIVER_FLAGS_HT_IBSS; + + if (flags & NL80211_FEATURE_FULL_AP_CLIENT_STATE) + capa->flags |= WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE; } @@ -476,6 +482,74 @@ static void wiphy_info_wowlan_triggers(struct wpa_driver_capa *capa, } +static void wiphy_info_extended_capab(struct wpa_driver_nl80211_data *drv, + struct nlattr *tb) +{ + int rem = 0, i; + struct nlattr *tb1[NL80211_ATTR_MAX + 1], *attr; + + if (!tb || drv->num_iface_ext_capa == NL80211_IFTYPE_MAX) + return; + + nla_for_each_nested(attr, tb, rem) { + unsigned int len; + struct drv_nl80211_ext_capa *capa; + + nla_parse(tb1, NL80211_ATTR_MAX, nla_data(attr), + nla_len(attr), NULL); + + if (!tb1[NL80211_ATTR_IFTYPE] || + !tb1[NL80211_ATTR_EXT_CAPA] || + !tb1[NL80211_ATTR_EXT_CAPA_MASK]) + continue; + + capa = &drv->iface_ext_capa[drv->num_iface_ext_capa]; + capa->iftype = nla_get_u32(tb1[NL80211_ATTR_IFTYPE]); + wpa_printf(MSG_DEBUG, + "nl80211: Driver-advertised extended capabilities for interface type %s", + nl80211_iftype_str(capa->iftype)); + + len = nla_len(tb1[NL80211_ATTR_EXT_CAPA]); + capa->ext_capa = os_malloc(len); + if (!capa->ext_capa) + goto err; + + os_memcpy(capa->ext_capa, nla_data(tb1[NL80211_ATTR_EXT_CAPA]), + len); + capa->ext_capa_len = len; + wpa_hexdump(MSG_DEBUG, "nl80211: Extended capabilities", + capa->ext_capa, capa->ext_capa_len); + + len = nla_len(tb1[NL80211_ATTR_EXT_CAPA_MASK]); + capa->ext_capa_mask = os_malloc(len); + if (!capa->ext_capa_mask) + goto err; + + os_memcpy(capa->ext_capa_mask, + nla_data(tb1[NL80211_ATTR_EXT_CAPA_MASK]), len); + wpa_hexdump(MSG_DEBUG, "nl80211: Extended capabilities mask", + capa->ext_capa_mask, capa->ext_capa_len); + + drv->num_iface_ext_capa++; + if (drv->num_iface_ext_capa == NL80211_IFTYPE_MAX) + break; + } + + return; + +err: + /* Cleanup allocated memory on error */ + for (i = 0; i < NL80211_IFTYPE_MAX; i++) { + os_free(drv->iface_ext_capa[i].ext_capa); + drv->iface_ext_capa[i].ext_capa = NULL; + os_free(drv->iface_ext_capa[i].ext_capa_mask); + drv->iface_ext_capa[i].ext_capa_mask = NULL; + drv->iface_ext_capa[i].ext_capa_len = 0; + } + drv->num_iface_ext_capa = 0; +} + + static int wiphy_info_handler(struct nl_msg *msg, void *arg) { struct nlattr *tb[NL80211_ATTR_MAX + 1]; @@ -487,6 +561,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); + if (tb[NL80211_ATTR_WIPHY]) + drv->wiphy_idx = nla_get_u32(tb[NL80211_ATTR_WIPHY]); + if (tb[NL80211_ATTR_WIPHY_NAME]) os_strlcpy(drv->phyname, nla_get_string(tb[NL80211_ATTR_WIPHY_NAME]), @@ -499,6 +576,19 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) capa->max_sched_scan_ssids = nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]); + if (tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS] && + tb[NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL] && + tb[NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS]) { + capa->max_sched_scan_plans = + nla_get_u32(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS]); + + capa->max_sched_scan_plan_interval = + nla_get_u32(tb[NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL]); + + capa->max_sched_scan_plan_iterations = + nla_get_u32(tb[NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS]); + } + if (tb[NL80211_ATTR_MAX_MATCH_SETS]) capa->max_match_sets = nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]); @@ -550,6 +640,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) nla_len(tb[NL80211_ATTR_EXT_CAPA])); drv->extended_capa_len = nla_len(tb[NL80211_ATTR_EXT_CAPA]); + wpa_hexdump(MSG_DEBUG, + "nl80211: Driver-advertised extended capabilities (default)", + drv->extended_capa, drv->extended_capa_len); } drv->extended_capa_mask = os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA_MASK])); @@ -557,6 +650,10 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) os_memcpy(drv->extended_capa_mask, nla_data(tb[NL80211_ATTR_EXT_CAPA_MASK]), nla_len(tb[NL80211_ATTR_EXT_CAPA_MASK])); + wpa_hexdump(MSG_DEBUG, + "nl80211: Driver-advertised extended capabilities mask (default)", + drv->extended_capa_mask, + drv->extended_capa_len); } else { os_free(drv->extended_capa); drv->extended_capa = NULL; @@ -564,6 +661,8 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) } } + wiphy_info_extended_capab(drv, tb[NL80211_ATTR_IFTYPE_EXT_CAPA]); + if (tb[NL80211_ATTR_VENDOR_DATA]) { struct nlattr *nl; int rem; @@ -580,6 +679,7 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) case QCA_NL80211_VENDOR_SUBCMD_TEST: drv->vendor_cmd_test_avail = 1; break; +#ifdef CONFIG_DRIVER_NL80211_QCA case QCA_NL80211_VENDOR_SUBCMD_ROAMING: drv->roaming_vendor_cmd_avail = 1; break; @@ -602,6 +702,13 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) case QCA_NL80211_VENDOR_SUBCMD_SETBAND: drv->setband_vendor_cmd_avail = 1; break; + case QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN: + drv->scan_vendor_cmd_avail = 1; + break; + case QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION: + drv->set_wifi_conf_vendor_cmd_avail = 1; + break; +#endif /* CONFIG_DRIVER_NL80211_QCA */ } } @@ -633,6 +740,10 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) capa->max_stations = nla_get_u32(tb[NL80211_ATTR_MAX_AP_ASSOC_STA]); + if (tb[NL80211_ATTR_MAX_CSA_COUNTERS]) + capa->max_csa_counters = + nla_get_u8(tb[NL80211_ATTR_MAX_CSA_COUNTERS]); + return NL_SKIP; } @@ -689,8 +800,6 @@ static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv, if (!drv->capa.max_remain_on_chan) drv->capa.max_remain_on_chan = 5000; - if (info->channel_switch_supported) - drv->capa.flags |= WPA_DRIVER_FLAGS_AP_CSA; drv->capa.wmm_ac_supported = info->wmm_ac_supported; drv->capa.mac_addr_rand_sched_scan_supported = @@ -698,10 +807,24 @@ static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv, drv->capa.mac_addr_rand_scan_supported = info->mac_addr_rand_scan_supported; + if (info->channel_switch_supported) { + drv->capa.flags |= WPA_DRIVER_FLAGS_AP_CSA; + if (!drv->capa.max_csa_counters) + drv->capa.max_csa_counters = 1; + } + + if (!drv->capa.max_sched_scan_plans) { + drv->capa.max_sched_scan_plans = 1; + drv->capa.max_sched_scan_plan_interval = UINT32_MAX; + drv->capa.max_sched_scan_plan_iterations = 0; + } + return 0; } +#ifdef CONFIG_DRIVER_NL80211_QCA + static int dfs_info_handler(struct nl_msg *msg, void *arg) { struct nlattr *tb[NL80211_ATTR_MAX + 1]; @@ -780,8 +903,12 @@ static int features_info_handler(struct nl_msg *msg, void *arg) attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_FEATURE_FLAGS]; if (attr) { - info->flags = nla_data(attr); - info->flags_len = nla_len(attr); + int len = nla_len(attr); + info->flags = os_malloc(len); + if (info->flags != NULL) { + os_memcpy(info->flags, nla_data(attr), len); + info->flags_len = len; + } } attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_CONCURRENCY_CAPA]; if (attr) @@ -840,8 +967,17 @@ static void qca_nl80211_get_features(struct wpa_driver_nl80211_data *drv) if (check_feature(QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY, &info)) drv->capa.flags |= WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY; + + if (check_feature(QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS, + &info)) + drv->capa.flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS; + if (check_feature(QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD, &info)) + drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD; + os_free(info.flags); } +#endif /* CONFIG_DRIVER_NL80211_QCA */ + int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) { @@ -898,21 +1034,8 @@ int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) * If poll command and tx status are supported, mac80211 is new enough * to have everything we need to not need monitor interfaces. */ - drv->use_monitor = !info.poll_command_supported || !info.data_tx_status; - - if (drv->device_ap_sme && drv->use_monitor) { - /* - * Non-mac80211 drivers may not support monitor interface. - * Make sure we do not get stuck with incorrect capability here - * by explicitly testing this. - */ - if (!info.monitor_supported) { - wpa_printf(MSG_DEBUG, "nl80211: Disable use_monitor " - "with device_ap_sme since no monitor mode " - "support detected"); - drv->use_monitor = 0; - } - } + drv->use_monitor = !info.device_ap_sme && + (!info.poll_command_supported || !info.data_tx_status); /* * If we aren't going to use monitor interfaces, but the @@ -922,9 +1045,21 @@ int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) if (!drv->use_monitor && !info.data_tx_status) drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS; +#ifdef CONFIG_DRIVER_NL80211_QCA qca_nl80211_check_dfs_capa(drv); qca_nl80211_get_features(drv); + /* + * To enable offchannel simultaneous support in wpa_supplicant, the + * underlying driver needs to support the same along with offchannel TX. + * Offchannel TX support is needed since remain_on_channel and + * action_tx use some common data structures and hence cannot be + * scheduled simultaneously. + */ + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX)) + drv->capa.flags &= ~WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS; +#endif /* CONFIG_DRIVER_NL80211_QCA */ + return 0; } @@ -933,6 +1068,7 @@ struct phy_info_arg { u16 *num_modes; struct hostapd_hw_modes *modes; int last_mode, last_chan_idx; + int failed; }; static void phy_info_ht_capa(struct hostapd_hw_modes *mode, struct nlattr *capa, @@ -1050,7 +1186,7 @@ static int phy_info_freqs(struct phy_info_arg *phy_info, mode->num_channels + new_channels, sizeof(struct hostapd_channel_data)); if (!channel) - return NL_SKIP; + return NL_STOP; mode->channels = channel; mode->num_channels += new_channels; @@ -1096,7 +1232,7 @@ static int phy_info_rates(struct hostapd_hw_modes *mode, struct nlattr *tb) mode->rates = os_calloc(mode->num_rates, sizeof(int)); if (!mode->rates) - return NL_SKIP; + return NL_STOP; idx = 0; @@ -1125,8 +1261,10 @@ static int phy_info_band(struct phy_info_arg *phy_info, struct nlattr *nl_band) mode = os_realloc_array(phy_info->modes, *phy_info->num_modes + 1, sizeof(*mode)); - if (!mode) - return NL_SKIP; + if (!mode) { + phy_info->failed = 1; + return NL_STOP; + } phy_info->modes = mode; mode = &phy_info->modes[*(phy_info->num_modes)]; @@ -1162,11 +1300,12 @@ static int phy_info_band(struct phy_info_arg *phy_info, struct nlattr *nl_band) phy_info_vht_capa(mode, tb_band[NL80211_BAND_ATTR_VHT_CAPA], tb_band[NL80211_BAND_ATTR_VHT_MCS_SET]); ret = phy_info_freqs(phy_info, mode, tb_band[NL80211_BAND_ATTR_FREQS]); - if (ret != NL_OK) - return ret; - ret = phy_info_rates(mode, tb_band[NL80211_BAND_ATTR_RATES]); - if (ret != NL_OK) + if (ret == NL_OK) + ret = phy_info_rates(mode, tb_band[NL80211_BAND_ATTR_RATES]); + if (ret != NL_OK) { + phy_info->failed = 1; return ret; + } return NL_OK; } @@ -1381,7 +1520,7 @@ static void nl80211_reg_rule_sec(struct nlattr *tb[], static void nl80211_set_vht_mode(struct hostapd_hw_modes *mode, int start, - int end) + int end, int max_bw) { int c; @@ -1398,6 +1537,32 @@ static void nl80211_set_vht_mode(struct hostapd_hw_modes *mode, int start, if (chan->freq - 70 >= start && chan->freq + 10 <= end) chan->flag |= HOSTAPD_CHAN_VHT_70_10; + + if (max_bw >= 160) { + if (chan->freq - 10 >= start && chan->freq + 150 <= end) + chan->flag |= HOSTAPD_CHAN_VHT_10_150; + + if (chan->freq - 30 >= start && chan->freq + 130 <= end) + chan->flag |= HOSTAPD_CHAN_VHT_30_130; + + if (chan->freq - 50 >= start && chan->freq + 110 <= end) + chan->flag |= HOSTAPD_CHAN_VHT_50_110; + + if (chan->freq - 70 >= start && chan->freq + 90 <= end) + chan->flag |= HOSTAPD_CHAN_VHT_70_90; + + if (chan->freq - 90 >= start && chan->freq + 70 <= end) + chan->flag |= HOSTAPD_CHAN_VHT_90_70; + + if (chan->freq - 110 >= start && chan->freq + 50 <= end) + chan->flag |= HOSTAPD_CHAN_VHT_110_50; + + if (chan->freq - 130 >= start && chan->freq + 30 <= end) + chan->flag |= HOSTAPD_CHAN_VHT_130_30; + + if (chan->freq - 150 >= start && chan->freq + 10 <= end) + chan->flag |= HOSTAPD_CHAN_VHT_150_10; + } } } @@ -1428,7 +1593,7 @@ static void nl80211_reg_rule_vht(struct nlattr *tb[], if (!results->modes[m].vht_capab) continue; - nl80211_set_vht_mode(&results->modes[m], start, end); + nl80211_set_vht_mode(&results->modes[m], start, end, max_bw); } } @@ -1566,6 +1731,7 @@ nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) .num_modes = num_modes, .modes = NULL, .last_mode = -1, + .failed = 0, }; *num_modes = 0; @@ -1582,6 +1748,16 @@ nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) { nl80211_set_regulatory_flags(drv, &result); + if (result.failed) { + int i; + + for (i = 0; result.modes && i < *num_modes; i++) { + os_free(result.modes[i].channels); + os_free(result.modes[i].rates); + } + os_free(result.modes); + return NULL; + } return wpa_driver_nl80211_postprocess_modes(result.modes, num_modes); } diff --git a/contrib/wpa/src/drivers/driver_nl80211_event.c b/contrib/wpa/src/drivers/driver_nl80211_event.c index 7b0f721e6584..762e3acc2807 100644 --- a/contrib/wpa/src/drivers/driver_nl80211_event.c +++ b/contrib/wpa/src/drivers/driver_nl80211_event.c @@ -265,10 +265,12 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, enum nl80211_commands cmd, struct nlattr *status, struct nlattr *addr, struct nlattr *req_ie, struct nlattr *resp_ie, + struct nlattr *timed_out, struct nlattr *authorized, struct nlattr *key_replay_ctr, struct nlattr *ptk_kck, - struct nlattr *ptk_kek) + struct nlattr *ptk_kek, + struct nlattr *subnet_status) { union wpa_event_data event; const u8 *ssid; @@ -284,6 +286,8 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, return; } + drv->connect_reassoc = 0; + status_code = status ? nla_get_u16(status) : WLAN_STATUS_SUCCESS; if (cmd == NL80211_CMD_CONNECT) { @@ -319,6 +323,7 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, event.assoc_reject.resp_ies_len = nla_len(resp_ie); } event.assoc_reject.status_code = status_code; + event.assoc_reject.timed_out = timed_out != NULL; wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event); return; } @@ -334,9 +339,9 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, event.assoc_info.req_ies_len = nla_len(req_ie); if (cmd == NL80211_CMD_ROAM) { - ssid = nl80211_get_ie(event.assoc_info.req_ies, - event.assoc_info.req_ies_len, - WLAN_EID_SSID); + ssid = get_ie(event.assoc_info.req_ies, + event.assoc_info.req_ies_len, + WLAN_EID_SSID); if (ssid && ssid[1] > 0 && ssid[1] <= 32) { drv->ssid_len = ssid[1]; os_memcpy(drv->ssid, ssid + 2, ssid[1]); @@ -367,6 +372,17 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, event.assoc_info.ptk_kek_len = nla_len(ptk_kek); } + if (subnet_status) { + /* + * At least for now, this is only available from + * QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS and that + * attribute has the same values 0, 1, 2 as are used in the + * variable here, so no mapping between different values are + * needed. + */ + event.assoc_info.subnet_status = nla_get_u8(subnet_status); + } + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event); } @@ -560,9 +576,10 @@ static void mlme_event_mgmt(struct i802_bss *bss, rx_freq = drv->last_mgmt_freq = event.rx_mgmt.freq; } wpa_printf(MSG_DEBUG, - "nl80211: RX frame sa=" MACSTR + "nl80211: RX frame da=" MACSTR " sa=" MACSTR " bssid=" MACSTR " freq=%d ssi_signal=%d fc=0x%x seq_ctrl=0x%x stype=%u (%s) len=%u", - MAC2STR(mgmt->sa), rx_freq, ssi_signal, fc, + MAC2STR(mgmt->da), MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid), + rx_freq, ssi_signal, fc, le_to_host16(mgmt->seq_ctrl), stype, fc2str(fc), (unsigned int) len); event.rx_mgmt.frame = frame; @@ -639,10 +656,39 @@ static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv, * Avoid issues with some roaming cases where * disconnection event for the old AP may show up after * we have started connection with the new AP. + * In case of locally generated event clear + * ignore_next_local_deauth as well, to avoid next local + * deauth event be wrongly ignored. */ - wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth/disassoc event from old AP " MACSTR " when already authenticating with " MACSTR, - MAC2STR(bssid), - MAC2STR(drv->auth_attempt_bssid)); + if (!os_memcmp(mgmt->sa, drv->first_bss->addr, + ETH_ALEN)) { + wpa_printf(MSG_DEBUG, + "nl80211: Received a locally generated deauth event. Clear ignore_next_local_deauth flag"); + drv->ignore_next_local_deauth = 0; + } else { + wpa_printf(MSG_DEBUG, + "nl80211: Ignore deauth/disassoc event from old AP " MACSTR " when already authenticating with " MACSTR, + MAC2STR(bssid), + MAC2STR(drv->auth_attempt_bssid)); + } + return; + } + + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) && + drv->connect_reassoc && drv->associated && + os_memcmp(bssid, drv->prev_bssid, ETH_ALEN) == 0 && + os_memcmp(bssid, drv->auth_attempt_bssid, ETH_ALEN) != 0) { + /* + * Avoid issues with some roaming cases where + * disconnection event for the old AP may show up after + * we have started connection with the new AP. + */ + wpa_printf(MSG_DEBUG, + "nl80211: Ignore deauth/disassoc event from old AP " + MACSTR + " when already connecting with " MACSTR, + MAC2STR(bssid), + MAC2STR(drv->auth_attempt_bssid)); return; } @@ -679,13 +725,15 @@ static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv, mgmt->u.disassoc.variable; } } else { + event.deauth_info.locally_generated = + !os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN); if (drv->ignore_deauth_event) { wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth event due to previous forced deauth-during-auth"); drv->ignore_deauth_event = 0; + if (event.deauth_info.locally_generated) + drv->ignore_next_local_deauth = 0; return; } - event.deauth_info.locally_generated = - !os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN); if (drv->ignore_next_local_deauth) { drv->ignore_next_local_deauth = 0; if (event.deauth_info.locally_generated) { @@ -868,6 +916,7 @@ static void mlme_event_join_ibss(struct wpa_driver_nl80211_data *drv, struct nlattr *tb[]) { unsigned int freq; + union wpa_event_data event; if (tb[NL80211_ATTR_MAC] == NULL) { wpa_printf(MSG_DEBUG, "nl80211: No address in IBSS joined " @@ -887,7 +936,10 @@ static void mlme_event_join_ibss(struct wpa_driver_nl80211_data *drv, drv->first_bss->freq = freq; } - wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL); + os_memset(&event, 0, sizeof(event)); + event.assoc_info.freq = freq; + + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event); } @@ -968,7 +1020,7 @@ static void mlme_event_ft_event(struct wpa_driver_nl80211_data *drv, static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted, - struct nlattr *tb[]) + struct nlattr *tb[], int external_scan) { union wpa_event_data event; struct nlattr *nl; @@ -978,7 +1030,7 @@ static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted, int freqs[MAX_REPORT_FREQS]; int num_freqs = 0; - if (drv->scan_for_auth) { + if (!external_scan && drv->scan_for_auth) { drv->scan_for_auth = 0; wpa_printf(MSG_DEBUG, "nl80211: Scan results for missing " "cfg80211 BSS entry"); @@ -989,6 +1041,8 @@ static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted, os_memset(&event, 0, sizeof(event)); info = &event.scan_info; info->aborted = aborted; + info->external_scan = external_scan; + info->nl_scan_event = 1; if (tb[NL80211_ATTR_SCAN_SSIDS]) { nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_SSIDS], rem) { @@ -1004,7 +1058,7 @@ static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted, } } if (tb[NL80211_ATTR_SCAN_FREQUENCIES]) { - char msg[200], *pos, *end; + char msg[300], *pos, *end; int res; pos = msg; @@ -1109,7 +1163,7 @@ static void nl80211_new_peer_candidate(struct wpa_driver_nl80211_data *drv, return; addr = nla_data(tb[NL80211_ATTR_MAC]); - wpa_printf(MSG_DEBUG, "nl80211: New peer candidate" MACSTR, + wpa_printf(MSG_DEBUG, "nl80211: New peer candidate " MACSTR, MAC2STR(addr)); os_memset(&data, 0, sizeof(data)); @@ -1154,6 +1208,7 @@ static void nl80211_new_station_event(struct wpa_driver_nl80211_data *drv, static void nl80211_del_station_event(struct wpa_driver_nl80211_data *drv, + struct i802_bss *bss, struct nlattr **tb) { u8 *addr; @@ -1166,7 +1221,7 @@ static void nl80211_del_station_event(struct wpa_driver_nl80211_data *drv, MAC2STR(addr)); if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) { - drv_event_disassoc(drv->ctx, addr); + drv_event_disassoc(bss->ctx, addr); return; } @@ -1175,7 +1230,7 @@ static void nl80211_del_station_event(struct wpa_driver_nl80211_data *drv, os_memset(&data, 0, sizeof(data)); os_memcpy(data.ibss_peer_lost.peer, addr, ETH_ALEN); - wpa_supplicant_event(drv->ctx, EVENT_IBSS_PEER_LOST, &data); + wpa_supplicant_event(bss->ctx, EVENT_IBSS_PEER_LOST, &data); } @@ -1444,6 +1499,8 @@ static void nl80211_spurious_frame(struct i802_bss *bss, struct nlattr **tb, } +#ifdef CONFIG_DRIVER_NL80211_QCA + static void qca_nl80211_avoid_freq(struct wpa_driver_nl80211_data *drv, const u8 *data, size_t len) { @@ -1593,10 +1650,12 @@ static void qca_nl80211_key_mgmt_auth(struct wpa_driver_nl80211_data *drv, tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID], tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE], tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE], + NULL, tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED], tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR], tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK], - tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK]); + tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK], + tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS]); } @@ -1686,6 +1745,165 @@ static void qca_nl80211_dfs_offload_radar_event( } +static void qca_nl80211_scan_trigger_event(struct wpa_driver_nl80211_data *drv, + u8 *data, size_t len) +{ + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1]; + u64 cookie = 0; + union wpa_event_data event; + struct scan_info *info; + + if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SCAN_MAX, + (struct nlattr *) data, len, NULL) || + !tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]) + return; + + cookie = nla_get_u64(tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]); + if (cookie != drv->vendor_scan_cookie) { + /* External scan trigger event, ignore */ + return; + } + + /* Cookie match, own scan */ + os_memset(&event, 0, sizeof(event)); + info = &event.scan_info; + info->external_scan = 0; + info->nl_scan_event = 0; + + drv->scan_state = SCAN_STARTED; + wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, &event); +} + + +static void send_vendor_scan_event(struct wpa_driver_nl80211_data *drv, + int aborted, struct nlattr *tb[], + int external_scan) +{ + union wpa_event_data event; + struct nlattr *nl; + int rem; + struct scan_info *info; + int freqs[MAX_REPORT_FREQS]; + int num_freqs = 0; + + os_memset(&event, 0, sizeof(event)); + info = &event.scan_info; + info->aborted = aborted; + info->external_scan = external_scan; + + if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS]) { + nla_for_each_nested(nl, + tb[QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS], rem) { + struct wpa_driver_scan_ssid *s = + &info->ssids[info->num_ssids]; + s->ssid = nla_data(nl); + s->ssid_len = nla_len(nl); + wpa_printf(MSG_DEBUG, + "nl80211: Scan probed for SSID '%s'", + wpa_ssid_txt(s->ssid, s->ssid_len)); + info->num_ssids++; + if (info->num_ssids == WPAS_MAX_SCAN_SSIDS) + break; + } + } + + if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES]) { + char msg[300], *pos, *end; + int res; + + pos = msg; + end = pos + sizeof(msg); + *pos = '\0'; + + nla_for_each_nested(nl, + tb[QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES], + rem) { + freqs[num_freqs] = nla_get_u32(nl); + res = os_snprintf(pos, end - pos, " %d", + freqs[num_freqs]); + if (!os_snprintf_error(end - pos, res)) + pos += res; + num_freqs++; + if (num_freqs == MAX_REPORT_FREQS - 1) + break; + } + + info->freqs = freqs; + info->num_freqs = num_freqs; + wpa_printf(MSG_DEBUG, "nl80211: Scan included frequencies:%s", + msg); + } + wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event); +} + + +static void qca_nl80211_scan_done_event(struct wpa_driver_nl80211_data *drv, + u8 *data, size_t len) +{ + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1]; + u64 cookie = 0; + enum scan_status status; + int external_scan; + + if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SCAN_MAX, + (struct nlattr *) data, len, NULL) || + !tb[QCA_WLAN_VENDOR_ATTR_SCAN_STATUS] || + !tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]) + return; + + status = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_SCAN_STATUS]); + if (status >= VENDOR_SCAN_STATUS_MAX) + return; /* invalid status */ + + cookie = nla_get_u64(tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]); + if (cookie != drv->vendor_scan_cookie) { + /* Event from an external scan, get scan results */ + external_scan = 1; + } else { + external_scan = 0; + if (status == VENDOR_SCAN_STATUS_NEW_RESULTS) + drv->scan_state = SCAN_COMPLETED; + else + drv->scan_state = SCAN_ABORTED; + + eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, + drv->ctx); + drv->vendor_scan_cookie = 0; + drv->last_scan_cmd = 0; + } + + send_vendor_scan_event(drv, (status == VENDOR_SCAN_STATUS_ABORTED), tb, + external_scan); +} + + +static void qca_nl80211_p2p_lo_stop_event(struct wpa_driver_nl80211_data *drv, + u8 *data, size_t len) +{ + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_MAX + 1]; + union wpa_event_data event; + + wpa_printf(MSG_DEBUG, + "nl80211: P2P listen offload stop vendor event received"); + + if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_MAX, + (struct nlattr *) data, len, NULL) || + !tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_STOP_REASON]) + return; + + os_memset(&event, 0, sizeof(event)); + event.p2p_lo_stop.reason_code = + nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_STOP_REASON]); + + wpa_printf(MSG_DEBUG, + "nl80211: P2P Listen offload stop reason: %d", + event.p2p_lo_stop.reason_code); + wpa_supplicant_event(drv->ctx, EVENT_P2P_LO_STOP, &event); +} + +#endif /* CONFIG_DRIVER_NL80211_QCA */ + + static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv, u32 subcmd, u8 *data, size_t len) { @@ -1693,6 +1911,7 @@ static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv, case QCA_NL80211_VENDOR_SUBCMD_TEST: wpa_hexdump(MSG_DEBUG, "nl80211: QCA test event", data, len); break; +#ifdef CONFIG_DRIVER_NL80211_QCA case QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY: qca_nl80211_avoid_freq(drv, data, len); break; @@ -1709,6 +1928,16 @@ static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv, case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED: qca_nl80211_dfs_offload_radar_event(drv, subcmd, data, len); break; + case QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN: + qca_nl80211_scan_trigger_event(drv, data, len); + break; + case QCA_NL80211_VENDOR_SUBCMD_SCAN_DONE: + qca_nl80211_scan_done_event(drv, data, len); + break; + case QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP: + qca_nl80211_p2p_lo_stop_event(drv, data, len); + break; +#endif /* CONFIG_DRIVER_NL80211_QCA */ default: wpa_printf(MSG_DEBUG, "nl80211: Ignore unsupported QCA vendor event %u", @@ -1831,6 +2060,7 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, { struct wpa_driver_nl80211_data *drv = bss->drv; union wpa_event_data data; + int external_scan_event = 0; wpa_printf(MSG_DEBUG, "nl80211: Drv Event %d (%s) received for %s", cmd, nl80211_command_to_string(cmd), bss->ifname); @@ -1883,28 +2113,38 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, case NL80211_CMD_NEW_SCAN_RESULTS: wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: New scan results available"); - drv->scan_state = SCAN_COMPLETED; drv->scan_complete_events = 1; - eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, - drv->ctx); - send_scan_event(drv, 0, tb); + if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) { + drv->scan_state = SCAN_COMPLETED; + eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, + drv, drv->ctx); + drv->last_scan_cmd = 0; + } else { + external_scan_event = 1; + } + send_scan_event(drv, 0, tb, external_scan_event); break; case NL80211_CMD_SCHED_SCAN_RESULTS: wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: New sched scan results available"); drv->scan_state = SCHED_SCAN_RESULTS; - send_scan_event(drv, 0, tb); + send_scan_event(drv, 0, tb, 0); break; case NL80211_CMD_SCAN_ABORTED: wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan aborted"); - drv->scan_state = SCAN_ABORTED; - /* - * Need to indicate that scan results are available in order - * not to make wpa_supplicant stop its scanning. - */ - eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, - drv->ctx); - send_scan_event(drv, 1, tb); + if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) { + drv->scan_state = SCAN_ABORTED; + /* + * Need to indicate that scan results are available in + * order not to make wpa_supplicant stop its scanning. + */ + eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, + drv, drv->ctx); + drv->last_scan_cmd = 0; + } else { + external_scan_event = 1; + } + send_scan_event(drv, 1, tb, external_scan_event); break; case NL80211_CMD_AUTHENTICATE: case NL80211_CMD_ASSOCIATE: @@ -1927,7 +2167,8 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_REQ_IE], tb[NL80211_ATTR_RESP_IE], - NULL, NULL, NULL, NULL); + tb[NL80211_ATTR_TIMED_OUT], + NULL, NULL, NULL, NULL, NULL); break; case NL80211_CMD_CH_SWITCH_NOTIFY: mlme_event_ch_switch(drv, @@ -1972,7 +2213,7 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, nl80211_new_station_event(drv, bss, tb); break; case NL80211_CMD_DEL_STATION: - nl80211_del_station_event(drv, tb); + nl80211_del_station_event(drv, bss, tb); break; case NL80211_CMD_SET_REKEY_OFFLOAD: nl80211_rekey_offload_event(drv, tb); diff --git a/contrib/wpa/src/drivers/driver_nl80211_monitor.c b/contrib/wpa/src/drivers/driver_nl80211_monitor.c index 45385da91f6a..9376d1143800 100644 --- a/contrib/wpa/src/drivers/driver_nl80211_monitor.c +++ b/contrib/wpa/src/drivers/driver_nl80211_monitor.c @@ -136,7 +136,7 @@ static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx) break; case IEEE80211_RADIOTAP_TX_FLAGS: injected = 1; - failed = le_to_host16((*(uint16_t *) iter.this_arg)) & + failed = le_to_host16((*(le16 *) iter.this_arg)) & IEEE80211_RADIOTAP_F_TX_FAIL; break; case IEEE80211_RADIOTAP_DATA_RETRIES: diff --git a/contrib/wpa/src/drivers/driver_nl80211_scan.c b/contrib/wpa/src/drivers/driver_nl80211_scan.c index 4b762eafbe8a..c115b6b31b7d 100644 --- a/contrib/wpa/src/drivers/driver_nl80211_scan.c +++ b/contrib/wpa/src/drivers/driver_nl80211_scan.c @@ -1,5 +1,6 @@ /* * Driver interaction with Linux nl80211/cfg80211 - Scanning + * Copyright(c) 2015 Intel Deutschland GmbH * Copyright (c) 2002-2014, Jouni Malinen * Copyright (c) 2007, Johannes Berg * Copyright (c) 2009-2010, Atheros Communications @@ -14,6 +15,8 @@ #include "utils/common.h" #include "utils/eloop.h" #include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "common/qca-vendor.h" #include "driver_nl80211.h" @@ -93,12 +96,20 @@ static int nl80211_get_noise_for_scan_results( void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_driver_nl80211_data *drv = eloop_ctx; + + wpa_printf(MSG_DEBUG, "nl80211: Scan timeout - try to abort it"); + if (!wpa_driver_nl80211_abort_scan(drv->first_bss)) + return; + + wpa_printf(MSG_DEBUG, "nl80211: Failed to abort scan"); + if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED) { wpa_driver_nl80211_set_mode(drv->first_bss, drv->ap_scan_as_station); drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED; } - wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); + + wpa_printf(MSG_DEBUG, "nl80211: Try to get scan results"); wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); } @@ -131,6 +142,8 @@ nl80211_scan_common(struct i802_bss *bss, u8 cmd, goto fail; } nla_nest_end(msg, ssids); + } else { + wpa_printf(MSG_DEBUG, "nl80211: Passive scan requested"); } if (params->extra_ies) { @@ -252,6 +265,13 @@ int wpa_driver_nl80211_scan(struct i802_bss *bss, goto fail; } + if (params->bssid) { + wpa_printf(MSG_DEBUG, "nl80211: Scan for a specific BSSID: " + MACSTR, MAC2STR(params->bssid)); + if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid)) + goto fail; + } + ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret) { @@ -297,6 +317,7 @@ int wpa_driver_nl80211_scan(struct i802_bss *bss, eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout, drv, drv->ctx); + drv->last_scan_cmd = NL80211_CMD_TRIGGER_SCAN; fail: nlmsg_free(msg); @@ -304,16 +325,82 @@ fail: } +static int +nl80211_sched_scan_add_scan_plans(struct wpa_driver_nl80211_data *drv, + struct nl_msg *msg, + struct wpa_driver_scan_params *params) +{ + struct nlattr *plans; + struct sched_scan_plan *scan_plans = params->sched_scan_plans; + unsigned int i; + + plans = nla_nest_start(msg, NL80211_ATTR_SCHED_SCAN_PLANS); + if (!plans) + return -1; + + for (i = 0; i < params->sched_scan_plans_num; i++) { + struct nlattr *plan = nla_nest_start(msg, i + 1); + + if (!plan) + return -1; + + if (!scan_plans[i].interval || + scan_plans[i].interval > + drv->capa.max_sched_scan_plan_interval) { + wpa_printf(MSG_DEBUG, + "nl80211: sched scan plan no. %u: Invalid interval: %u", + i, scan_plans[i].interval); + return -1; + } + + if (nla_put_u32(msg, NL80211_SCHED_SCAN_PLAN_INTERVAL, + scan_plans[i].interval)) + return -1; + + if (scan_plans[i].iterations > + drv->capa.max_sched_scan_plan_iterations) { + wpa_printf(MSG_DEBUG, + "nl80211: sched scan plan no. %u: Invalid number of iterations: %u", + i, scan_plans[i].iterations); + return -1; + } + + if (scan_plans[i].iterations && + nla_put_u32(msg, NL80211_SCHED_SCAN_PLAN_ITERATIONS, + scan_plans[i].iterations)) + return -1; + + nla_nest_end(msg, plan); + + /* + * All the scan plans must specify the number of iterations + * except the last plan, which will run infinitely. So if the + * number of iterations is not specified, this ought to be the + * last scan plan. + */ + if (!scan_plans[i].iterations) + break; + } + + if (i != params->sched_scan_plans_num - 1) { + wpa_printf(MSG_DEBUG, + "nl80211: All sched scan plans but the last must specify number of iterations"); + return -1; + } + + nla_nest_end(msg, plans); + return 0; +} + + /** * wpa_driver_nl80211_sched_scan - Initiate a scheduled scan * @priv: Pointer to private driver data from wpa_driver_nl80211_init() * @params: Scan parameters - * @interval: Interval between scan cycles in milliseconds * Returns: 0 on success, -1 on failure or if not supported */ int wpa_driver_nl80211_sched_scan(void *priv, - struct wpa_driver_scan_params *params, - u32 interval) + struct wpa_driver_scan_params *params) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; @@ -328,11 +415,27 @@ int wpa_driver_nl80211_sched_scan(void *priv, return android_pno_start(bss, params); #endif /* ANDROID */ + if (!params->sched_scan_plans_num || + params->sched_scan_plans_num > drv->capa.max_sched_scan_plans) { + wpa_printf(MSG_ERROR, + "nl80211: Invalid number of sched scan plans: %u", + params->sched_scan_plans_num); + return -1; + } + msg = nl80211_scan_common(bss, NL80211_CMD_START_SCHED_SCAN, params); - if (!msg || - nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, interval)) + if (!msg) goto fail; + if (drv->capa.max_sched_scan_plan_iterations) { + if (nl80211_sched_scan_add_scan_plans(drv, msg, params)) + goto fail; + } else { + if (nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, + params->sched_scan_plans[0].interval * 1000)) + goto fail; + } + if ((drv->num_filter_ssids && (int) drv->num_filter_ssids <= drv->capa.max_match_sets) || params->filter_rssi) { @@ -395,8 +498,7 @@ int wpa_driver_nl80211_sched_scan(void *priv, goto fail; } - wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d) - " - "scan interval %d msec", ret, interval); + wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d)", ret); fail: nlmsg_free(msg); @@ -436,28 +538,6 @@ int wpa_driver_nl80211_stop_sched_scan(void *priv) } -const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie) -{ - const u8 *end, *pos; - - if (ies == NULL) - return NULL; - - pos = ies; - end = ies + ies_len; - - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) - break; - if (pos[0] == ie) - return pos; - pos += 2 + pos[1]; - } - - return NULL; -} - - static int nl80211_scan_filtered(struct wpa_driver_nl80211_data *drv, const u8 *ie, size_t ie_len) { @@ -467,7 +547,7 @@ static int nl80211_scan_filtered(struct wpa_driver_nl80211_data *drv, if (drv->filter_ssids == NULL) return 0; - ssid = nl80211_get_ie(ie, ie_len, WLAN_EID_SSID); + ssid = get_ie(ie, ie_len, WLAN_EID_SSID); if (ssid == NULL) return 1; @@ -628,9 +708,9 @@ int bss_info_handler(struct nl_msg *msg, void *arg) if (os_memcmp(res->res[i]->bssid, r->bssid, ETH_ALEN) != 0) continue; - s1 = nl80211_get_ie((u8 *) (res->res[i] + 1), - res->res[i]->ie_len, WLAN_EID_SSID); - s2 = nl80211_get_ie((u8 *) (r + 1), r->ie_len, WLAN_EID_SSID); + s1 = get_ie((u8 *) (res->res[i] + 1), + res->res[i]->ie_len, WLAN_EID_SSID); + s2 = get_ie((u8 *) (r + 1), r->ie_len, WLAN_EID_SSID); if (s1 == NULL || s2 == NULL || s1[1] != s2[1] || os_memcmp(s1, s2, 2 + s1[1]) != 0) continue; @@ -781,3 +861,263 @@ void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv) wpa_scan_results_free(res); } + + +int wpa_driver_nl80211_abort_scan(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int ret; + struct nl_msg *msg; + + wpa_printf(MSG_DEBUG, "nl80211: Abort scan"); + msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_ABORT_SCAN); + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Abort scan failed: ret=%d (%s)", + ret, strerror(-ret)); + } + + return ret; +} + + +#ifdef CONFIG_DRIVER_NL80211_QCA + +static int scan_cookie_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + u64 *cookie = arg; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (tb[NL80211_ATTR_VENDOR_DATA]) { + struct nlattr *nl_vendor = tb[NL80211_ATTR_VENDOR_DATA]; + struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1]; + + nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_SCAN_MAX, + nla_data(nl_vendor), nla_len(nl_vendor), NULL); + + if (tb_vendor[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]) + *cookie = nla_get_u64( + tb_vendor[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]); + } + + return NL_SKIP; +} + + +/** + * wpa_driver_nl80211_vendor_scan - Request the driver to initiate a vendor scan + * @bss: Pointer to private driver data from wpa_driver_nl80211_init() + * @params: Scan parameters + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss, + struct wpa_driver_scan_params *params) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg = NULL; + struct nlattr *attr; + size_t i; + u32 scan_flags = 0; + int ret = -1; + u64 cookie = 0; + + wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: vendor scan request"); + drv->scan_for_auth = 0; + + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN) ) + goto fail; + + attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA); + if (attr == NULL) + goto fail; + + if (params->num_ssids) { + struct nlattr *ssids; + + ssids = nla_nest_start(msg, QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS); + 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); + if (nla_put(msg, i + 1, params->ssids[i].ssid_len, + params->ssids[i].ssid)) + goto fail; + } + nla_nest_end(msg, ssids); + } + + if (params->extra_ies) { + wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan extra IEs", + params->extra_ies, params->extra_ies_len); + if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SCAN_IE, + params->extra_ies_len, params->extra_ies)) + goto fail; + } + + if (params->freqs) { + struct nlattr *freqs; + + freqs = nla_nest_start(msg, + QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES); + if (freqs == NULL) + goto fail; + for (i = 0; params->freqs[i]; i++) { + wpa_printf(MSG_MSGDUMP, + "nl80211: Scan frequency %u MHz", + params->freqs[i]); + if (nla_put_u32(msg, i + 1, params->freqs[i])) + goto fail; + } + nla_nest_end(msg, freqs); + } + + os_free(drv->filter_ssids); + drv->filter_ssids = params->filter_ssids; + params->filter_ssids = NULL; + drv->num_filter_ssids = params->num_filter_ssids; + + if (params->low_priority && drv->have_low_prio_scan) { + wpa_printf(MSG_DEBUG, + "nl80211: Add NL80211_SCAN_FLAG_LOW_PRIORITY"); + scan_flags |= NL80211_SCAN_FLAG_LOW_PRIORITY; + } + + if (params->mac_addr_rand) { + wpa_printf(MSG_DEBUG, + "nl80211: Add NL80211_SCAN_FLAG_RANDOM_ADDR"); + scan_flags |= NL80211_SCAN_FLAG_RANDOM_ADDR; + + if (params->mac_addr) { + wpa_printf(MSG_DEBUG, "nl80211: MAC address: " MACSTR, + MAC2STR(params->mac_addr)); + if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SCAN_MAC, + ETH_ALEN, params->mac_addr)) + goto fail; + } + + if (params->mac_addr_mask) { + wpa_printf(MSG_DEBUG, "nl80211: MAC address mask: " + MACSTR, MAC2STR(params->mac_addr_mask)); + if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK, + ETH_ALEN, params->mac_addr_mask)) + goto fail; + } + } + + if (scan_flags && + nla_put_u32(msg, NL80211_ATTR_SCAN_FLAGS, scan_flags)) + goto fail; + + if (params->p2p_probe) { + struct nlattr *rates; + + wpa_printf(MSG_DEBUG, "nl80211: P2P probe - mask SuppRates"); + + rates = nla_nest_start(msg, + QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES); + if (rates == NULL) + goto fail; + + /* + * Remove 2.4 GHz rates 1, 2, 5.5, 11 Mbps from supported rates + * by masking out everything else apart from the OFDM rates 6, + * 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS rates. All 5 GHz + * rates are left enabled. + */ + if (nla_put(msg, NL80211_BAND_2GHZ, 8, + "\x0c\x12\x18\x24\x30\x48\x60\x6c")) + goto fail; + nla_nest_end(msg, rates); + + if (nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE)) + goto fail; + } + + nla_nest_end(msg, attr); + + ret = send_and_recv_msgs(drv, msg, scan_cookie_handler, &cookie); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, + "nl80211: Vendor scan trigger failed: ret=%d (%s)", + ret, strerror(-ret)); + goto fail; + } + + drv->vendor_scan_cookie = cookie; + drv->scan_state = SCAN_REQUESTED; + + wpa_printf(MSG_DEBUG, + "nl80211: Vendor scan requested (ret=%d) - scan timeout 30 seconds, scan cookie:0x%llx", + ret, (long long unsigned int) cookie); + eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); + eloop_register_timeout(30, 0, wpa_driver_nl80211_scan_timeout, + drv, drv->ctx); + drv->last_scan_cmd = NL80211_CMD_VENDOR; + +fail: + nlmsg_free(msg); + return ret; +} + + +/** + * nl80211_set_default_scan_ies - Set the scan default IEs to the driver + * @priv: Pointer to private driver data from wpa_driver_nl80211_init() + * @ies: Pointer to IEs buffer + * @ies_len: Length of IEs in bytes + * Returns: 0 on success, -1 on failure + */ +int nl80211_set_default_scan_ies(void *priv, const u8 *ies, size_t ies_len) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg = NULL; + struct nlattr *attr; + int ret = -1; + + if (!drv->set_wifi_conf_vendor_cmd_avail) + return -1; + + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION)) + goto fail; + + attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA); + if (attr == NULL) + goto fail; + + wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan default IEs", ies, ies_len); + if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_DEFAULT_IES, + ies_len, ies)) + goto fail; + + nla_nest_end(msg, attr); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_ERROR, + "nl80211: Set scan default IEs failed: ret=%d (%s)", + ret, strerror(-ret)); + goto fail; + } + +fail: + nlmsg_free(msg); + return ret; +} + +#endif /* CONFIG_DRIVER_NL80211_QCA */ diff --git a/contrib/wpa/src/drivers/driver_privsep.c b/contrib/wpa/src/drivers/driver_privsep.c index 1f1676a20ac5..43d41937d474 100644 --- a/contrib/wpa/src/drivers/driver_privsep.c +++ b/contrib/wpa/src/drivers/driver_privsep.c @@ -161,11 +161,11 @@ wpa_driver_privsep_get_scan_results2(void *priv) return NULL; } - while (results->num < (size_t) num && pos + sizeof(int) < end) { + while (results->num < (size_t) num && end - pos > (int) sizeof(int)) { int len; os_memcpy(&len, pos, sizeof(int)); pos += sizeof(int); - if (len < 0 || len > 10000 || pos + len > end) + if (len < 0 || len > 10000 || len > end - pos) break; r = os_malloc(len); diff --git a/contrib/wpa/src/drivers/driver_wired.c b/contrib/wpa/src/drivers/driver_wired.c index 960bd892a192..755008ee7983 100644 --- a/contrib/wpa/src/drivers/driver_wired.c +++ b/contrib/wpa/src/drivers/driver_wired.c @@ -8,12 +8,17 @@ */ #include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "driver.h" + #include +#undef IFNAMSIZ #include #ifdef __linux__ #include #include -#include #endif /* __linux__ */ #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) #include @@ -23,10 +28,6 @@ #include #endif /* __sun__ */ -#include "common.h" -#include "eloop.h" -#include "driver.h" - #ifdef _MSC_VER #pragma pack(push, 1) #endif /* _MSC_VER */ diff --git a/contrib/wpa/src/drivers/drivers.c b/contrib/wpa/src/drivers/drivers.c index a98af9ac7d71..00773a7113f6 100644 --- a/contrib/wpa/src/drivers/drivers.c +++ b/contrib/wpa/src/drivers/drivers.c @@ -10,42 +10,6 @@ #include "utils/common.h" #include "driver.h" -#ifdef CONFIG_DRIVER_WEXT -extern struct wpa_driver_ops wpa_driver_wext_ops; /* driver_wext.c */ -#endif /* CONFIG_DRIVER_WEXT */ -#ifdef CONFIG_DRIVER_NL80211 -extern struct wpa_driver_ops wpa_driver_nl80211_ops; /* driver_nl80211.c */ -#endif /* CONFIG_DRIVER_NL80211 */ -#ifdef CONFIG_DRIVER_HOSTAP -extern struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */ -#endif /* CONFIG_DRIVER_HOSTAP */ -#ifdef CONFIG_DRIVER_BSD -extern struct wpa_driver_ops wpa_driver_bsd_ops; /* driver_bsd.c */ -#endif /* CONFIG_DRIVER_BSD */ -#ifdef CONFIG_DRIVER_OPENBSD -extern struct wpa_driver_ops wpa_driver_openbsd_ops; /* driver_openbsd.c */ -#endif /* CONFIG_DRIVER_OPENBSD */ -#ifdef CONFIG_DRIVER_NDIS -extern struct wpa_driver_ops wpa_driver_ndis_ops; /* driver_ndis.c */ -#endif /* CONFIG_DRIVER_NDIS */ -#ifdef CONFIG_DRIVER_WIRED -extern struct wpa_driver_ops wpa_driver_wired_ops; /* driver_wired.c */ -#endif /* CONFIG_DRIVER_WIRED */ -#ifdef CONFIG_DRIVER_MACSEC_QCA - /* driver_macsec_qca.c */ -extern struct wpa_driver_ops wpa_driver_macsec_qca_ops; -#endif /* CONFIG_DRIVER_MACSEC_QCA */ -#ifdef CONFIG_DRIVER_ROBOSWITCH -/* driver_roboswitch.c */ -extern struct wpa_driver_ops wpa_driver_roboswitch_ops; -#endif /* CONFIG_DRIVER_ROBOSWITCH */ -#ifdef CONFIG_DRIVER_ATHEROS -extern struct wpa_driver_ops wpa_driver_atheros_ops; /* driver_atheros.c */ -#endif /* CONFIG_DRIVER_ATHEROS */ -#ifdef CONFIG_DRIVER_NONE -extern struct wpa_driver_ops wpa_driver_none_ops; /* driver_none.c */ -#endif /* CONFIG_DRIVER_NONE */ - const struct wpa_driver_ops *const wpa_drivers[] = { diff --git a/contrib/wpa/src/eap_common/eap_eke_common.c b/contrib/wpa/src/eap_common/eap_eke_common.c index 4dfdb3f9c96b..621746821538 100644 --- a/contrib/wpa/src/eap_common/eap_eke_common.c +++ b/contrib/wpa/src/eap_common/eap_eke_common.c @@ -44,9 +44,7 @@ static int eap_eke_dhcomp_len(u8 dhgroup, u8 encr) int dhlen; dhlen = eap_eke_dh_len(dhgroup); - if (dhlen < 0) - return -1; - if (encr != EAP_EKE_ENCR_AES128_CBC) + if (dhlen < 0 || encr != EAP_EKE_ENCR_AES128_CBC) return -1; return AES_BLOCK_SIZE + dhlen; } @@ -166,14 +164,11 @@ int eap_eke_dh_init(u8 group, u8 *ret_priv, u8 *ret_pub) size_t pub_len, i; generator = eap_eke_dh_generator(group); - if (generator < 0 || generator > 255) + dh = eap_eke_dh_group(group); + if (generator < 0 || generator > 255 || !dh) return -1; gen = generator; - dh = eap_eke_dh_group(group); - if (dh == NULL) - return -1; - /* x = random number 2 .. p-1 */ if (random_get_bytes(ret_priv, dh->prime_len)) return -1; @@ -411,11 +406,8 @@ int eap_eke_shared_secret(struct eap_eke_session *sess, const u8 *key, size_t len; const struct dh_group *dh; - if (sess->encr != EAP_EKE_ENCR_AES128_CBC) - return -1; - dh = eap_eke_dh_group(sess->dhgroup); - if (dh == NULL) + if (sess->encr != EAP_EKE_ENCR_AES128_CBC || !dh) return -1; /* Decrypt peer DHComponent */ @@ -635,6 +627,7 @@ int eap_eke_prot(struct eap_eke_session *sess, if (*prot_len < block_size + data_len + pad + icv_len) { wpa_printf(MSG_INFO, "EAP-EKE: Not enough room for Prot() data"); + return -1; } pos = prot; @@ -653,10 +646,8 @@ int eap_eke_prot(struct eap_eke_session *sess, pos += pad; } - if (aes_128_cbc_encrypt(sess->ke, iv, e, data_len + pad) < 0) - return -1; - - if (eap_eke_mac(sess->mac, sess->ki, e, data_len + pad, pos) < 0) + if (aes_128_cbc_encrypt(sess->ke, iv, e, data_len + pad) < 0 || + eap_eke_mac(sess->mac, sess->ki, e, data_len + pad, pos) < 0) return -1; pos += icv_len; @@ -684,9 +675,8 @@ int eap_eke_decrypt_prot(struct eap_eke_session *sess, else return -1; - if (prot_len < 2 * block_size + icv_len) - return -1; - if ((prot_len - icv_len) % block_size) + if (prot_len < 2 * block_size + icv_len || + (prot_len - icv_len) % block_size) return -1; if (eap_eke_mac(sess->mac, sess->ki, prot + block_size, @@ -737,22 +727,14 @@ int eap_eke_session_init(struct eap_eke_session *sess, u8 dhgroup, u8 encr, sess->mac = mac; sess->prf_len = eap_eke_prf_len(prf); - if (sess->prf_len < 0) - return -1; sess->nonce_len = eap_eke_nonce_len(prf); - if (sess->nonce_len < 0) - return -1; sess->auth_len = eap_eke_auth_len(prf); - if (sess->auth_len < 0) - return -1; sess->dhcomp_len = eap_eke_dhcomp_len(sess->dhgroup, sess->encr); - if (sess->dhcomp_len < 0) - return -1; sess->pnonce_len = eap_eke_pnonce_len(sess->mac); - if (sess->pnonce_len < 0) - return -1; sess->pnonce_ps_len = eap_eke_pnonce_ps_len(sess->mac); - if (sess->pnonce_ps_len < 0) + if (sess->prf_len < 0 || sess->nonce_len < 0 || sess->auth_len < 0 || + sess->dhcomp_len < 0 || sess->pnonce_len < 0 || + sess->pnonce_ps_len < 0) return -1; return 0; diff --git a/contrib/wpa/src/eap_common/eap_fast_common.c b/contrib/wpa/src/eap_common/eap_fast_common.c index 151cc7859c5d..9ef671c41c7d 100644 --- a/contrib/wpa/src/eap_common/eap_fast_common.c +++ b/contrib/wpa/src/eap_common/eap_fast_common.c @@ -93,8 +93,7 @@ void eap_fast_derive_master_secret(const u8 *pac_key, const u8 *server_random, } -u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn, - const char *label, size_t len) +u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn, size_t len) { u8 *out; @@ -102,7 +101,7 @@ u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn, if (out == NULL) return NULL; - if (tls_connection_prf(ssl_ctx, conn, label, 1, 1, out, len)) { + if (tls_connection_get_eap_fast_key(ssl_ctx, conn, out, len)) { os_free(out); return NULL; } @@ -111,22 +110,24 @@ u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn, } -void eap_fast_derive_eap_msk(const u8 *simck, u8 *msk) +int eap_fast_derive_eap_msk(const u8 *simck, u8 *msk) { /* * RFC 4851, Section 5.4: EAP Master Session Key Generation * MSK = T-PRF(S-IMCK[j], "Session Key Generating Function", 64) */ - sha1_t_prf(simck, EAP_FAST_SIMCK_LEN, - "Session Key Generating Function", (u8 *) "", 0, - msk, EAP_FAST_KEY_LEN); + if (sha1_t_prf(simck, EAP_FAST_SIMCK_LEN, + "Session Key Generating Function", (u8 *) "", 0, + msk, EAP_FAST_KEY_LEN) < 0) + return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Derived key (MSK)", msk, EAP_FAST_KEY_LEN); + return 0; } -void eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk) +int eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk) { /* * RFC 4851, Section 5.4: EAP Master Session Key Genreration @@ -134,11 +135,13 @@ void eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk) * "Extended Session Key Generating Function", 64) */ - sha1_t_prf(simck, EAP_FAST_SIMCK_LEN, - "Extended Session Key Generating Function", (u8 *) "", 0, - emsk, EAP_EMSK_LEN); + if (sha1_t_prf(simck, EAP_FAST_SIMCK_LEN, + "Extended Session Key Generating Function", (u8 *) "", 0, + emsk, EAP_EMSK_LEN) < 0) + return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Derived key (EMSK)", emsk, EAP_EMSK_LEN); + return 0; } diff --git a/contrib/wpa/src/eap_common/eap_fast_common.h b/contrib/wpa/src/eap_common/eap_fast_common.h index d59a8450ba8c..724204cb5e32 100644 --- a/contrib/wpa/src/eap_common/eap_fast_common.h +++ b/contrib/wpa/src/eap_common/eap_fast_common.h @@ -98,9 +98,9 @@ struct wpabuf * eap_fast_tlv_eap_payload(struct wpabuf *buf); void eap_fast_derive_master_secret(const u8 *pac_key, const u8 *server_random, const u8 *client_random, u8 *master_secret); u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn, - const char *label, size_t len); -void eap_fast_derive_eap_msk(const u8 *simck, u8 *msk); -void eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk); + size_t len); +int eap_fast_derive_eap_msk(const u8 *simck, u8 *msk); +int eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk); int eap_fast_parse_tlv(struct eap_fast_tlv_parse *tlv, int tlv_type, u8 *pos, size_t len); diff --git a/contrib/wpa/src/eap_common/eap_gpsk_common.c b/contrib/wpa/src/eap_common/eap_gpsk_common.c index 8c7ae27b933c..b0818797025f 100644 --- a/contrib/wpa/src/eap_common/eap_gpsk_common.c +++ b/contrib/wpa/src/eap_common/eap_gpsk_common.c @@ -92,7 +92,8 @@ static int eap_gpsk_gkdf_sha256(const u8 *psk /* Y */, n = (len + hashlen - 1) / hashlen; for (i = 1; i <= n; i++) { WPA_PUT_BE16(ibuf, i); - hmac_sha256_vector(psk, 32, 2, addr, vlen, hash); + if (hmac_sha256_vector(psk, 32, 2, addr, vlen, hash)) + return -1; clen = left > hashlen ? hashlen : left; os_memcpy(opos, hash, clen); opos += clen; @@ -534,8 +535,7 @@ int eap_gpsk_compute_mic(const u8 *sk, size_t sk_len, int vendor, break; #ifdef EAP_GPSK_SHA256 case EAP_GPSK_CIPHER_SHA256: - hmac_sha256(sk, sk_len, data, len, mic); - ret = 0; + ret = hmac_sha256(sk, sk_len, data, len, mic); break; #endif /* EAP_GPSK_SHA256 */ default: @@ -545,5 +545,8 @@ int eap_gpsk_compute_mic(const u8 *sk, size_t sk_len, int vendor, break; } + if (ret) + wpa_printf(MSG_DEBUG, "EAP-GPSK: Could not compute MIC"); + return ret; } diff --git a/contrib/wpa/src/eap_common/eap_pax_common.c b/contrib/wpa/src/eap_common/eap_pax_common.c index 0e80ef511c11..a11bce8f9ba3 100644 --- a/contrib/wpa/src/eap_common/eap_pax_common.c +++ b/contrib/wpa/src/eap_common/eap_pax_common.c @@ -57,7 +57,8 @@ int eap_pax_kdf(u8 mac_id, const u8 *key, size_t key_len, left = output_len; for (counter = 1; counter <= (u8) num_blocks; counter++) { size_t clen = left > EAP_PAX_MAC_LEN ? EAP_PAX_MAC_LEN : left; - hmac_sha1_vector(key, key_len, 3, addr, len, mac); + if (hmac_sha1_vector(key, key_len, 3, addr, len, mac) < 0) + return -1; os_memcpy(pos, mac, clen); pos += clen; left -= clen; @@ -106,7 +107,8 @@ int eap_pax_mac(u8 mac_id, const u8 *key, size_t key_len, len[2] = data3_len; count = (data1 ? 1 : 0) + (data2 ? 1 : 0) + (data3 ? 1 : 0); - hmac_sha1_vector(key, key_len, count, addr, len, hash); + if (hmac_sha1_vector(key, key_len, count, addr, len, hash) < 0) + return -1; os_memcpy(mac, hash, EAP_PAX_MAC_LEN); return 0; diff --git a/contrib/wpa/src/eap_common/eap_pwd_common.c b/contrib/wpa/src/eap_common/eap_pwd_common.c index 4d27623f87bf..67f8f7098c4b 100644 --- a/contrib/wpa/src/eap_common/eap_pwd_common.c +++ b/contrib/wpa/src/eap_common/eap_pwd_common.c @@ -115,6 +115,26 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, case 26: nid = NID_secp224r1; break; +#ifdef NID_brainpoolP224r1 + case 27: + nid = NID_brainpoolP224r1; + break; +#endif /* NID_brainpoolP224r1 */ +#ifdef NID_brainpoolP256r1 + case 28: + nid = NID_brainpoolP256r1; + break; +#endif /* NID_brainpoolP256r1 */ +#ifdef NID_brainpoolP384r1 + case 29: + nid = NID_brainpoolP384r1; + break; +#endif /* NID_brainpoolP384r1 */ +#ifdef NID_brainpoolP512r1 + case 30: + nid = NID_brainpoolP512r1; + break; +#endif /* NID_brainpoolP512r1 */ default: wpa_printf(MSG_INFO, "EAP-pwd: unsupported group %d", num); return -1; diff --git a/contrib/wpa/src/eap_common/eap_sake_common.c b/contrib/wpa/src/eap_common/eap_sake_common.c index c22e43ed84b6..8819541b2264 100644 --- a/contrib/wpa/src/eap_common/eap_sake_common.c +++ b/contrib/wpa/src/eap_common/eap_sake_common.c @@ -121,7 +121,7 @@ static int eap_sake_parse_add_attr(struct eap_sake_parse_attr *attr, attr->next_tmpid_len = len; break; case EAP_SAKE_AT_MSK_LIFE: - wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_IV"); + wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_MSK_LIFE"); if (len != 4) { wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid " "AT_MSK_LIFE payload length %d", len); diff --git a/contrib/wpa/src/eap_common/ikev2_common.c b/contrib/wpa/src/eap_common/ikev2_common.c index d60358c733f0..90fb89e243b8 100644 --- a/contrib/wpa/src/eap_common/ikev2_common.c +++ b/contrib/wpa/src/eap_common/ikev2_common.c @@ -62,13 +62,15 @@ int ikev2_integ_hash(int alg, const u8 *key, size_t key_len, const u8 *data, case AUTH_HMAC_SHA1_96: if (key_len != 20) return -1; - hmac_sha1(key, key_len, data, data_len, tmphash); + if (hmac_sha1(key, key_len, data, data_len, tmphash) < 0) + return -1; os_memcpy(hash, tmphash, 12); break; case AUTH_HMAC_MD5_96: if (key_len != 16) return -1; - hmac_md5(key, key_len, data, data_len, tmphash); + if (hmac_md5(key, key_len, data, data_len, tmphash) < 0) + return -1; os_memcpy(hash, tmphash, 12); break; default: @@ -98,16 +100,13 @@ int ikev2_prf_hash(int alg, const u8 *key, size_t key_len, { switch (alg) { case PRF_HMAC_SHA1: - hmac_sha1_vector(key, key_len, num_elem, addr, len, hash); - break; + return hmac_sha1_vector(key, key_len, num_elem, addr, len, + hash); case PRF_HMAC_MD5: - hmac_md5_vector(key, key_len, num_elem, addr, len, hash); - break; + return hmac_md5_vector(key, key_len, num_elem, addr, len, hash); default: return -1; } - - return 0; } diff --git a/contrib/wpa/src/eap_peer/eap.c b/contrib/wpa/src/eap_peer/eap.c index 56c24b550320..9110ca5b9cfd 100644 --- a/contrib/wpa/src/eap_peer/eap.c +++ b/contrib/wpa/src/eap_peer/eap.c @@ -48,6 +48,8 @@ static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req); static const char * eap_sm_method_state_txt(EapMethodState state); static const char * eap_sm_decision_txt(EapDecision decision); #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ +static void eap_sm_request(struct eap_sm *sm, enum wpa_ctrl_req_type field, + const char *msg, size_t msglen); @@ -188,6 +190,14 @@ SM_STATE(EAP, INITIALIZE) */ eapol_set_bool(sm, EAPOL_eapResp, FALSE); eapol_set_bool(sm, EAPOL_eapNoResp, FALSE); + /* + * RFC 4137 does not reset ignore here, but since it is possible for + * some method code paths to end up not setting ignore=FALSE, clear the + * value here to avoid issues if a previous authentication attempt + * failed with ignore=TRUE being left behind in the last + * m.check(eapReqData) operation. + */ + sm->ignore = 0; sm->num_rounds = 0; sm->prev_failure = 0; sm->expected_failure = 0; @@ -312,11 +322,14 @@ SM_STATE(EAP, GET_METHOD) wpa_printf(MSG_DEBUG, "EAP: Initialize selected EAP method: " "vendor %u method %u (%s)", sm->reqVendor, method, sm->m->name); - if (reinit) + if (reinit) { sm->eap_method_priv = sm->m->init_for_reauth( sm, sm->eap_method_priv); - else + } else { + sm->waiting_ext_cert_check = 0; + sm->ext_cert_check = 0; sm->eap_method_priv = sm->m->init(sm); + } if (sm->eap_method_priv == NULL) { struct eap_peer_config *config = eap_get_config(sm); @@ -1373,13 +1386,10 @@ static int eap_sm_imsi_identity(struct eap_sm *sm, return 0; } -#endif /* PCSC_FUNCS */ - static int eap_sm_set_scard_pin(struct eap_sm *sm, struct eap_peer_config *conf) { -#ifdef PCSC_FUNCS if (scard_set_pin(sm->scard_ctx, conf->pin)) { /* * Make sure the same PIN is not tried again in order to avoid @@ -1393,24 +1403,20 @@ static int eap_sm_set_scard_pin(struct eap_sm *sm, return -1; } return 0; -#else /* PCSC_FUNCS */ - return -1; -#endif /* PCSC_FUNCS */ } + static int eap_sm_get_scard_identity(struct eap_sm *sm, struct eap_peer_config *conf) { -#ifdef PCSC_FUNCS if (eap_sm_set_scard_pin(sm, conf)) return -1; return eap_sm_imsi_identity(sm, conf); -#else /* PCSC_FUNCS */ - return -1; -#endif /* PCSC_FUNCS */ } +#endif /* PCSC_FUNCS */ + /** * eap_sm_buildIdentity - Build EAP-Identity/Response for the current network @@ -1453,23 +1459,27 @@ struct wpabuf * eap_sm_buildIdentity(struct eap_sm *sm, int id, int encrypted) identity, identity_len); } - if (identity == NULL) { - wpa_printf(MSG_WARNING, "EAP: buildIdentity: identity " - "configuration was not available"); - if (config->pcsc) { + if (config->pcsc) { +#ifdef PCSC_FUNCS + if (!identity) { if (eap_sm_get_scard_identity(sm, config) < 0) return NULL; identity = config->identity; identity_len = config->identity_len; - wpa_hexdump_ascii(MSG_DEBUG, "permanent identity from " - "IMSI", identity, identity_len); - } else { - eap_sm_request_identity(sm); + wpa_hexdump_ascii(MSG_DEBUG, + "permanent identity from IMSI", + identity, identity_len); + } else if (eap_sm_set_scard_pin(sm, config) < 0) { return NULL; } - } else if (config->pcsc) { - if (eap_sm_set_scard_pin(sm, config) < 0) - return NULL; +#else /* PCSC_FUNCS */ + return NULL; +#endif /* PCSC_FUNCS */ + } else if (!identity) { + wpa_printf(MSG_WARNING, + "EAP: buildIdentity: identity configuration was not available"); + eap_sm_request_identity(sm); + return NULL; } resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, identity_len, @@ -1510,15 +1520,9 @@ static void eap_sm_processNotify(struct eap_sm *sm, const struct wpabuf *req) static struct wpabuf * eap_sm_buildNotify(int id) { - struct wpabuf *resp; - wpa_printf(MSG_DEBUG, "EAP: Generating EAP-Response Notification"); - resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NOTIFICATION, 0, - EAP_CODE_RESPONSE, id); - if (resp == NULL) - return NULL; - - return resp; + return eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NOTIFICATION, 0, + EAP_CODE_RESPONSE, id); } @@ -1850,6 +1854,11 @@ static void eap_peer_sm_tls_event(void *ctx, enum tls_event ev, case TLS_CERT_CHAIN_SUCCESS: eap_notify_status(sm, "remote certificate verification", "success"); + if (sm->ext_cert_check) { + sm->waiting_ext_cert_check = 1; + eap_sm_request(sm, WPA_CTRL_REQ_EXT_CERT_CHECK, + NULL, 0); + } break; case TLS_CERT_CHAIN_FAILURE: wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_TLS_CERT_ERROR @@ -2172,10 +2181,10 @@ int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose) #endif /* CONFIG_CTRL_IFACE */ -#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) static void eap_sm_request(struct eap_sm *sm, enum wpa_ctrl_req_type field, const char *msg, size_t msglen) { +#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) struct eap_peer_config *config; const char *txt = NULL; char *tmp; @@ -2224,16 +2233,17 @@ static void eap_sm_request(struct eap_sm *sm, enum wpa_ctrl_req_type field, case WPA_CTRL_REQ_SIM: txt = msg; break; + case WPA_CTRL_REQ_EXT_CERT_CHECK: + break; default: return; } if (sm->eapol_cb->eap_param_needed) sm->eapol_cb->eap_param_needed(sm->eapol_ctx, field, txt); -} -#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ -#define eap_sm_request(sm, type, msg, msglen) do { } while (0) #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ +} + const char * eap_sm_get_method_name(struct eap_sm *sm) { diff --git a/contrib/wpa/src/eap_peer/eap_aka.c b/contrib/wpa/src/eap_peer/eap_aka.c index dc9e8cc34d4a..0bac62dee523 100644 --- a/contrib/wpa/src/eap_peer/eap_aka.c +++ b/contrib/wpa/src/eap_peer/eap_aka.c @@ -1492,7 +1492,6 @@ static u8 * eap_aka_get_emsk(struct eap_sm *sm, void *priv, size_t *len) int eap_peer_aka_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_AKA, "AKA"); @@ -1511,10 +1510,7 @@ int eap_peer_aka_register(void) eap->get_identity = eap_aka_get_identity; eap->get_emsk = eap_aka_get_emsk; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } @@ -1522,7 +1518,6 @@ int eap_peer_aka_register(void) int eap_peer_aka_prime_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME, @@ -1542,10 +1537,6 @@ int eap_peer_aka_prime_register(void) eap->get_identity = eap_aka_get_identity; eap->get_emsk = eap_aka_get_emsk; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - - return ret; + return eap_peer_method_register(eap); } #endif /* EAP_AKA_PRIME */ diff --git a/contrib/wpa/src/eap_peer/eap_config.h b/contrib/wpa/src/eap_peer/eap_config.h index 2b1a1d5e4b25..f98007263b33 100644 --- a/contrib/wpa/src/eap_peer/eap_config.h +++ b/contrib/wpa/src/eap_peer/eap_config.h @@ -181,13 +181,13 @@ struct eap_peer_config { * subject_match - Constraint for server certificate subject * * This substring is matched against the subject of the authentication - * server certificate. If this string is set, the server sertificate is + * server certificate. If this string is set, the server certificate is * only accepted if it contains this string in the subject. The subject * string is in following format: * * /C=US/ST=CA/L=San Francisco/CN=Test AS/emailAddress=as@n.example.com * - * Note: Since this is a substring match, this cannot be used securily + * Note: Since this is a substring match, this cannot be used securely * 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. */ @@ -198,7 +198,7 @@ struct eap_peer_config { * * Semicolon separated string of entries to be matched against the * alternative subject name of the authentication server certificate. - * If this string is set, the server sertificate is only accepted if it + * If this string is set, the server certificate is only accepted if it * contains one of the entries in an alternative subject name * extension. * @@ -739,6 +739,20 @@ struct eap_peer_config { * erp - Whether EAP Re-authentication Protocol (ERP) is enabled */ int erp; + + /** + * pending_ext_cert_check - External server certificate check status + * + * This field should not be set in configuration step. It is only used + * internally when control interface is used to request external + * validation of server certificate chain. + */ + enum { + NO_CHECK = 0, + PENDING_CHECK, + EXT_CERT_CHECK_GOOD, + EXT_CERT_CHECK_BAD, + } pending_ext_cert_check; }; diff --git a/contrib/wpa/src/eap_peer/eap_eke.c b/contrib/wpa/src/eap_peer/eap_eke.c index dfbda5644f6f..f899f653fdca 100644 --- a/contrib/wpa/src/eap_peer/eap_eke.c +++ b/contrib/wpa/src/eap_peer/eap_eke.c @@ -452,6 +452,7 @@ static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, /* DHComponent_P = Encr(key, y_p) */ rpos = wpabuf_put(resp, data->sess.dhcomp_len); if (eap_eke_dhcomp(&data->sess, key, pub, rpos) < 0) { + wpabuf_free(resp); wpa_printf(MSG_INFO, "EAP-EKE: Failed to build DHComponent_P"); os_memset(key, 0, sizeof(key)); return eap_eke_build_fail(data, ret, id, @@ -770,7 +771,6 @@ static u8 * eap_eke_get_session_id(struct eap_sm *sm, void *priv, size_t *len) int eap_peer_eke_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_EKE, "EKE"); @@ -785,8 +785,5 @@ int eap_peer_eke_register(void) eap->get_emsk = eap_eke_get_emsk; eap->getSessionId = eap_eke_get_session_id; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } diff --git a/contrib/wpa/src/eap_peer/eap_fast.c b/contrib/wpa/src/eap_peer/eap_fast.c index 4cbe3bacb0a6..964ebe74fede 100644 --- a/contrib/wpa/src/eap_peer/eap_fast.c +++ b/contrib/wpa/src/eap_peer/eap_fast.c @@ -1,6 +1,6 @@ /* * EAP peer method: EAP-FAST (RFC 4851) - * Copyright (c) 2004-2008, Jouni Malinen + * Copyright (c) 2004-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -67,6 +67,7 @@ struct eap_fast_data { int simck_idx; struct wpabuf *pending_phase2_req; + struct wpabuf *pending_resp; }; @@ -112,8 +113,8 @@ static int eap_fast_session_ticket_cb(void *ctx, const u8 *ticket, size_t len, } -static int eap_fast_parse_phase1(struct eap_fast_data *data, - const char *phase1) +static void eap_fast_parse_phase1(struct eap_fast_data *data, + const char *phase1) { const char *pos; @@ -139,8 +140,6 @@ static int eap_fast_parse_phase1(struct eap_fast_data *data, wpa_printf(MSG_DEBUG, "EAP-FAST: Using binary format for PAC " "list"); } - - return 0; } @@ -158,10 +157,8 @@ static void * eap_fast_init(struct eap_sm *sm) data->fast_version = EAP_FAST_VERSION; data->max_pac_list_len = 10; - if (config->phase1 && eap_fast_parse_phase1(data, config->phase1) < 0) { - eap_fast_deinit(sm, data); - return NULL; - } + if (config->phase1) + eap_fast_parse_phase1(data, config->phase1); if (eap_peer_select_phase2_methods(config, "auth=", &data->phase2_types, @@ -254,14 +251,16 @@ static void eap_fast_deinit(struct eap_sm *sm, void *priv) os_memset(data->emsk, 0, EAP_EMSK_LEN); os_free(data->session_id); wpabuf_free(data->pending_phase2_req); + wpabuf_free(data->pending_resp); os_free(data); } static int eap_fast_derive_msk(struct eap_fast_data *data) { - eap_fast_derive_eap_msk(data->simck, data->key_data); - eap_fast_derive_eap_emsk(data->simck, data->emsk); + if (eap_fast_derive_eap_msk(data->simck, data->key_data) < 0 || + eap_fast_derive_eap_emsk(data->simck, data->emsk) < 0) + return -1; data->success = 1; return 0; } @@ -276,7 +275,7 @@ static int eap_fast_derive_key_auth(struct eap_sm *sm, * Extra key material after TLS key_block: session_key_seed[40] */ - sks = eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn, "key expansion", + sks = eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn, EAP_FAST_SKS_LEN); if (sks == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive " @@ -304,7 +303,6 @@ static int eap_fast_derive_key_provisioning(struct eap_sm *sm, os_free(data->key_block_p); data->key_block_p = (struct eap_fast_key_block_provisioning *) eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn, - "key expansion", sizeof(*data->key_block_p)); if (data->key_block_p == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive key block"); @@ -711,9 +709,10 @@ static int eap_fast_get_cmk(struct eap_sm *sm, struct eap_fast_data *data, if (eap_fast_get_phase2_key(sm, data, isk, sizeof(isk)) < 0) return -1; wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: ISK[j]", isk, sizeof(isk)); - sha1_t_prf(data->simck, EAP_FAST_SIMCK_LEN, - "Inner Methods Compound Keys", - isk, sizeof(isk), imck, sizeof(imck)); + if (sha1_t_prf(data->simck, EAP_FAST_SIMCK_LEN, + "Inner Methods Compound Keys", + isk, sizeof(isk), imck, sizeof(imck)) < 0) + return -1; data->simck_idx++; os_memcpy(data->simck, imck, EAP_FAST_SIMCK_LEN); wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: S-IMCK[j]", @@ -1096,7 +1095,7 @@ static int eap_fast_parse_decrypted(struct wpabuf *decrypted, /* Parse TLVs from the decrypted Phase 2 data */ pos = wpabuf_mhead(decrypted); end = pos + wpabuf_len(decrypted); - while (pos + 4 < end) { + while (end - pos > 4) { mandatory = pos[0] & 0x80; tlv_type = WPA_GET_BE16(pos) & 0x3fff; pos += 2; @@ -1443,7 +1442,7 @@ static int eap_fast_clear_pac_opaque_ext(struct eap_sm *sm, static int eap_fast_set_provisioning_ciphers(struct eap_sm *sm, struct eap_fast_data *data) { - u8 ciphers[5]; + u8 ciphers[7]; int count = 0; if (data->provisioning_allowed & EAP_FAST_PROV_UNAUTH) { @@ -1455,7 +1454,9 @@ static int eap_fast_set_provisioning_ciphers(struct eap_sm *sm, if (data->provisioning_allowed & EAP_FAST_PROV_AUTH) { wpa_printf(MSG_DEBUG, "EAP-FAST: Enabling authenticated " "provisioning TLS cipher suites"); + ciphers[count++] = TLS_CIPHER_RSA_DHE_AES256_SHA; ciphers[count++] = TLS_CIPHER_RSA_DHE_AES128_SHA; + ciphers[count++] = TLS_CIPHER_AES256_SHA; ciphers[count++] = TLS_CIPHER_AES128_SHA; ciphers[count++] = TLS_CIPHER_RC4_SHA; } @@ -1567,6 +1568,34 @@ static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv, res = 1; } } else { + if (sm->waiting_ext_cert_check && data->pending_resp) { + struct eap_peer_config *config = eap_get_config(sm); + + if (config->pending_ext_cert_check == + EXT_CERT_CHECK_GOOD) { + wpa_printf(MSG_DEBUG, + "EAP-FAST: External certificate check succeeded - continue handshake"); + resp = data->pending_resp; + data->pending_resp = NULL; + sm->waiting_ext_cert_check = 0; + return resp; + } + + if (config->pending_ext_cert_check == + EXT_CERT_CHECK_BAD) { + wpa_printf(MSG_DEBUG, + "EAP-FAST: External certificate check failed - force authentication failure"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + sm->waiting_ext_cert_check = 0; + return NULL; + } + + wpa_printf(MSG_DEBUG, + "EAP-FAST: Continuing to wait external server certificate validation"); + return NULL; + } + /* Continue processing TLS handshake (phase 1). */ res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_FAST, @@ -1580,6 +1609,14 @@ static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv, return resp; } + if (sm->waiting_ext_cert_check) { + wpa_printf(MSG_DEBUG, + "EAP-FAST: Waiting external server certificate validation"); + wpabuf_free(data->pending_resp); + data->pending_resp = resp; + return NULL; + } + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { char cipher[80]; wpa_printf(MSG_DEBUG, @@ -1644,6 +1681,8 @@ static void eap_fast_deinit_for_reauth(struct eap_sm *sm, void *priv) data->key_block_p = NULL; wpabuf_free(data->pending_phase2_req); data->pending_phase2_req = NULL; + wpabuf_free(data->pending_resp); + data->pending_resp = NULL; } @@ -1721,7 +1760,7 @@ static u8 * eap_fast_get_session_id(struct eap_sm *sm, void *priv, size_t *len) struct eap_fast_data *data = priv; u8 *id; - if (!data->success) + if (!data->success || !data->session_id) return NULL; id = os_malloc(data->id_len); @@ -1757,7 +1796,6 @@ static u8 * eap_fast_get_emsk(struct eap_sm *sm, void *priv, size_t *len) int eap_peer_fast_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_FAST, "FAST"); @@ -1778,8 +1816,5 @@ int eap_peer_fast_register(void) #endif eap->get_emsk = eap_fast_get_emsk; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } diff --git a/contrib/wpa/src/eap_peer/eap_fast_pac.c b/contrib/wpa/src/eap_peer/eap_fast_pac.c index 89e604ecf84b..c81586035513 100644 --- a/contrib/wpa/src/eap_peer/eap_fast_pac.c +++ b/contrib/wpa/src/eap_peer/eap_fast_pac.c @@ -455,7 +455,8 @@ int eap_fast_load_pac(struct eap_sm *sm, struct eap_fast_pac **pac_root, } if (pac) { - err = "PAC block not terminated with END"; + if (!err) + err = "PAC block not terminated with END"; eap_fast_free_pac(pac); } @@ -709,7 +710,7 @@ static void eap_fast_pac_get_a_id(struct eap_fast_pac *pac) pos = pac->pac_info; end = pos + pac->pac_info_len; - while (pos + 4 < end) { + while (end - pos > 4) { type = WPA_GET_BE16(pos); pos += 2; len = WPA_GET_BE16(pos); @@ -801,8 +802,10 @@ int eap_fast_load_pac_bin(struct eap_sm *sm, struct eap_fast_pac **pac_root, while (pos < end) { u16 val; - if (end - pos < 2 + EAP_FAST_PAC_KEY_LEN + 2 + 2) + if (end - pos < 2 + EAP_FAST_PAC_KEY_LEN + 2 + 2) { + pac = NULL; goto parse_fail; + } pac = os_zalloc(sizeof(*pac)); if (pac == NULL) diff --git a/contrib/wpa/src/eap_peer/eap_gpsk.c b/contrib/wpa/src/eap_peer/eap_gpsk.c index 902b4ba26d6e..177cbccf5850 100644 --- a/contrib/wpa/src/eap_peer/eap_gpsk.c +++ b/contrib/wpa/src/eap_peer/eap_gpsk.c @@ -771,7 +771,6 @@ static u8 * eap_gpsk_get_session_id(struct eap_sm *sm, void *priv, size_t *len) int eap_peer_gpsk_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_GPSK, "GPSK"); @@ -786,8 +785,5 @@ int eap_peer_gpsk_register(void) eap->get_emsk = eap_gpsk_get_emsk; eap->getSessionId = eap_gpsk_get_session_id; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } diff --git a/contrib/wpa/src/eap_peer/eap_gtc.c b/contrib/wpa/src/eap_peer/eap_gtc.c index 9f3cfbdacadc..a519a780a90b 100644 --- a/contrib/wpa/src/eap_peer/eap_gtc.c +++ b/contrib/wpa/src/eap_peer/eap_gtc.c @@ -127,7 +127,6 @@ static struct wpabuf * eap_gtc_process(struct eap_sm *sm, void *priv, int eap_peer_gtc_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_GTC, "GTC"); @@ -138,8 +137,5 @@ int eap_peer_gtc_register(void) eap->deinit = eap_gtc_deinit; eap->process = eap_gtc_process; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } diff --git a/contrib/wpa/src/eap_peer/eap_i.h b/contrib/wpa/src/eap_peer/eap_i.h index 99b44dae4e34..6ab24834d654 100644 --- a/contrib/wpa/src/eap_peer/eap_i.h +++ b/contrib/wpa/src/eap_peer/eap_i.h @@ -366,6 +366,8 @@ struct eap_sm { int external_sim; unsigned int expected_failure:1; + unsigned int ext_cert_check:1; + unsigned int waiting_ext_cert_check:1; struct dl_list erp_keys; /* struct eap_erp_key */ }; diff --git a/contrib/wpa/src/eap_peer/eap_ikev2.c b/contrib/wpa/src/eap_peer/eap_ikev2.c index b5ef71bac3ba..390f0ec8cf4d 100644 --- a/contrib/wpa/src/eap_peer/eap_ikev2.c +++ b/contrib/wpa/src/eap_peer/eap_ikev2.c @@ -513,7 +513,6 @@ static u8 * eap_ikev2_get_session_id(struct eap_sm *sm, void *priv, size_t *len) int eap_peer_ikev2_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_IKEV2, @@ -529,8 +528,5 @@ int eap_peer_ikev2_register(void) eap->get_emsk = eap_ikev2_get_emsk; eap->getSessionId = eap_ikev2_get_session_id; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } diff --git a/contrib/wpa/src/eap_peer/eap_leap.c b/contrib/wpa/src/eap_peer/eap_leap.c index e0f8bcf6b0df..ff6fa4afd2f7 100644 --- a/contrib/wpa/src/eap_peer/eap_leap.c +++ b/contrib/wpa/src/eap_peer/eap_leap.c @@ -393,7 +393,6 @@ static u8 * eap_leap_getKey(struct eap_sm *sm, void *priv, size_t *len) int eap_peer_leap_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_LEAP, "LEAP"); @@ -406,8 +405,5 @@ int eap_peer_leap_register(void) eap->isKeyAvailable = eap_leap_isKeyAvailable; eap->getKey = eap_leap_getKey; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } diff --git a/contrib/wpa/src/eap_peer/eap_md5.c b/contrib/wpa/src/eap_peer/eap_md5.c index d06befaeb1df..efae8deba85d 100644 --- a/contrib/wpa/src/eap_peer/eap_md5.c +++ b/contrib/wpa/src/eap_peer/eap_md5.c @@ -102,7 +102,6 @@ static struct wpabuf * eap_md5_process(struct eap_sm *sm, void *priv, int eap_peer_md5_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_MD5, "MD5"); @@ -113,8 +112,5 @@ int eap_peer_md5_register(void) eap->deinit = eap_md5_deinit; eap->process = eap_md5_process; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } diff --git a/contrib/wpa/src/eap_peer/eap_methods.c b/contrib/wpa/src/eap_peer/eap_methods.c index 1bdd81e1ad52..9747954952a7 100644 --- a/contrib/wpa/src/eap_peer/eap_methods.c +++ b/contrib/wpa/src/eap_peer/eap_methods.c @@ -18,6 +18,8 @@ static struct eap_method *eap_methods = NULL; +static void eap_peer_method_free(struct eap_method *method); + /** * eap_peer_get_eap_method - Get EAP method based on type number @@ -295,7 +297,7 @@ struct eap_method * eap_peer_method_alloc(int version, int vendor, * eap_peer_method_free - Free EAP peer method structure * @method: Method structure allocated with eap_peer_method_alloc() */ -void eap_peer_method_free(struct eap_method *method) +static void eap_peer_method_free(struct eap_method *method) { os_free(method); } @@ -303,26 +305,31 @@ void eap_peer_method_free(struct eap_method *method) /** * eap_peer_method_register - Register an EAP peer method - * @method: EAP method to register + * @method: EAP method to register from eap_peer_method_alloc() * Returns: 0 on success, -1 on invalid method, or -2 if a matching EAP method * has already been registered * * Each EAP peer method needs to call this function to register itself as a - * supported EAP method. + * supported EAP method. The caller must not free the allocated method data + * regardless of the return value. */ int eap_peer_method_register(struct eap_method *method) { struct eap_method *m, *last = NULL; if (method == NULL || method->name == NULL || - method->version != EAP_PEER_METHOD_INTERFACE_VERSION) + method->version != EAP_PEER_METHOD_INTERFACE_VERSION) { + eap_peer_method_free(method); return -1; + } for (m = eap_methods; m; m = m->next) { if ((m->vendor == method->vendor && m->method == method->method) || - os_strcmp(m->name, method->name) == 0) + os_strcmp(m->name, method->name) == 0) { + eap_peer_method_free(method); return -2; + } last = m; } diff --git a/contrib/wpa/src/eap_peer/eap_methods.h b/contrib/wpa/src/eap_peer/eap_methods.h index e35c919abce9..b96b211de258 100644 --- a/contrib/wpa/src/eap_peer/eap_methods.h +++ b/contrib/wpa/src/eap_peer/eap_methods.h @@ -16,7 +16,6 @@ const struct eap_method * eap_peer_get_methods(size_t *count); struct eap_method * eap_peer_method_alloc(int version, int vendor, EapType method, const char *name); -void eap_peer_method_free(struct eap_method *method); int eap_peer_method_register(struct eap_method *method); diff --git a/contrib/wpa/src/eap_peer/eap_mschapv2.c b/contrib/wpa/src/eap_peer/eap_mschapv2.c index 6acf1e8ad390..ce2227d388ef 100644 --- a/contrib/wpa/src/eap_peer/eap_mschapv2.c +++ b/contrib/wpa/src/eap_peer/eap_mschapv2.c @@ -880,7 +880,6 @@ static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len) int eap_peer_mschapv2_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, @@ -894,8 +893,5 @@ int eap_peer_mschapv2_register(void) eap->isKeyAvailable = eap_mschapv2_isKeyAvailable; eap->getKey = eap_mschapv2_getKey; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } diff --git a/contrib/wpa/src/eap_peer/eap_otp.c b/contrib/wpa/src/eap_peer/eap_otp.c index 9ac744a7ddad..0ab4c7907ab5 100644 --- a/contrib/wpa/src/eap_peer/eap_otp.c +++ b/contrib/wpa/src/eap_peer/eap_otp.c @@ -83,7 +83,6 @@ static struct wpabuf * eap_otp_process(struct eap_sm *sm, void *priv, int eap_peer_otp_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_OTP, "OTP"); @@ -94,8 +93,5 @@ int eap_peer_otp_register(void) eap->deinit = eap_otp_deinit; eap->process = eap_otp_process; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } diff --git a/contrib/wpa/src/eap_peer/eap_pax.c b/contrib/wpa/src/eap_peer/eap_pax.c index c920bcd3182f..a7012d2870cf 100644 --- a/contrib/wpa/src/eap_peer/eap_pax.c +++ b/contrib/wpa/src/eap_peer/eap_pax.c @@ -276,9 +276,16 @@ static struct wpabuf * eap_pax_process_std_3(struct eap_pax_data *data, left -= 2; wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)", pos, 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, mac); + 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, mac) < 0) { + wpa_printf(MSG_INFO, + "EAP-PAX: Could not derive MAC_CK(B, CID)"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return NULL; + } + if (os_memcmp_const(pos, mac, EAP_PAX_MAC_LEN) != 0) { wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(B, CID) " "received"); @@ -306,9 +313,12 @@ static struct wpabuf * eap_pax_process_std_3(struct eap_pax_data *data, /* Optional ADE could be added here, if needed */ rpos = wpabuf_put(resp, EAP_PAX_ICV_LEN); - eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, - wpabuf_head(resp), wpabuf_len(resp) - EAP_PAX_ICV_LEN, - NULL, 0, NULL, 0, rpos); + if (eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, + wpabuf_head(resp), wpabuf_len(resp) - EAP_PAX_ICV_LEN, + NULL, 0, NULL, 0, rpos) < 0) { + wpabuf_free(resp); + return NULL; + } wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", rpos, EAP_PAX_ICV_LEN); data->state = PAX_DONE; @@ -472,9 +482,13 @@ static u8 * eap_pax_getKey(struct eap_sm *sm, void *priv, size_t *len) return NULL; *len = EAP_MSK_LEN; - eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN, - "Master Session Key", data->rand.e, 2 * EAP_PAX_RAND_LEN, - EAP_MSK_LEN, key); + if (eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN, + "Master Session Key", + data->rand.e, 2 * EAP_PAX_RAND_LEN, + EAP_MSK_LEN, key) < 0) { + os_free(key); + return NULL; + } return key; } @@ -493,10 +507,13 @@ static u8 * eap_pax_get_emsk(struct eap_sm *sm, void *priv, size_t *len) return NULL; *len = EAP_EMSK_LEN; - eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN, - "Extended Master Session Key", - data->rand.e, 2 * EAP_PAX_RAND_LEN, - EAP_EMSK_LEN, key); + if (eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN, + "Extended Master Session Key", + data->rand.e, 2 * EAP_PAX_RAND_LEN, + EAP_EMSK_LEN, key) < 0) { + os_free(key); + return NULL; + } return key; } @@ -525,7 +542,6 @@ static u8 * eap_pax_get_session_id(struct eap_sm *sm, void *priv, size_t *len) int eap_peer_pax_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_PAX, "PAX"); @@ -540,8 +556,5 @@ int eap_peer_pax_register(void) eap->get_emsk = eap_pax_get_emsk; eap->getSessionId = eap_pax_get_session_id; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } diff --git a/contrib/wpa/src/eap_peer/eap_peap.c b/contrib/wpa/src/eap_peer/eap_peap.c index 98a48a6cf5d3..45ba38168d4f 100644 --- a/contrib/wpa/src/eap_peer/eap_peap.c +++ b/contrib/wpa/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-2008, Jouni Malinen + * Copyright (c) 2004-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -59,6 +59,7 @@ struct eap_peap_data { size_t id_len; struct wpabuf *pending_phase2_req; + struct wpabuf *pending_resp; enum { NO_BINDING, OPTIONAL_BINDING, REQUIRE_BINDING } crypto_binding; int crypto_binding_used; u8 binding_nonce[32]; @@ -69,8 +70,8 @@ struct eap_peap_data { }; -static int eap_peap_parse_phase1(struct eap_peap_data *data, - const char *phase1) +static void eap_peap_parse_phase1(struct eap_peap_data *data, + const char *phase1) { const char *pos; @@ -125,8 +126,6 @@ static int eap_peap_parse_phase1(struct eap_peap_data *data, wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 2 enabled"); } #endif /* EAP_TNC */ - - return 0; } @@ -144,11 +143,8 @@ static void * eap_peap_init(struct eap_sm *sm) data->peap_outer_success = 2; data->crypto_binding = OPTIONAL_BINDING; - if (config && config->phase1 && - eap_peap_parse_phase1(data, config->phase1) < 0) { - eap_peap_deinit(sm, data); - return NULL; - } + if (config && config->phase1) + eap_peap_parse_phase1(data, config->phase1); if (eap_peer_select_phase2_methods(config, "auth=", &data->phase2_types, @@ -191,6 +187,7 @@ static void eap_peap_deinit(struct eap_sm *sm, void *priv) eap_peap_free_key(data); os_free(data->session_id); wpabuf_free(data->pending_phase2_req); + wpabuf_free(data->pending_resp); os_free(data); } @@ -256,6 +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; /* * Tunnel key (TK) is the first 60 octets of the key generated by @@ -266,8 +264,12 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60); - if (data->reauth && - tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) { + resumed = tls_connection_resumed(sm->ssl_ctx, data->ssl.conn); + wpa_printf(MSG_DEBUG, + "EAP-PEAP: CMK derivation - reauth=%d resumed=%d phase2_eap_started=%d phase2_success=%d", + data->reauth, resumed, data->phase2_eap_started, + data->phase2_success); + if (data->reauth && !data->phase2_eap_started && resumed) { /* Fast-connect: IPMK|CMK = TK */ os_memcpy(data->ipmk, tk, 40); wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK from TK", @@ -337,7 +339,8 @@ static int eap_tlv_add_cryptobinding(struct eap_sm *sm, addr[0], len[0]); wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 2", addr[1], len[1]); - hmac_sha1_vector(data->cmk, 20, 2, addr, len, mac); + if (hmac_sha1_vector(data->cmk, 20, 2, addr, len, mac) < 0) + return -1; wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC", mac, SHA1_MAC_LEN); data->crypto_binding_used = 1; @@ -648,6 +651,7 @@ static int eap_peap_phase2_request(struct eap_sm *sm, if (*resp == NULL) { ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; + wpabuf_free(buf); return -1; } wpabuf_put_buf(*resp, buf); @@ -1006,6 +1010,34 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, !data->resuming) { res = eap_peap_decrypt(sm, data, ret, req, &msg, &resp); } else { + if (sm->waiting_ext_cert_check && data->pending_resp) { + struct eap_peer_config *config = eap_get_config(sm); + + if (config->pending_ext_cert_check == + EXT_CERT_CHECK_GOOD) { + wpa_printf(MSG_DEBUG, + "EAP-PEAP: External certificate check succeeded - continue handshake"); + resp = data->pending_resp; + data->pending_resp = NULL; + sm->waiting_ext_cert_check = 0; + return resp; + } + + if (config->pending_ext_cert_check == + EXT_CERT_CHECK_BAD) { + wpa_printf(MSG_DEBUG, + "EAP-PEAP: External certificate check failed - force authentication failure"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + sm->waiting_ext_cert_check = 0; + return NULL; + } + + wpa_printf(MSG_DEBUG, + "EAP-PEAP: Continuing to wait external server certificate validation"); + return NULL; + } + res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_PEAP, data->peap_version, id, &msg, @@ -1018,6 +1050,16 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, ret->decision = DECISION_FAIL; return resp; } + + + if (sm->waiting_ext_cert_check) { + wpa_printf(MSG_DEBUG, + "EAP-PEAP: Waiting external server certificate validation"); + wpabuf_free(data->pending_resp); + data->pending_resp = resp; + return NULL; + } + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { char *label; wpa_printf(MSG_DEBUG, @@ -1123,6 +1165,8 @@ static void eap_peap_deinit_for_reauth(struct eap_sm *sm, void *priv) struct eap_peap_data *data = priv; wpabuf_free(data->pending_phase2_req); data->pending_phase2_req = NULL; + wpabuf_free(data->pending_resp); + data->pending_resp = NULL; data->crypto_binding_used = 0; } @@ -1237,7 +1281,6 @@ static u8 * eap_peap_get_session_id(struct eap_sm *sm, void *priv, size_t *len) int eap_peer_peap_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_PEAP, "PEAP"); @@ -1255,8 +1298,5 @@ int eap_peer_peap_register(void) eap->init_for_reauth = eap_peap_init_for_reauth; eap->getSessionId = eap_peap_get_session_id; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } diff --git a/contrib/wpa/src/eap_peer/eap_psk.c b/contrib/wpa/src/eap_peer/eap_psk.c index f01266354e90..ac18c158ad8b 100644 --- a/contrib/wpa/src/eap_peer/eap_psk.c +++ b/contrib/wpa/src/eap_peer/eap_psk.c @@ -480,7 +480,6 @@ static u8 * eap_psk_get_emsk(struct eap_sm *sm, void *priv, size_t *len) int eap_peer_psk_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_PSK, "PSK"); @@ -495,8 +494,5 @@ int eap_peer_psk_register(void) eap->getSessionId = eap_psk_get_session_id; eap->get_emsk = eap_psk_get_emsk; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } diff --git a/contrib/wpa/src/eap_peer/eap_pwd.c b/contrib/wpa/src/eap_peer/eap_pwd.c index 1f785443ee5a..d2bc981cd06b 100644 --- a/contrib/wpa/src/eap_peer/eap_pwd.c +++ b/contrib/wpa/src/eap_peer/eap_pwd.c @@ -418,7 +418,6 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, wpa_printf(MSG_INFO, "EAP-PWD (peer): element inversion fail"); goto fin; } - BN_clear_free(mask); if (((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) { @@ -555,6 +554,7 @@ fin: os_free(element); BN_clear_free(x); BN_clear_free(y); + BN_clear_free(mask); BN_clear_free(cofactor); EC_POINT_clear_free(K); EC_POINT_clear_free(point); @@ -774,7 +774,8 @@ eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data, wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN); fin: - bin_clear_free(cruft, BN_num_bytes(data->grp->prime)); + if (data->grp) + bin_clear_free(cruft, BN_num_bytes(data->grp->prime)); BN_clear_free(x); BN_clear_free(y); if (data->outbuf == NULL) { @@ -903,7 +904,7 @@ 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)) { + if (EAP_PWD_GET_MORE_BIT(lm_exch) || data->in_frag_pos) { data->in_frag_pos += len; if (data->in_frag_pos > wpabuf_size(data->inbuf)) { wpa_printf(MSG_INFO, "EAP-pwd: Buffer overflow attack " @@ -916,7 +917,8 @@ eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, return NULL; } wpabuf_put_data(data->inbuf, pos, len); - + } + if (EAP_PWD_GET_MORE_BIT(lm_exch)) { resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, EAP_PWD_HDR_SIZE, EAP_CODE_RESPONSE, eap_get_id(reqData)); @@ -930,10 +932,8 @@ 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) { - wpabuf_put_data(data->inbuf, pos, len); wpa_printf(MSG_DEBUG, "EAP-pwd: Last fragment, %d bytes", (int) len); - data->in_frag_pos += len; pos = wpabuf_head_u8(data->inbuf); len = data->in_frag_pos; } @@ -1054,7 +1054,6 @@ static u8 * eap_pwd_get_emsk(struct eap_sm *sm, void *priv, size_t *len) int eap_peer_pwd_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_PWD, "PWD"); @@ -1069,8 +1068,5 @@ int eap_peer_pwd_register(void) eap->getSessionId = eap_pwd_get_session_id; eap->get_emsk = eap_pwd_get_emsk; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } diff --git a/contrib/wpa/src/eap_peer/eap_sake.c b/contrib/wpa/src/eap_peer/eap_sake.c index c4f9843febb3..330febbefd78 100644 --- a/contrib/wpa/src/eap_peer/eap_sake.c +++ b/contrib/wpa/src/eap_peer/eap_sake.c @@ -309,11 +309,20 @@ static struct wpabuf * eap_sake_process_confirm(struct eap_sm *sm, return NULL; } - eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, - data->serverid, data->serverid_len, - data->peerid, data->peerid_len, 0, - wpabuf_head(reqData), wpabuf_len(reqData), - attr.mic_s, mic_s); + if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, + data->serverid, data->serverid_len, + data->peerid, data->peerid_len, 0, + wpabuf_head(reqData), wpabuf_len(reqData), + attr.mic_s, mic_s)) { + wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC"); + eap_sake_state(data, FAILURE); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + ret->allowNotifications = FALSE; + wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Auth-Reject"); + return eap_sake_build_msg(data, id, 0, + EAP_SAKE_SUBTYPE_AUTH_REJECT); + } if (os_memcmp_const(attr.mic_s, mic_s, EAP_SAKE_MIC_LEN) != 0) { wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_S"); eap_sake_state(data, FAILURE); @@ -494,7 +503,6 @@ static u8 * eap_sake_get_emsk(struct eap_sm *sm, void *priv, size_t *len) int eap_peer_sake_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_SAKE, "SAKE"); @@ -509,8 +517,5 @@ int eap_peer_sake_register(void) eap->getSessionId = eap_sake_get_session_id; eap->get_emsk = eap_sake_get_emsk; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } diff --git a/contrib/wpa/src/eap_peer/eap_sim.c b/contrib/wpa/src/eap_peer/eap_sim.c index 99a2816ce61e..b97c95db196f 100644 --- a/contrib/wpa/src/eap_peer/eap_sim.c +++ b/contrib/wpa/src/eap_peer/eap_sim.c @@ -249,6 +249,7 @@ static int eap_sim_gsm_auth(struct eap_sm *sm, struct eap_sim_data *data) return eap_sim_ext_sim_req(sm, data); } +#ifdef PCSC_FUNCS if (conf->pcsc) { if (scard_gsm_auth(sm->scard_ctx, data->rand[0], data->sres[0], data->kc[0]) || @@ -263,6 +264,7 @@ static int eap_sim_gsm_auth(struct eap_sm *sm, struct eap_sim_data *data) } return 0; } +#endif /* PCSC_FUNCS */ #ifdef CONFIG_SIM_SIMULATOR if (conf->password) { @@ -1135,7 +1137,7 @@ static void * eap_sim_init_for_reauth(struct eap_sm *sm, void *priv) if (random_get_bytes(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) { wpa_printf(MSG_WARNING, "EAP-SIM: Failed to get random data " "for NONCE_MT"); - os_free(data); + eap_sim_deinit(sm, data); return NULL; } data->num_id_req = 0; @@ -1235,7 +1237,6 @@ static u8 * eap_sim_get_emsk(struct eap_sm *sm, void *priv, size_t *len) int eap_peer_sim_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_SIM, "SIM"); @@ -1254,8 +1255,5 @@ int eap_peer_sim_register(void) eap->get_identity = eap_sim_get_identity; eap->get_emsk = eap_sim_get_emsk; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } diff --git a/contrib/wpa/src/eap_peer/eap_tls.c b/contrib/wpa/src/eap_peer/eap_tls.c index 66a027a626e0..ca2354f8a785 100644 --- a/contrib/wpa/src/eap_peer/eap_tls.c +++ b/contrib/wpa/src/eap_peer/eap_tls.c @@ -1,6 +1,6 @@ /* * EAP peer method: EAP-TLS (RFC 2716) - * Copyright (c) 2004-2008, 2012, Jouni Malinen + * Copyright (c) 2004-2008, 2012-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -25,6 +25,7 @@ struct eap_tls_data { size_t id_len; void *ssl_ctx; u8 eap_type; + struct wpabuf *pending_resp; }; @@ -142,6 +143,7 @@ static void eap_tls_deinit(struct eap_sm *sm, void *priv) eap_peer_tls_ssl_deinit(sm, &data->ssl); eap_tls_free_key(data); os_free(data->session_id); + wpabuf_free(data->pending_resp); os_free(data); } @@ -216,6 +218,32 @@ static struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv, struct eap_tls_data *data = priv; struct wpabuf msg; + if (sm->waiting_ext_cert_check && data->pending_resp) { + struct eap_peer_config *config = eap_get_config(sm); + + if (config->pending_ext_cert_check == EXT_CERT_CHECK_GOOD) { + wpa_printf(MSG_DEBUG, + "EAP-TLS: External certificate check succeeded - continue handshake"); + resp = data->pending_resp; + data->pending_resp = NULL; + sm->waiting_ext_cert_check = 0; + return resp; + } + + if (config->pending_ext_cert_check == EXT_CERT_CHECK_BAD) { + wpa_printf(MSG_DEBUG, + "EAP-TLS: External certificate check failed - force authentication failure"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + sm->waiting_ext_cert_check = 0; + return NULL; + } + + wpa_printf(MSG_DEBUG, + "EAP-TLS: Continuing to wait external server certificate validation"); + return NULL; + } + pos = eap_peer_tls_process_init(sm, &data->ssl, data->eap_type, ret, reqData, &left, &flags); if (pos == NULL) @@ -237,6 +265,14 @@ static struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv, return eap_tls_failure(sm, data, ret, res, resp, id); } + if (sm->waiting_ext_cert_check) { + wpa_printf(MSG_DEBUG, + "EAP-TLS: Waiting external server certificate validation"); + wpabuf_free(data->pending_resp); + data->pending_resp = resp; + return NULL; + } + if (tls_connection_established(data->ssl_ctx, data->ssl.conn)) eap_tls_success(sm, data, ret); @@ -258,6 +294,10 @@ static Boolean eap_tls_has_reauth_data(struct eap_sm *sm, void *priv) static void eap_tls_deinit_for_reauth(struct eap_sm *sm, void *priv) { + struct eap_tls_data *data = priv; + + wpabuf_free(data->pending_resp); + data->pending_resp = NULL; } @@ -350,7 +390,6 @@ static u8 * eap_tls_get_session_id(struct eap_sm *sm, void *priv, size_t *len) int eap_peer_tls_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_TLS, "TLS"); @@ -369,10 +408,7 @@ int eap_peer_tls_register(void) eap->init_for_reauth = eap_tls_init_for_reauth; eap->get_emsk = eap_tls_get_emsk; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } @@ -380,7 +416,6 @@ int eap_peer_tls_register(void) int eap_peer_unauth_tls_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_UNAUTH_TLS, @@ -399,10 +434,7 @@ int eap_peer_unauth_tls_register(void) eap->init_for_reauth = eap_tls_init_for_reauth; eap->get_emsk = eap_tls_get_emsk; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } #endif /* EAP_UNAUTH_TLS */ @@ -411,7 +443,6 @@ int eap_peer_unauth_tls_register(void) int eap_peer_wfa_unauth_tls_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_WFA_NEW, @@ -431,9 +462,6 @@ int eap_peer_wfa_unauth_tls_register(void) eap->init_for_reauth = eap_tls_init_for_reauth; eap->get_emsk = eap_tls_get_emsk; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } #endif /* CONFIG_HS20 */ diff --git a/contrib/wpa/src/eap_peer/eap_tls_common.c b/contrib/wpa/src/eap_peer/eap_tls_common.c index af2b7541d701..0dcb9c138f81 100644 --- a/contrib/wpa/src/eap_peer/eap_tls_common.c +++ b/contrib/wpa/src/eap_peer/eap_tls_common.c @@ -80,6 +80,10 @@ static void eap_tls_params_flags(struct tls_connection_params *params, params->flags |= TLS_CONN_DISABLE_TLSv1_2; if (os_strstr(txt, "tls_disable_tlsv1_2=0")) params->flags &= ~TLS_CONN_DISABLE_TLSv1_2; + if (os_strstr(txt, "tls_ext_cert_check=1")) + params->flags |= TLS_CONN_EXT_CERT_CHECK; + if (os_strstr(txt, "tls_ext_cert_check=0")) + params->flags &= ~TLS_CONN_EXT_CERT_CHECK; } @@ -177,6 +181,8 @@ static int eap_tls_params_from_conf(struct eap_sm *sm, params->openssl_ciphers = config->openssl_ciphers; + sm->ext_cert_check = !!(params->flags & TLS_CONN_EXT_CERT_CHECK); + return 0; } @@ -190,8 +196,10 @@ static int eap_tls_init_connection(struct eap_sm *sm, if (config->ocsp) params->flags |= TLS_CONN_REQUEST_OCSP; - if (config->ocsp == 2) + if (config->ocsp >= 2) params->flags |= TLS_CONN_REQUIRE_OCSP; + if (config->ocsp == 3) + params->flags |= TLS_CONN_REQUIRE_OCSP_ALL; data->conn = tls_connection_init(data->ssl_ctx); if (data->conn == NULL) { wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS " @@ -320,8 +328,8 @@ u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, if (out == NULL) return NULL; - if (tls_connection_prf(data->ssl_ctx, data->conn, label, 0, 0, - out, len)) { + if (tls_connection_export_key(data->ssl_ctx, data->conn, label, out, + len)) { os_free(out); return NULL; } @@ -350,10 +358,8 @@ u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm, struct tls_random keys; u8 *out; - if (tls_connection_get_random(sm->ssl_ctx, data->conn, &keys)) - return NULL; - - if (keys.client_random == NULL || keys.server_random == NULL) + if (tls_connection_get_random(sm->ssl_ctx, data->conn, &keys) || + keys.client_random == NULL || keys.server_random == NULL) return NULL; *len = 1 + keys.client_random_len + keys.server_random_len; @@ -1035,6 +1041,9 @@ int eap_peer_select_phase2_methods(struct eap_peer_config *config, if (vendor == EAP_VENDOR_IETF && method == EAP_TYPE_NONE) { wpa_printf(MSG_ERROR, "TLS: Unsupported Phase2 EAP " "method '%s'", start); + os_free(methods); + os_free(buf); + return -1; } else { num_methods++; _methods = os_realloc_array(methods, num_methods, diff --git a/contrib/wpa/src/eap_peer/eap_tnc.c b/contrib/wpa/src/eap_peer/eap_tnc.c index 25b9f124801a..726221e6b69c 100644 --- a/contrib/wpa/src/eap_peer/eap_tnc.c +++ b/contrib/wpa/src/eap_peer/eap_tnc.c @@ -10,6 +10,7 @@ #include "common.h" #include "eap_i.h" +#include "eap_config.h" #include "tncc.h" @@ -35,12 +36,16 @@ struct eap_tnc_data { static void * eap_tnc_init(struct eap_sm *sm) { struct eap_tnc_data *data; + struct eap_peer_config *config = eap_get_config(sm); data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->state = WAIT_START; - data->fragment_size = 1300; + if (config && config->fragment_size) + data->fragment_size = config->fragment_size; + else + data->fragment_size = 1300; data->tncc = tncc_init(); if (data->tncc == NULL) { os_free(data); @@ -345,11 +350,6 @@ static struct wpabuf * eap_tnc_process(struct eap_sm *sm, void *priv, ret->decision = DECISION_UNCOND_SUCC; ret->allowNotifications = TRUE; - if (data->out_buf) { - data->state = PROC_MSG; - return eap_tnc_build_msg(data, ret, id); - } - if (tncs_done) { resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, EAP_CODE_RESPONSE, eap_get_id(reqData)); @@ -410,7 +410,6 @@ fail: int eap_peer_tnc_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_TNC, "TNC"); @@ -421,8 +420,5 @@ int eap_peer_tnc_register(void) eap->deinit = eap_tnc_deinit; eap->process = eap_tnc_process; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } diff --git a/contrib/wpa/src/eap_peer/eap_ttls.c b/contrib/wpa/src/eap_peer/eap_ttls.c index b186c9156a74..92f94dcd6019 100644 --- a/contrib/wpa/src/eap_peer/eap_ttls.c +++ b/contrib/wpa/src/eap_peer/eap_ttls.c @@ -1,6 +1,6 @@ /* * EAP peer method: EAP-TTLS (RFC 5281) - * Copyright (c) 2004-2011, Jouni Malinen + * Copyright (c) 2004-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -35,6 +35,7 @@ struct eap_ttls_data { void *phase2_priv; int phase2_success; int phase2_start; + EapDecision decision_succ; enum phase2_types { EAP_TTLS_PHASE2_EAP, @@ -58,6 +59,7 @@ struct eap_ttls_data { size_t id_len; struct wpabuf *pending_phase2_req; + struct wpabuf *pending_resp; #ifdef EAP_TNC int ready_for_tnc; @@ -70,6 +72,7 @@ static void * eap_ttls_init(struct eap_sm *sm) { struct eap_ttls_data *data; struct eap_peer_config *config = eap_get_config(sm); + int selected_non_eap; char *selected; data = os_zalloc(sizeof(*data)); @@ -77,26 +80,67 @@ static void * eap_ttls_init(struct eap_sm *sm) return NULL; data->ttls_version = EAP_TTLS_VERSION; selected = "EAP"; + selected_non_eap = 0; data->phase2_type = EAP_TTLS_PHASE2_EAP; + /* + * Either one auth= type or one or more autheap= methods can be + * specified. + */ if (config && config->phase2) { + const char *token, *last = NULL; + + while ((token = cstr_token(config->phase2, " \t", &last))) { + if (os_strncmp(token, "auth=", 5) != 0) + continue; + token += 5; + + if (last - token == 8 && + os_strncmp(token, "MSCHAPV2", 8) == 0) { + selected = "MSCHAPV2"; + data->phase2_type = EAP_TTLS_PHASE2_MSCHAPV2; + } else if (last - token == 6 && + os_strncmp(token, "MSCHAP", 6) == 0) { + selected = "MSCHAP"; + data->phase2_type = EAP_TTLS_PHASE2_MSCHAP; + } else if (last - token == 3 && + os_strncmp(token, "PAP", 3) == 0) { + selected = "PAP"; + data->phase2_type = EAP_TTLS_PHASE2_PAP; + } else if (last - token == 4 && + os_strncmp(token, "CHAP", 4) == 0) { + selected = "CHAP"; + data->phase2_type = EAP_TTLS_PHASE2_CHAP; + } else { + wpa_printf(MSG_ERROR, + "EAP-TTLS: Unsupported Phase2 type '%s'", + token); + eap_ttls_deinit(sm, data); + return NULL; + } + + if (selected_non_eap) { + wpa_printf(MSG_ERROR, + "EAP-TTLS: Only one Phase2 type can be specified"); + eap_ttls_deinit(sm, data); + return NULL; + } + + selected_non_eap = 1; + } + if (os_strstr(config->phase2, "autheap=")) { + if (selected_non_eap) { + wpa_printf(MSG_ERROR, + "EAP-TTLS: Both auth= and autheap= params cannot be specified"); + eap_ttls_deinit(sm, data); + return NULL; + } selected = "EAP"; data->phase2_type = EAP_TTLS_PHASE2_EAP; - } else if (os_strstr(config->phase2, "auth=MSCHAPV2")) { - selected = "MSCHAPV2"; - data->phase2_type = EAP_TTLS_PHASE2_MSCHAPV2; - } else if (os_strstr(config->phase2, "auth=MSCHAP")) { - selected = "MSCHAP"; - data->phase2_type = EAP_TTLS_PHASE2_MSCHAP; - } else if (os_strstr(config->phase2, "auth=PAP")) { - selected = "PAP"; - data->phase2_type = EAP_TTLS_PHASE2_PAP; - } else if (os_strstr(config->phase2, "auth=CHAP")) { - selected = "CHAP"; - data->phase2_type = EAP_TTLS_PHASE2_CHAP; } } + wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase2 type: %s", selected); if (data->phase2_type == EAP_TTLS_PHASE2_EAP) { @@ -153,6 +197,7 @@ static void eap_ttls_deinit(struct eap_sm *sm, void *priv) eap_ttls_free_key(data); os_free(data->session_id); wpabuf_free(data->pending_phase2_req); + wpabuf_free(data->pending_resp); os_free(data); } @@ -1408,6 +1453,32 @@ static int eap_ttls_process_handshake(struct eap_sm *sm, { int res; + if (sm->waiting_ext_cert_check && data->pending_resp) { + struct eap_peer_config *config = eap_get_config(sm); + + if (config->pending_ext_cert_check == EXT_CERT_CHECK_GOOD) { + wpa_printf(MSG_DEBUG, + "EAP-TTLS: External certificate check succeeded - continue handshake"); + *out_data = data->pending_resp; + data->pending_resp = NULL; + sm->waiting_ext_cert_check = 0; + return 0; + } + + if (config->pending_ext_cert_check == EXT_CERT_CHECK_BAD) { + wpa_printf(MSG_DEBUG, + "EAP-TTLS: External certificate check failed - force authentication failure"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + sm->waiting_ext_cert_check = 0; + return 0; + } + + wpa_printf(MSG_DEBUG, + "EAP-TTLS: Continuing to wait external server certificate validation"); + return 0; + } + res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_TTLS, data->ttls_version, identifier, in_data, out_data); @@ -1418,6 +1489,15 @@ static int eap_ttls_process_handshake(struct eap_sm *sm, return -1; } + if (sm->waiting_ext_cert_check) { + wpa_printf(MSG_DEBUG, + "EAP-TTLS: Waiting external server certificate validation"); + wpabuf_free(data->pending_resp); + data->pending_resp = *out_data; + *out_data = NULL; + return 0; + } + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS done, proceed to " "Phase 2"); @@ -1468,6 +1548,7 @@ static void eap_ttls_check_auth_status(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication " "completed successfully"); data->phase2_success = 1; + data->decision_succ = ret->decision; #ifdef EAP_TNC if (!data->ready_for_tnc && !data->tnc_started) { /* @@ -1485,6 +1566,18 @@ static void eap_ttls_check_auth_status(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication " "completed successfully (MAY_CONT)"); data->phase2_success = 1; + data->decision_succ = ret->decision; + } else if (data->decision_succ != DECISION_FAIL && + data->phase2_success && + !data->ssl.tls_out) { + /* + * This is needed to cover the case where the final Phase 2 + * message gets fragmented since fragmentation clears + * decision back to FAIL. + */ + wpa_printf(MSG_DEBUG, + "EAP-TTLS: Restore success decision after fragmented frame sent completely"); + ret->decision = data->decision_succ; } } @@ -1557,6 +1650,9 @@ static void eap_ttls_deinit_for_reauth(struct eap_sm *sm, void *priv) struct eap_ttls_data *data = priv; wpabuf_free(data->pending_phase2_req); data->pending_phase2_req = NULL; + wpabuf_free(data->pending_resp); + data->pending_resp = NULL; + data->decision_succ = DECISION_FAIL; #ifdef EAP_TNC data->ready_for_tnc = 0; data->tnc_started = 0; @@ -1695,7 +1791,6 @@ static u8 * eap_ttls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) int eap_peer_ttls_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_TTLS, "TTLS"); @@ -1714,8 +1809,5 @@ int eap_peer_ttls_register(void) eap->init_for_reauth = eap_ttls_init_for_reauth; eap->get_emsk = eap_ttls_get_emsk; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } diff --git a/contrib/wpa/src/eap_peer/eap_vendor_test.c b/contrib/wpa/src/eap_peer/eap_vendor_test.c index b61057ee6755..16e3c39563b7 100644 --- a/contrib/wpa/src/eap_peer/eap_vendor_test.c +++ b/contrib/wpa/src/eap_peer/eap_vendor_test.c @@ -169,7 +169,6 @@ static u8 * eap_vendor_test_getKey(struct eap_sm *sm, void *priv, size_t *len) int eap_peer_vendor_test_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_ID, EAP_VENDOR_TYPE, @@ -183,8 +182,5 @@ int eap_peer_vendor_test_register(void) eap->isKeyAvailable = eap_vendor_test_isKeyAvailable; eap->getKey = eap_vendor_test_getKey; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } diff --git a/contrib/wpa/src/eap_peer/eap_wsc.c b/contrib/wpa/src/eap_peer/eap_wsc.c index 7ac99c7ce727..d140c88b8cdd 100644 --- a/contrib/wpa/src/eap_peer/eap_wsc.c +++ b/contrib/wpa/src/eap_peer/eap_wsc.c @@ -17,7 +17,7 @@ struct eap_wsc_data { - enum { WAIT_START, MESG, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state; + enum { WAIT_START, MESG, WAIT_FRAG_ACK, FAIL } state; int registrar; struct wpabuf *in_buf; struct wpabuf *out_buf; @@ -36,12 +36,8 @@ static const char * eap_wsc_state_txt(int state) return "WAIT_START"; case MESG: return "MESG"; - case FRAG_ACK: - return "FRAG_ACK"; case WAIT_FRAG_ACK: return "WAIT_FRAG_ACK"; - case DONE: - return "DONE"; case FAIL: return "FAIL"; default: @@ -579,7 +575,6 @@ send_msg: int eap_peer_wsc_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, @@ -591,8 +586,5 @@ int eap_peer_wsc_register(void) eap->deinit = eap_wsc_deinit; eap->process = eap_wsc_process; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } diff --git a/contrib/wpa/src/eap_peer/ikev2.c b/contrib/wpa/src/eap_peer/ikev2.c index 55ab72aee66c..ca6502ea02e7 100644 --- a/contrib/wpa/src/eap_peer/ikev2.c +++ b/contrib/wpa/src/eap_peer/ikev2.c @@ -128,7 +128,7 @@ static int ikev2_parse_transform(struct ikev2_proposal_data *prop, t = (const struct ikev2_transform *) pos; transform_len = WPA_GET_BE16(t->transform_length); - if (transform_len < (int) sizeof(*t) || pos + transform_len > end) { + if (transform_len < (int) sizeof(*t) || transform_len > end - pos) { wpa_printf(MSG_INFO, "IKEV2: Invalid transform length %d", transform_len); return -1; @@ -248,7 +248,7 @@ static int ikev2_parse_proposal(struct ikev2_proposal_data *prop, ppos = (const u8 *) (p + 1); pend = pos + proposal_len; - if (ppos + p->spi_size > pend) { + if (p->spi_size > pend - ppos) { wpa_printf(MSG_INFO, "IKEV2: Not enough room for SPI " "in proposal"); return -1; diff --git a/contrib/wpa/src/eap_peer/tncc.c b/contrib/wpa/src/eap_peer/tncc.c index 7ca956e5b235..0c5caa7dd522 100644 --- a/contrib/wpa/src/eap_peer/tncc.c +++ b/contrib/wpa/src/eap_peer/tncc.c @@ -104,7 +104,7 @@ static struct tnc_if_imc *tnc_imc[TNC_MAX_IMC_ID] = { NULL }; /* TNCC functions that IMCs can call */ -TNC_Result TNC_TNCC_ReportMessageTypes( +static TNC_Result TNC_TNCC_ReportMessageTypes( TNC_IMCID imcID, TNC_MessageTypeList supportedTypes, TNC_UInt32 typeCount) @@ -138,7 +138,7 @@ TNC_Result TNC_TNCC_ReportMessageTypes( } -TNC_Result TNC_TNCC_SendMessage( +static TNC_Result TNC_TNCC_SendMessage( TNC_IMCID imcID, TNC_ConnectionID connectionID, TNC_BufferReference message, @@ -183,7 +183,7 @@ TNC_Result TNC_TNCC_SendMessage( } -TNC_Result TNC_TNCC_RequestHandshakeRetry( +static TNC_Result TNC_TNCC_RequestHandshakeRetry( TNC_IMCID imcID, TNC_ConnectionID connectionID, TNC_RetryReason reason) @@ -203,8 +203,8 @@ TNC_Result TNC_TNCC_RequestHandshakeRetry( } -TNC_Result TNC_9048_LogMessage(TNC_IMCID imcID, TNC_UInt32 severity, - const char *message) +static TNC_Result TNC_9048_LogMessage(TNC_IMCID imcID, TNC_UInt32 severity, + const char *message) { wpa_printf(MSG_DEBUG, "TNC: TNC_9048_LogMessage(imcID=%lu " "severity==%lu message='%s')", @@ -213,8 +213,9 @@ TNC_Result TNC_9048_LogMessage(TNC_IMCID imcID, TNC_UInt32 severity, } -TNC_Result TNC_9048_UserMessage(TNC_IMCID imcID, TNC_ConnectionID connectionID, - const char *message) +static TNC_Result TNC_9048_UserMessage(TNC_IMCID imcID, + TNC_ConnectionID connectionID, + const char *message) { wpa_printf(MSG_DEBUG, "TNC: TNC_9048_UserMessage(imcID=%lu " "connectionID==%lu message='%s')", @@ -223,7 +224,7 @@ TNC_Result TNC_9048_UserMessage(TNC_IMCID imcID, TNC_ConnectionID connectionID, } -TNC_Result TNC_TNCC_BindFunction( +static TNC_Result TNC_TNCC_BindFunction( TNC_IMCID imcID, char *functionName, void **pOutfunctionPointer) @@ -694,6 +695,8 @@ enum tncc_process_res tncc_process_if_tnccs(struct tncc_data *tncc, enum tncc_process_res res = TNCCS_PROCESS_OK_NO_RECOMMENDATION; int recommendation_msg = 0; + wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: Received IF-TNCCS message", + msg, len); buf = dup_binstr(msg, len); if (buf == NULL) return TNCCS_PROCESS_ERROR; diff --git a/contrib/wpa/src/eap_server/eap_methods.h b/contrib/wpa/src/eap_server/eap_methods.h index 0baa3279086e..3bf1495f76bf 100644 --- a/contrib/wpa/src/eap_server/eap_methods.h +++ b/contrib/wpa/src/eap_server/eap_methods.h @@ -15,7 +15,6 @@ const struct eap_method * eap_server_get_eap_method(int vendor, EapType method); struct eap_method * eap_server_method_alloc(int version, int vendor, EapType method, const char *name); -void eap_server_method_free(struct eap_method *method); int eap_server_method_register(struct eap_method *method); EapType eap_server_get_type(const char *name, int *vendor); diff --git a/contrib/wpa/src/eap_server/eap_server_aka.c b/contrib/wpa/src/eap_server/eap_server_aka.c index db9b6aa2db39..a8bb5eae6b56 100644 --- a/contrib/wpa/src/eap_server/eap_server_aka.c +++ b/contrib/wpa/src/eap_server/eap_server_aka.c @@ -1319,7 +1319,6 @@ static u8 * eap_aka_get_session_id(struct eap_sm *sm, void *priv, size_t *len) int eap_server_aka_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_AKA, "AKA"); @@ -1337,10 +1336,7 @@ int eap_server_aka_register(void) eap->get_emsk = eap_aka_get_emsk; eap->getSessionId = eap_aka_get_session_id; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } @@ -1348,7 +1344,6 @@ int eap_server_aka_register(void) int eap_server_aka_prime_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME, @@ -1367,10 +1362,6 @@ int eap_server_aka_prime_register(void) eap->get_emsk = eap_aka_get_emsk; eap->getSessionId = eap_aka_get_session_id; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - - return ret; + return eap_server_method_register(eap); } #endif /* EAP_SERVER_AKA_PRIME */ diff --git a/contrib/wpa/src/eap_server/eap_server_eke.c b/contrib/wpa/src/eap_server/eap_server_eke.c index ba82be9c3f3a..1eba8f515648 100644 --- a/contrib/wpa/src/eap_server/eap_server_eke.c +++ b/contrib/wpa/src/eap_server/eap_server_eke.c @@ -792,7 +792,6 @@ static u8 * eap_eke_get_session_id(struct eap_sm *sm, void *priv, size_t *len) int eap_server_eke_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_EKE, "EKE"); @@ -810,8 +809,5 @@ int eap_server_eke_register(void) eap->get_emsk = eap_eke_get_emsk; eap->getSessionId = eap_eke_get_session_id; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } diff --git a/contrib/wpa/src/eap_server/eap_server_fast.c b/contrib/wpa/src/eap_server/eap_server_fast.c index bd9018e78b56..20491726880e 100644 --- a/contrib/wpa/src/eap_server/eap_server_fast.c +++ b/contrib/wpa/src/eap_server/eap_server_fast.c @@ -180,42 +180,47 @@ static int eap_fast_session_ticket_cb(void *ctx, const u8 *ticket, size_t len, buf, end - buf); pos = buf; - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) + while (end - pos > 1) { + u8 id, elen; + + id = *pos++; + elen = *pos++; + if (elen > end - pos) break; - switch (*pos) { + switch (id) { case PAC_OPAQUE_TYPE_PAD: goto done; case PAC_OPAQUE_TYPE_KEY: - if (pos[1] != EAP_FAST_PAC_KEY_LEN) { - wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid " - "PAC-Key length %d", pos[1]); + if (elen != EAP_FAST_PAC_KEY_LEN) { + wpa_printf(MSG_DEBUG, + "EAP-FAST: Invalid PAC-Key length %d", + elen); os_free(buf); return -1; } - pac_key = pos + 2; + pac_key = pos; wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: PAC-Key from " "decrypted PAC-Opaque", pac_key, EAP_FAST_PAC_KEY_LEN); break; case PAC_OPAQUE_TYPE_LIFETIME: - if (pos[1] != 4) { + if (elen != 4) { wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid " "PAC-Key lifetime length %d", - pos[1]); + elen); os_free(buf); return -1; } - lifetime = WPA_GET_BE32(pos + 2); + lifetime = WPA_GET_BE32(pos); break; case PAC_OPAQUE_TYPE_IDENTITY: - identity = pos + 2; - identity_len = pos[1]; + identity = pos; + identity_len = elen; break; } - pos += 2 + pos[1]; + pos += elen; } done: @@ -273,7 +278,7 @@ static void eap_fast_derive_key_auth(struct eap_sm *sm, * Extra key material after TLS key_block: session_key_seed[40] */ - sks = eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn, "key expansion", + sks = eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn, EAP_FAST_SKS_LEN); if (sks == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive " @@ -300,7 +305,6 @@ static void eap_fast_derive_key_provisioning(struct eap_sm *sm, os_free(data->key_block_p); data->key_block_p = (struct eap_fast_key_block_provisioning *) eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn, - "key expansion", sizeof(*data->key_block_p)); if (data->key_block_p == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive key block"); @@ -407,11 +411,13 @@ static int eap_fast_update_icmk(struct eap_sm *sm, struct eap_fast_data *data) static void * eap_fast_init(struct eap_sm *sm) { struct eap_fast_data *data; - u8 ciphers[5] = { + u8 ciphers[7] = { TLS_CIPHER_ANON_DH_AES128_SHA, TLS_CIPHER_AES128_SHA, TLS_CIPHER_RSA_DHE_AES128_SHA, TLS_CIPHER_RC4_SHA, + TLS_CIPHER_RSA_DHE_AES256_SHA, + TLS_CIPHER_AES256_SHA, TLS_CIPHER_NONE }; @@ -1134,7 +1140,7 @@ static int eap_fast_parse_tlvs(struct wpabuf *data, pos = wpabuf_mhead(data); end = pos + wpabuf_len(data); - while (pos + 4 < end) { + while (end - pos > 4) { mandatory = pos[0] & 0x80; tlv_type = WPA_GET_BE16(pos) & 0x3fff; pos += 2; @@ -1559,7 +1565,10 @@ static u8 * eap_fast_getKey(struct eap_sm *sm, void *priv, size_t *len) if (eapKeyData == NULL) return NULL; - eap_fast_derive_eap_msk(data->simck, eapKeyData); + if (eap_fast_derive_eap_msk(data->simck, eapKeyData) < 0) { + os_free(eapKeyData); + return NULL; + } *len = EAP_FAST_KEY_LEN; return eapKeyData; @@ -1578,7 +1587,10 @@ static u8 * eap_fast_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (eapKeyData == NULL) return NULL; - eap_fast_derive_eap_emsk(data->simck, eapKeyData); + if (eap_fast_derive_eap_emsk(data->simck, eapKeyData) < 0) { + os_free(eapKeyData); + return NULL; + } *len = EAP_EMSK_LEN; return eapKeyData; @@ -1607,7 +1619,6 @@ static u8 * eap_fast_get_session_id(struct eap_sm *sm, void *priv, size_t *len) int eap_server_fast_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_FAST, "FAST"); @@ -1625,8 +1636,5 @@ int eap_server_fast_register(void) eap->isSuccess = eap_fast_isSuccess; eap->getSessionId = eap_fast_get_session_id; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } diff --git a/contrib/wpa/src/eap_server/eap_server_gpsk.c b/contrib/wpa/src/eap_server/eap_server_gpsk.c index 50f15c31d0dc..94e74ec9b2f7 100644 --- a/contrib/wpa/src/eap_server/eap_server_gpsk.c +++ b/contrib/wpa/src/eap_server/eap_server_gpsk.c @@ -631,7 +631,6 @@ static u8 * eap_gpsk_get_session_id(struct eap_sm *sm, void *priv, size_t *len) int eap_server_gpsk_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_GPSK, "GPSK"); @@ -649,8 +648,5 @@ int eap_server_gpsk_register(void) eap->get_emsk = eap_gpsk_get_emsk; eap->getSessionId = eap_gpsk_get_session_id; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } diff --git a/contrib/wpa/src/eap_server/eap_server_gtc.c b/contrib/wpa/src/eap_server/eap_server_gtc.c index 98ac3c6ec495..193a8517ac08 100644 --- a/contrib/wpa/src/eap_server/eap_server_gtc.c +++ b/contrib/wpa/src/eap_server/eap_server_gtc.c @@ -202,7 +202,6 @@ static Boolean eap_gtc_isSuccess(struct eap_sm *sm, void *priv) int eap_server_gtc_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_GTC, "GTC"); @@ -217,8 +216,5 @@ int eap_server_gtc_register(void) eap->isDone = eap_gtc_isDone; eap->isSuccess = eap_gtc_isSuccess; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } diff --git a/contrib/wpa/src/eap_server/eap_server_identity.c b/contrib/wpa/src/eap_server/eap_server_identity.c index 45015336b907..1b1db53f25b2 100644 --- a/contrib/wpa/src/eap_server/eap_server_identity.c +++ b/contrib/wpa/src/eap_server/eap_server_identity.c @@ -157,7 +157,6 @@ static Boolean eap_identity_isSuccess(struct eap_sm *sm, void *priv) int eap_server_identity_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, @@ -174,8 +173,5 @@ int eap_server_identity_register(void) eap->isDone = eap_identity_isDone; eap->isSuccess = eap_identity_isSuccess; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } diff --git a/contrib/wpa/src/eap_server/eap_server_ikev2.c b/contrib/wpa/src/eap_server/eap_server_ikev2.c index 16e62764cc55..3a249d141e0c 100644 --- a/contrib/wpa/src/eap_server/eap_server_ikev2.c +++ b/contrib/wpa/src/eap_server/eap_server_ikev2.c @@ -550,7 +550,6 @@ static u8 * eap_ikev2_get_session_id(struct eap_sm *sm, void *priv, size_t *len) int eap_server_ikev2_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_IKEV2, @@ -569,8 +568,5 @@ int eap_server_ikev2_register(void) eap->get_emsk = eap_ikev2_get_emsk; eap->getSessionId = eap_ikev2_get_session_id; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } diff --git a/contrib/wpa/src/eap_server/eap_server_md5.c b/contrib/wpa/src/eap_server/eap_server_md5.c index 71e8d59e0396..cf5ceb1d1529 100644 --- a/contrib/wpa/src/eap_server/eap_server_md5.c +++ b/contrib/wpa/src/eap_server/eap_server_md5.c @@ -153,7 +153,6 @@ static Boolean eap_md5_isSuccess(struct eap_sm *sm, void *priv) int eap_server_md5_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_MD5, "MD5"); @@ -168,8 +167,5 @@ int eap_server_md5_register(void) eap->isDone = eap_md5_isDone; eap->isSuccess = eap_md5_isSuccess; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } diff --git a/contrib/wpa/src/eap_server/eap_server_methods.c b/contrib/wpa/src/eap_server/eap_server_methods.c index 9e9dc934eb77..79ed3447ac0a 100644 --- a/contrib/wpa/src/eap_server/eap_server_methods.c +++ b/contrib/wpa/src/eap_server/eap_server_methods.c @@ -87,7 +87,7 @@ struct eap_method * eap_server_method_alloc(int version, int vendor, * eap_server_method_free - Free EAP server method structure * @method: Method structure allocated with eap_server_method_alloc() */ -void eap_server_method_free(struct eap_method *method) +static void eap_server_method_free(struct eap_method *method) { os_free(method); } @@ -95,26 +95,31 @@ void eap_server_method_free(struct eap_method *method) /** * eap_server_method_register - Register an EAP server method - * @method: EAP method to register + * @method: EAP method to register from eap_server_method_alloc() * Returns: 0 on success, -1 on invalid method, or -2 if a matching EAP method * has already been registered * * Each EAP server method needs to call this function to register itself as a - * supported EAP method. + * supported EAP method. The caller must not free the allocated method data + * regardless of the return value. */ int eap_server_method_register(struct eap_method *method) { struct eap_method *m, *last = NULL; if (method == NULL || method->name == NULL || - method->version != EAP_SERVER_METHOD_INTERFACE_VERSION) + method->version != EAP_SERVER_METHOD_INTERFACE_VERSION) { + eap_server_method_free(method); return -1; + } for (m = eap_methods; m; m = m->next) { if ((m->vendor == method->vendor && m->method == method->method) || - os_strcmp(m->name, method->name) == 0) + os_strcmp(m->name, method->name) == 0) { + eap_server_method_free(method); return -2; + } last = m; } diff --git a/contrib/wpa/src/eap_server/eap_server_mschapv2.c b/contrib/wpa/src/eap_server/eap_server_mschapv2.c index 98d74e0d717e..460cd9c82ff5 100644 --- a/contrib/wpa/src/eap_server/eap_server_mschapv2.c +++ b/contrib/wpa/src/eap_server/eap_server_mschapv2.c @@ -571,7 +571,6 @@ static Boolean eap_mschapv2_isSuccess(struct eap_sm *sm, void *priv) int eap_server_mschapv2_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, @@ -588,8 +587,5 @@ int eap_server_mschapv2_register(void) eap->getKey = eap_mschapv2_getKey; eap->isSuccess = eap_mschapv2_isSuccess; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } diff --git a/contrib/wpa/src/eap_server/eap_server_pax.c b/contrib/wpa/src/eap_server/eap_server_pax.c index 0e6b4a0698ed..782b8c316537 100644 --- a/contrib/wpa/src/eap_server/eap_server_pax.c +++ b/contrib/wpa/src/eap_server/eap_server_pax.c @@ -565,7 +565,6 @@ static u8 * eap_pax_get_session_id(struct eap_sm *sm, void *priv, size_t *len) int eap_server_pax_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_PAX, "PAX"); @@ -583,8 +582,5 @@ int eap_server_pax_register(void) eap->get_emsk = eap_pax_get_emsk; eap->getSessionId = eap_pax_get_session_id; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } diff --git a/contrib/wpa/src/eap_server/eap_server_peap.c b/contrib/wpa/src/eap_server/eap_server_peap.c index 51062b0987e4..18d31b527fdd 100644 --- a/contrib/wpa/src/eap_server/eap_server_peap.c +++ b/contrib/wpa/src/eap_server/eap_server_peap.c @@ -335,6 +335,18 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60); + if (tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) { + /* Fast-connect: IPMK|CMK = TK */ + os_memcpy(data->ipmk, tk, 40); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK from TK", + data->ipmk, 40); + os_memcpy(data->cmk, tk + 40, 20); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK from TK", + data->cmk, 20); + os_free(tk); + return 0; + } + eap_peap_get_isk(data, isk, sizeof(isk)); wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: ISK", isk, sizeof(isk)); @@ -357,7 +369,6 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) os_free(tk); - /* TODO: fast-connect: IPMK|CMK = TK */ os_memcpy(data->ipmk, imck, 40); wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40); os_memcpy(data->cmk, imck + 40, 20); @@ -1267,8 +1278,9 @@ static void eap_peap_process(struct eap_sm *sm, void *priv, wpa_printf(MSG_DEBUG, "EAP-PEAP: Resuming previous session - skip Phase2"); - eap_peap_state(data, SUCCESS_REQ); - tls_connection_set_success_data_resumed(data->ssl.conn); + eap_peap_req_success(sm, data); + if (data->state == SUCCESS_REQ) + tls_connection_set_success_data_resumed(data->ssl.conn); } @@ -1351,7 +1363,6 @@ static u8 * eap_peap_get_session_id(struct eap_sm *sm, void *priv, size_t *len) int eap_server_peap_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_PEAP, "PEAP"); @@ -1368,8 +1379,5 @@ int eap_server_peap_register(void) eap->isSuccess = eap_peap_isSuccess; eap->getSessionId = eap_peap_get_session_id; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } diff --git a/contrib/wpa/src/eap_server/eap_server_psk.c b/contrib/wpa/src/eap_server/eap_server_psk.c index 12b5d25d67ff..857d421393bc 100644 --- a/contrib/wpa/src/eap_server/eap_server_psk.c +++ b/contrib/wpa/src/eap_server/eap_server_psk.c @@ -510,7 +510,6 @@ static u8 * eap_psk_get_session_id(struct eap_sm *sm, void *priv, size_t *len) int eap_server_psk_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_PSK, "PSK"); @@ -528,8 +527,5 @@ int eap_server_psk_register(void) eap->get_emsk = eap_psk_get_emsk; eap->getSessionId = eap_psk_get_session_id; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } diff --git a/contrib/wpa/src/eap_server/eap_server_pwd.c b/contrib/wpa/src/eap_server/eap_server_pwd.c index cb83ff7305bd..64bf708e039a 100644 --- a/contrib/wpa/src/eap_server/eap_server_pwd.c +++ b/contrib/wpa/src/eap_server/eap_server_pwd.c @@ -178,8 +178,13 @@ static void eap_pwd_build_id_req(struct eap_sm *sm, struct eap_pwd_data *data, return; } - /* an lfsr is good enough to generate unpredictable tokens */ - data->token = os_random(); + if (os_get_random((u8 *) &data->token, sizeof(data->token)) < 0) { + wpabuf_free(data->outbuf); + data->outbuf = NULL; + eap_pwd_state(data, FAILURE); + return; + } + wpabuf_put_be16(data->outbuf, data->group_num); wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_RAND_FUNC); wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_PRF); @@ -970,7 +975,7 @@ 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)) { + if (EAP_PWD_GET_MORE_BIT(lm_exch) || data->in_frag_pos) { if ((data->in_frag_pos + len) > wpabuf_size(data->inbuf)) { wpa_printf(MSG_DEBUG, "EAP-pwd: Buffer overflow " "attack detected! (%d+%d > %d)", @@ -981,6 +986,8 @@ static void eap_pwd_process(struct eap_sm *sm, void *priv, } wpabuf_put_data(data->inbuf, pos, len); data->in_frag_pos += len; + } + if (EAP_PWD_GET_MORE_BIT(lm_exch)) { wpa_printf(MSG_DEBUG, "EAP-pwd: Got a %d byte fragment", (int) len); return; @@ -990,8 +997,6 @@ static void eap_pwd_process(struct eap_sm *sm, void *priv, * buffering fragments so that's how we know it's the last) */ if (data->in_frag_pos) { - wpabuf_put_data(data->inbuf, pos, len); - data->in_frag_pos += len; pos = wpabuf_head_u8(data->inbuf); len = data->in_frag_pos; wpa_printf(MSG_DEBUG, "EAP-pwd: Last fragment, %d bytes", @@ -1094,7 +1099,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; - int ret; struct timeval tp; struct timezone tz; u32 sr; @@ -1121,9 +1125,6 @@ int eap_server_pwd_register(void) eap->isSuccess = eap_pwd_is_success; eap->getSessionId = eap_pwd_get_session_id; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } diff --git a/contrib/wpa/src/eap_server/eap_server_sake.c b/contrib/wpa/src/eap_server/eap_server_sake.c index de7077731899..84d0e0be4dd1 100644 --- a/contrib/wpa/src/eap_server/eap_server_sake.c +++ b/contrib/wpa/src/eap_server/eap_server_sake.c @@ -520,7 +520,6 @@ static u8 * eap_sake_get_session_id(struct eap_sm *sm, void *priv, size_t *len) int eap_server_sake_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_SAKE, "SAKE"); @@ -538,8 +537,5 @@ int eap_server_sake_register(void) eap->get_emsk = eap_sake_get_emsk; eap->getSessionId = eap_sake_get_session_id; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } diff --git a/contrib/wpa/src/eap_server/eap_server_sim.c b/contrib/wpa/src/eap_server/eap_server_sim.c index ddfb71cf4e2e..3a6ed795c768 100644 --- a/contrib/wpa/src/eap_server/eap_server_sim.c +++ b/contrib/wpa/src/eap_server/eap_server_sim.c @@ -846,7 +846,6 @@ static u8 * eap_sim_get_session_id(struct eap_sm *sm, void *priv, size_t *len) int eap_server_sim_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_SIM, "SIM"); @@ -864,8 +863,5 @@ int eap_server_sim_register(void) eap->get_emsk = eap_sim_get_emsk; eap->getSessionId = eap_sim_get_session_id; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } diff --git a/contrib/wpa/src/eap_server/eap_server_tls.c b/contrib/wpa/src/eap_server/eap_server_tls.c index bd18a4ba654c..7249858844ef 100644 --- a/contrib/wpa/src/eap_server/eap_server_tls.c +++ b/contrib/wpa/src/eap_server/eap_server_tls.c @@ -375,7 +375,6 @@ static u8 * eap_tls_get_session_id(struct eap_sm *sm, void *priv, size_t *len) int eap_server_tls_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_TLS, "TLS"); @@ -393,10 +392,7 @@ int eap_server_tls_register(void) eap->get_emsk = eap_tls_get_emsk; eap->getSessionId = eap_tls_get_session_id; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } @@ -404,7 +400,6 @@ int eap_server_tls_register(void) int eap_server_unauth_tls_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_UNAUTH_TLS, @@ -423,10 +418,7 @@ int eap_server_unauth_tls_register(void) eap->isSuccess = eap_tls_isSuccess; eap->get_emsk = eap_tls_get_emsk; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } #endif /* EAP_SERVER_UNAUTH_TLS */ @@ -435,7 +427,6 @@ int eap_server_unauth_tls_register(void) int eap_server_wfa_unauth_tls_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_WFA_NEW, @@ -454,9 +445,6 @@ int eap_server_wfa_unauth_tls_register(void) eap->isSuccess = eap_tls_isSuccess; eap->get_emsk = eap_tls_get_emsk; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } #endif /* CONFIG_HS20 */ diff --git a/contrib/wpa/src/eap_server/eap_server_tls_common.c b/contrib/wpa/src/eap_server/eap_server_tls_common.c index 05677b70e887..69096954b826 100644 --- a/contrib/wpa/src/eap_server/eap_server_tls_common.c +++ b/contrib/wpa/src/eap_server/eap_server_tls_common.c @@ -115,8 +115,8 @@ u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, if (out == NULL) return NULL; - if (tls_connection_prf(sm->ssl_ctx, data->conn, label, 0, 0, - out, len)) { + if (tls_connection_export_key(sm->ssl_ctx, data->conn, label, out, + len)) { os_free(out); return NULL; } diff --git a/contrib/wpa/src/eap_server/eap_server_tnc.c b/contrib/wpa/src/eap_server/eap_server_tnc.c index 21bd26f8296e..b568558fd42e 100644 --- a/contrib/wpa/src/eap_server/eap_server_tnc.c +++ b/contrib/wpa/src/eap_server/eap_server_tnc.c @@ -554,7 +554,6 @@ static Boolean eap_tnc_isSuccess(struct eap_sm *sm, void *priv) int eap_server_tnc_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_TNC, "TNC"); @@ -569,8 +568,5 @@ int eap_server_tnc_register(void) eap->isDone = eap_tnc_isDone; eap->isSuccess = eap_tnc_isSuccess; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } diff --git a/contrib/wpa/src/eap_server/eap_server_ttls.c b/contrib/wpa/src/eap_server/eap_server_ttls.c index 53ffa1ec6785..a53633f8f1fe 100644 --- a/contrib/wpa/src/eap_server/eap_server_ttls.c +++ b/contrib/wpa/src/eap_server/eap_server_ttls.c @@ -1335,7 +1335,6 @@ static u8 * eap_ttls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) int eap_server_ttls_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_TTLS, "TTLS"); @@ -1353,8 +1352,5 @@ int eap_server_ttls_register(void) eap->getSessionId = eap_ttls_get_session_id; eap->get_emsk = eap_ttls_get_emsk; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } diff --git a/contrib/wpa/src/eap_server/eap_server_vendor_test.c b/contrib/wpa/src/eap_server/eap_server_vendor_test.c index 30f600d3baf6..96399775945b 100644 --- a/contrib/wpa/src/eap_server/eap_server_vendor_test.c +++ b/contrib/wpa/src/eap_server/eap_server_vendor_test.c @@ -168,7 +168,6 @@ static Boolean eap_vendor_test_isSuccess(struct eap_sm *sm, void *priv) int eap_server_vendor_test_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_ID, EAP_VENDOR_TYPE, @@ -185,8 +184,5 @@ int eap_server_vendor_test_register(void) eap->getKey = eap_vendor_test_getKey; eap->isSuccess = eap_vendor_test_isSuccess; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } diff --git a/contrib/wpa/src/eap_server/eap_server_wsc.c b/contrib/wpa/src/eap_server/eap_server_wsc.c index 9d9c28d704c1..7d9d285c39d0 100644 --- a/contrib/wpa/src/eap_server/eap_server_wsc.c +++ b/contrib/wpa/src/eap_server/eap_server_wsc.c @@ -488,7 +488,6 @@ static int eap_wsc_getTimeout(struct eap_sm *sm, void *priv) int eap_server_wsc_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, @@ -505,8 +504,5 @@ int eap_server_wsc_register(void) eap->isSuccess = eap_wsc_isSuccess; eap->getTimeout = eap_wsc_getTimeout; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } diff --git a/contrib/wpa/src/eap_server/eap_sim_db.c b/contrib/wpa/src/eap_server/eap_sim_db.c index acf5435300dc..d84c3d27de6b 100644 --- a/contrib/wpa/src/eap_server/eap_sim_db.c +++ b/contrib/wpa/src/eap_server/eap_sim_db.c @@ -66,6 +66,7 @@ struct eap_sim_db_data { struct eap_sim_pseudonym *pseudonyms; struct eap_sim_reauth *reauths; struct eap_sim_db_pending *pending; + unsigned int eap_sim_db_timeout; #ifdef CONFIG_SQLITE sqlite3 *sqlite_db; char db_tmp_identity[100]; @@ -76,6 +77,10 @@ struct eap_sim_db_data { }; +static void eap_sim_db_del_timeout(void *eloop_ctx, void *user_ctx); +static void eap_sim_db_query_timeout(void *eloop_ctx, void *user_ctx); + + #ifdef CONFIG_SQLITE static int db_table_exists(sqlite3 *db, const char *name) @@ -397,6 +402,57 @@ static void eap_sim_db_add_pending(struct eap_sim_db_data *data, } +static void eap_sim_db_free_pending(struct eap_sim_db_data *data, + struct eap_sim_db_pending *entry) +{ + eloop_cancel_timeout(eap_sim_db_query_timeout, data, entry); + eloop_cancel_timeout(eap_sim_db_del_timeout, data, entry); + os_free(entry); +} + + +static void eap_sim_db_del_pending(struct eap_sim_db_data *data, + struct eap_sim_db_pending *entry) +{ + struct eap_sim_db_pending **pp = &data->pending; + + while (*pp != NULL) { + if (*pp == entry) { + *pp = entry->next; + eap_sim_db_free_pending(data, entry); + return; + } + pp = &(*pp)->next; + } +} + + +static void eap_sim_db_del_timeout(void *eloop_ctx, void *user_ctx) +{ + struct eap_sim_db_data *data = eloop_ctx; + struct eap_sim_db_pending *entry = user_ctx; + + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Delete query timeout for %p", entry); + eap_sim_db_del_pending(data, entry); +} + + +static void eap_sim_db_query_timeout(void *eloop_ctx, void *user_ctx) +{ + struct eap_sim_db_data *data = eloop_ctx; + struct eap_sim_db_pending *entry = user_ctx; + + /* + * Report failure and allow some time for EAP server to process it + * before deleting the query. + */ + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Query timeout for %p", entry); + entry->state = FAILURE; + data->get_complete_cb(data->ctx, entry->cb_session_ctx); + eloop_register_timeout(1, 0, eap_sim_db_del_timeout, data, entry); +} + + static void eap_sim_db_sim_resp_auth(struct eap_sim_db_data *data, const char *imsi, char *buf) { @@ -472,7 +528,7 @@ static void eap_sim_db_sim_resp_auth(struct eap_sim_db_data *data, parse_fail: wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string"); - os_free(entry); + eap_sim_db_free_pending(data, entry); } @@ -563,7 +619,7 @@ static void eap_sim_db_aka_resp_auth(struct eap_sim_db_data *data, parse_fail: wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string"); - os_free(entry); + eap_sim_db_free_pending(data, entry); } @@ -690,12 +746,13 @@ static void eap_sim_db_close_socket(struct eap_sim_db_data *data) /** * eap_sim_db_init - Initialize EAP-SIM DB / authentication gateway interface * @config: Configuration data (e.g., file name) + * @db_timeout: Database lookup timeout * @get_complete_cb: Callback function for reporting availability of triplets * @ctx: Context pointer for get_complete_cb * Returns: Pointer to a private data structure or %NULL on failure */ struct eap_sim_db_data * -eap_sim_db_init(const char *config, +eap_sim_db_init(const char *config, unsigned int db_timeout, void (*get_complete_cb)(void *ctx, void *session_ctx), void *ctx) { @@ -709,6 +766,7 @@ eap_sim_db_init(const char *config, data->sock = -1; data->get_complete_cb = get_complete_cb; data->ctx = ctx; + data->eap_sim_db_timeout = db_timeout; data->fname = os_strdup(config); if (data->fname == NULL) goto fail; @@ -796,7 +854,7 @@ void eap_sim_db_deinit(void *priv) while (pending) { prev_pending = pending; pending = pending->next; - os_free(prev_pending); + eap_sim_db_free_pending(data, prev_pending); } os_free(data); @@ -833,11 +891,11 @@ static int eap_sim_db_send(struct eap_sim_db_data *data, const char *msg, } -static void eap_sim_db_expire_pending(struct eap_sim_db_data *data) +static void eap_sim_db_expire_pending(struct eap_sim_db_data *data, + struct eap_sim_db_pending *entry) { - /* TODO: add limit for maximum length for pending list; remove latest - * (i.e., last) entry from the list if the limit is reached; could also - * use timeout to expire pending entries */ + eloop_register_timeout(data->eap_sim_db_timeout, 0, + eap_sim_db_query_timeout, data, entry); } @@ -891,7 +949,7 @@ int eap_sim_db_get_gsm_triplets(struct eap_sim_db_data *data, if (entry->state == FAILURE) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> " "failure"); - os_free(entry); + eap_sim_db_free_pending(data, entry); return EAP_SIM_DB_FAILURE; } @@ -911,7 +969,7 @@ int eap_sim_db_get_gsm_triplets(struct eap_sim_db_data *data, os_memcpy(sres, entry->u.sim.sres, num_chal * EAP_SIM_SRES_LEN); os_memcpy(kc, entry->u.sim.kc, num_chal * EAP_SIM_KC_LEN); - os_free(entry); + eap_sim_db_free_pending(data, entry); return num_chal; } @@ -945,7 +1003,8 @@ int eap_sim_db_get_gsm_triplets(struct eap_sim_db_data *data, entry->cb_session_ctx = cb_session_ctx; entry->state = PENDING; eap_sim_db_add_pending(data, entry); - eap_sim_db_expire_pending(data); + eap_sim_db_expire_pending(data, entry); + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added query %p", entry); return EAP_SIM_DB_PENDING; } @@ -1356,7 +1415,7 @@ int eap_sim_db_get_aka_auth(struct eap_sim_db_data *data, const char *username, entry = eap_sim_db_get_pending(data, imsi, 1); if (entry) { if (entry->state == FAILURE) { - os_free(entry); + eap_sim_db_free_pending(data, entry); wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failure"); return EAP_SIM_DB_FAILURE; } @@ -1375,7 +1434,7 @@ int eap_sim_db_get_aka_auth(struct eap_sim_db_data *data, const char *username, os_memcpy(ck, entry->u.aka.ck, EAP_AKA_CK_LEN); os_memcpy(res, entry->u.aka.res, EAP_AKA_RES_MAX_LEN); *res_len = entry->u.aka.res_len; - os_free(entry); + eap_sim_db_free_pending(data, entry); return 0; } @@ -1406,7 +1465,8 @@ int eap_sim_db_get_aka_auth(struct eap_sim_db_data *data, const char *username, entry->cb_session_ctx = cb_session_ctx; entry->state = PENDING; eap_sim_db_add_pending(data, entry); - eap_sim_db_expire_pending(data); + eap_sim_db_expire_pending(data, entry); + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added query %p", entry); return EAP_SIM_DB_PENDING; } diff --git a/contrib/wpa/src/eap_server/eap_sim_db.h b/contrib/wpa/src/eap_server/eap_sim_db.h index 53a1a7c3b4e3..ca900b915852 100644 --- a/contrib/wpa/src/eap_server/eap_sim_db.h +++ b/contrib/wpa/src/eap_server/eap_sim_db.h @@ -31,7 +31,7 @@ enum eap_sim_db_method { struct eap_sim_db_data; struct eap_sim_db_data * -eap_sim_db_init(const char *config, +eap_sim_db_init(const char *config, unsigned int db_timeout, void (*get_complete_cb)(void *ctx, void *session_ctx), void *ctx); diff --git a/contrib/wpa/src/eap_server/ikev2.c b/contrib/wpa/src/eap_server/ikev2.c index 632598fac72a..5385cd89246f 100644 --- a/contrib/wpa/src/eap_server/ikev2.c +++ b/contrib/wpa/src/eap_server/ikev2.c @@ -133,7 +133,7 @@ static int ikev2_parse_transform(struct ikev2_initiator_data *data, t = (const struct ikev2_transform *) pos; transform_len = WPA_GET_BE16(t->transform_length); - if (transform_len < (int) sizeof(*t) || pos + transform_len > end) { + if (transform_len < (int) sizeof(*t) || transform_len > end - pos) { wpa_printf(MSG_INFO, "IKEV2: Invalid transform length %d", transform_len); return -1; @@ -221,7 +221,7 @@ static int ikev2_parse_proposal(struct ikev2_initiator_data *data, p = (const struct ikev2_proposal *) pos; proposal_len = WPA_GET_BE16(p->proposal_length); - if (proposal_len < (int) sizeof(*p) || pos + proposal_len > end) { + if (proposal_len < (int) sizeof(*p) || proposal_len > end - pos) { wpa_printf(MSG_INFO, "IKEV2: Invalid proposal length %d", proposal_len); return -1; @@ -256,7 +256,7 @@ static int ikev2_parse_proposal(struct ikev2_initiator_data *data, ppos = (const u8 *) (p + 1); pend = pos + proposal_len; - if (ppos + p->spi_size > pend) { + if (p->spi_size > pend - ppos) { wpa_printf(MSG_INFO, "IKEV2: Not enough room for SPI " "in proposal"); return -1; diff --git a/contrib/wpa/src/eap_server/tncs.c b/contrib/wpa/src/eap_server/tncs.c index dc6f689c0b5e..cfcbd3ed828c 100644 --- a/contrib/wpa/src/eap_server/tncs.c +++ b/contrib/wpa/src/eap_server/tncs.c @@ -140,7 +140,7 @@ static struct tncs_data * tncs_get_conn(TNC_ConnectionID connectionID) /* TNCS functions that IMVs can call */ -TNC_Result TNC_TNCS_ReportMessageTypes( +static TNC_Result TNC_TNCS_ReportMessageTypes( TNC_IMVID imvID, TNC_MessageTypeList supportedTypes, TNC_UInt32 typeCount) @@ -173,7 +173,7 @@ TNC_Result TNC_TNCS_ReportMessageTypes( } -TNC_Result TNC_TNCS_SendMessage( +static TNC_Result TNC_TNCS_SendMessage( TNC_IMVID imvID, TNC_ConnectionID connectionID, TNC_BufferReference message, @@ -222,7 +222,7 @@ TNC_Result TNC_TNCS_SendMessage( } -TNC_Result TNC_TNCS_RequestHandshakeRetry( +static TNC_Result TNC_TNCS_RequestHandshakeRetry( TNC_IMVID imvID, TNC_ConnectionID connectionID, TNC_RetryReason reason) @@ -233,7 +233,7 @@ TNC_Result TNC_TNCS_RequestHandshakeRetry( } -TNC_Result TNC_TNCS_ProvideRecommendation( +static TNC_Result TNC_TNCS_ProvideRecommendation( TNC_IMVID imvID, TNC_ConnectionID connectionID, TNC_IMV_Action_Recommendation recommendation, @@ -260,7 +260,7 @@ TNC_Result TNC_TNCS_ProvideRecommendation( } -TNC_Result TNC_TNCS_GetAttribute( +static TNC_Result TNC_TNCS_GetAttribute( TNC_IMVID imvID, TNC_ConnectionID connectionID, TNC_AttributeID attribureID, @@ -274,7 +274,7 @@ TNC_Result TNC_TNCS_GetAttribute( } -TNC_Result TNC_TNCS_SetAttribute( +static TNC_Result TNC_TNCS_SetAttribute( TNC_IMVID imvID, TNC_ConnectionID connectionID, TNC_AttributeID attribureID, @@ -287,7 +287,7 @@ TNC_Result TNC_TNCS_SetAttribute( } -TNC_Result TNC_TNCS_BindFunction( +static TNC_Result TNC_TNCS_BindFunction( TNC_IMVID imvID, char *functionName, void **pOutFunctionPointer) diff --git a/contrib/wpa/src/eapol_auth/eapol_auth_sm.c b/contrib/wpa/src/eapol_auth/eapol_auth_sm.c index ff33d286223f..ff673bb2e785 100644 --- a/contrib/wpa/src/eapol_auth/eapol_auth_sm.c +++ b/contrib/wpa/src/eapol_auth/eapol_auth_sm.c @@ -866,10 +866,13 @@ eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, sm->radius_cui = wpabuf_alloc_copy(radius_cui, os_strlen(radius_cui)); - sm->acct_multi_session_id_lo = eapol->acct_multi_session_id_lo++; - if (eapol->acct_multi_session_id_lo == 0) - eapol->acct_multi_session_id_hi++; - sm->acct_multi_session_id_hi = eapol->acct_multi_session_id_hi; +#ifndef CONFIG_NO_RADIUS + if (radius_gen_session_id((u8 *) &sm->acct_multi_session_id, + sizeof(sm->acct_multi_session_id)) < 0) { + eapol_auth_free(sm); + return NULL; + } +#endif /* CONFIG_NO_RADIUS */ return sm; } @@ -884,6 +887,9 @@ void eapol_auth_free(struct eapol_state_machine *sm) eloop_cancel_timeout(eapol_sm_step_cb, sm, NULL); if (sm->eap) eap_server_sm_deinit(sm->eap); + + wpabuf_free(sm->radius_cui); + os_free(sm->identity); os_free(sm); } @@ -1271,7 +1277,6 @@ struct eapol_authenticator * eapol_auth_init(struct eapol_auth_config *conf, struct eapol_auth_cb *cb) { struct eapol_authenticator *eapol; - struct os_time now; eapol = os_zalloc(sizeof(*eapol)); if (eapol == NULL) @@ -1300,12 +1305,6 @@ struct eapol_authenticator * eapol_auth_init(struct eapol_auth_config *conf, eapol->cb.erp_get_key = cb->erp_get_key; eapol->cb.erp_add_key = cb->erp_add_key; - /* Acct-Multi-Session-Id should be unique over reboots. If reliable - * clock is not available, this could be replaced with reboot counter, - * etc. */ - os_get_time(&now); - eapol->acct_multi_session_id_hi = now.sec; - return eapol; } diff --git a/contrib/wpa/src/eapol_auth/eapol_auth_sm_i.h b/contrib/wpa/src/eapol_auth/eapol_auth_sm_i.h index a29b49c90c72..04386b2ce551 100644 --- a/contrib/wpa/src/eapol_auth/eapol_auth_sm_i.h +++ b/contrib/wpa/src/eapol_auth/eapol_auth_sm_i.h @@ -30,9 +30,6 @@ struct eapol_authenticator { u8 *default_wep_key; u8 default_wep_key_idx; - - u32 acct_multi_session_id_hi; - u32 acct_multi_session_id_lo; }; @@ -162,12 +159,6 @@ struct eapol_state_machine { struct radius_class_data radius_class; struct wpabuf *radius_cui; /* Chargeable-User-Identity */ - /* Keys for encrypting and signing EAPOL-Key frames */ - u8 *eapol_key_sign; - size_t eapol_key_sign_len; - u8 *eapol_key_crypt; - size_t eapol_key_crypt_len; - struct eap_sm *eap; Boolean initializing; /* in process of initializing state machines */ @@ -179,8 +170,7 @@ struct eapol_state_machine { int remediation; - u32 acct_multi_session_id_hi; - u32 acct_multi_session_id_lo; + u64 acct_multi_session_id; }; #endif /* EAPOL_AUTH_SM_I_H */ diff --git a/contrib/wpa/src/eapol_supp/eapol_supp_sm.c b/contrib/wpa/src/eapol_supp/eapol_supp_sm.c index 09cf4f6b9222..65460fc3bec0 100644 --- a/contrib/wpa/src/eapol_supp/eapol_supp_sm.c +++ b/contrib/wpa/src/eapol_supp/eapol_supp_sm.c @@ -314,6 +314,16 @@ SM_STATE(SUPP_PAE, RESTART) { SM_ENTRY(SUPP_PAE, RESTART); sm->eapRestart = TRUE; + if (sm->altAccept) { + /* + * Prevent EAP peer state machine from failing due to prior + * external EAP success notification (altSuccess=TRUE in the + * IDLE state could result in a transition to the FAILURE state. + */ + wpa_printf(MSG_DEBUG, "EAPOL: Clearing prior altAccept TRUE"); + sm->eapSuccess = FALSE; + sm->altAccept = FALSE; + } } diff --git a/contrib/wpa/src/fst/fst.c b/contrib/wpa/src/fst/fst.c index 2880870213e6..32cd941b41e2 100644 --- a/contrib/wpa/src/fst/fst.c +++ b/contrib/wpa/src/fst/fst.c @@ -15,6 +15,7 @@ #include "fst/fst_defs.h" #include "fst/fst_ctrl_iface.h" +static int fst_global_initialized = 0; struct dl_list fst_global_ctrls_list; @@ -106,6 +107,7 @@ int fst_global_init(void) dl_list_init(&fst_global_groups_list); dl_list_init(&fst_global_ctrls_list); fst_session_global_init(); + fst_global_initialized = 1; return 0; } @@ -115,6 +117,9 @@ void fst_global_deinit(void) struct fst_group *group; struct fst_ctrl_handle *h; + if (!fst_global_initialized) + return; + fst_session_global_deinit(); while ((group = fst_first_group()) != NULL) fst_group_delete(group); @@ -122,6 +127,7 @@ void fst_global_deinit(void) struct fst_ctrl_handle, global_ctrls_lentry))) fst_global_del_ctrl(h); + fst_global_initialized = 0; } @@ -160,7 +166,7 @@ void fst_global_del_ctrl(struct fst_ctrl_handle *h) void fst_rx_action(struct fst_iface *iface, const struct ieee80211_mgmt *mgmt, size_t len) { - if (fst_iface_is_connected(iface, mgmt->sa)) + if (fst_iface_is_connected(iface, mgmt->sa, FALSE)) fst_session_on_action_rx(iface, mgmt, len); else wpa_printf(MSG_DEBUG, diff --git a/contrib/wpa/src/fst/fst_ctrl_aux.c b/contrib/wpa/src/fst/fst_ctrl_aux.c index dc7b2a7d7202..b6328279b745 100644 --- a/contrib/wpa/src/fst/fst_ctrl_aux.c +++ b/contrib/wpa/src/fst/fst_ctrl_aux.c @@ -14,27 +14,28 @@ static const char *session_event_names[] = { - [EVENT_FST_ESTABLISHED] FST_PVAL_EVT_TYPE_ESTABLISHED, - [EVENT_FST_SETUP] FST_PVAL_EVT_TYPE_SETUP, - [EVENT_FST_SESSION_STATE_CHANGED] FST_PVAL_EVT_TYPE_SESSION_STATE, + [EVENT_FST_ESTABLISHED] = FST_PVAL_EVT_TYPE_ESTABLISHED, + [EVENT_FST_SETUP] = FST_PVAL_EVT_TYPE_SETUP, + [EVENT_FST_SESSION_STATE_CHANGED] = FST_PVAL_EVT_TYPE_SESSION_STATE, }; static const char *reason_names[] = { - [REASON_TEARDOWN] FST_CS_PVAL_REASON_TEARDOWN, - [REASON_SETUP] FST_CS_PVAL_REASON_SETUP, - [REASON_SWITCH] FST_CS_PVAL_REASON_SWITCH, - [REASON_STT] FST_CS_PVAL_REASON_STT, - [REASON_REJECT] FST_CS_PVAL_REASON_REJECT, - [REASON_ERROR_PARAMS] FST_CS_PVAL_REASON_ERROR_PARAMS, - [REASON_RESET] FST_CS_PVAL_REASON_RESET, - [REASON_DETACH_IFACE] FST_CS_PVAL_REASON_DETACH_IFACE, + [REASON_TEARDOWN] = FST_CS_PVAL_REASON_TEARDOWN, + [REASON_SETUP] = FST_CS_PVAL_REASON_SETUP, + [REASON_SWITCH] = FST_CS_PVAL_REASON_SWITCH, + [REASON_STT] = FST_CS_PVAL_REASON_STT, + [REASON_REJECT] = FST_CS_PVAL_REASON_REJECT, + [REASON_ERROR_PARAMS] = FST_CS_PVAL_REASON_ERROR_PARAMS, + [REASON_RESET] = FST_CS_PVAL_REASON_RESET, + [REASON_DETACH_IFACE] = FST_CS_PVAL_REASON_DETACH_IFACE, }; static const char *session_state_names[] = { - [FST_SESSION_STATE_INITIAL] FST_CS_PVAL_STATE_INITIAL, - [FST_SESSION_STATE_SETUP_COMPLETION] FST_CS_PVAL_STATE_SETUP_COMPLETION, - [FST_SESSION_STATE_TRANSITION_DONE] FST_CS_PVAL_STATE_TRANSITION_DONE, - [FST_SESSION_STATE_TRANSITION_CONFIRMED] + [FST_SESSION_STATE_INITIAL] = FST_CS_PVAL_STATE_INITIAL, + [FST_SESSION_STATE_SETUP_COMPLETION] = + FST_CS_PVAL_STATE_SETUP_COMPLETION, + [FST_SESSION_STATE_TRANSITION_DONE] = FST_CS_PVAL_STATE_TRANSITION_DONE, + [FST_SESSION_STATE_TRANSITION_CONFIRMED] = FST_CS_PVAL_STATE_TRANSITION_CONFIRMED, }; diff --git a/contrib/wpa/src/fst/fst_ctrl_iface.c b/contrib/wpa/src/fst/fst_ctrl_iface.c index d0907188a389..7820e586629f 100644 --- a/contrib/wpa/src/fst/fst_ctrl_iface.c +++ b/contrib/wpa/src/fst/fst_ctrl_iface.c @@ -648,9 +648,9 @@ static int list_groups(const char *cmd, char *buf, size_t buflen) static const char * band_freq(enum mb_band_id band) { static const char *band_names[] = { - [MB_BAND_ID_WIFI_2_4GHZ] "2.4GHZ", - [MB_BAND_ID_WIFI_5GHZ] "5GHZ", - [MB_BAND_ID_WIFI_60GHZ] "60GHZ", + [MB_BAND_ID_WIFI_2_4GHZ] = "2.4GHZ", + [MB_BAND_ID_WIFI_5GHZ] = "5GHZ", + [MB_BAND_ID_WIFI_60GHZ] = "60GHZ", }; return fst_get_str_name(band, band_names, ARRAY_SIZE(band_names)); @@ -749,7 +749,7 @@ int fst_ctrl_iface_mb_info(const u8 *addr, char *buf, size_t buflen) foreach_fst_group(g) { foreach_fst_group_iface(g, f) { - if (fst_iface_is_connected(f, addr)) { + if (fst_iface_is_connected(f, addr, TRUE)) { ret += print_band(num++, f, addr, buf + ret, buflen - ret); } diff --git a/contrib/wpa/src/fst/fst_defs.h b/contrib/wpa/src/fst/fst_defs.h index 8ddcc61376b2..5859f6f5ed91 100644 --- a/contrib/wpa/src/fst/fst_defs.h +++ b/contrib/wpa/src/fst/fst_defs.h @@ -34,7 +34,7 @@ enum session_type { struct session_transition_ie { u8 element_id; u8 length; - u32 fsts_id; + le32 fsts_id; u8 session_control; u8 new_band_id; u8 new_band_setup; @@ -47,7 +47,7 @@ struct session_transition_ie { struct fst_setup_req { u8 action; u8 dialog_token; - u32 llt; + le32 llt; struct session_transition_ie stie; /* Multi-band (optional) */ /* Wakeup Schedule (optional) */ @@ -70,18 +70,18 @@ struct fst_setup_res { struct fst_ack_req { u8 action; u8 dialog_token; - u32 fsts_id; + le32 fsts_id; } STRUCT_PACKED; struct fst_ack_res { u8 action; u8 dialog_token; - u32 fsts_id; + le32 fsts_id; } STRUCT_PACKED; struct fst_tear_down { u8 action; - u32 fsts_id; + le32 fsts_id; } STRUCT_PACKED; #endif /* IEEE_80211_FST_DEFS_H */ diff --git a/contrib/wpa/src/fst/fst_group.c b/contrib/wpa/src/fst/fst_group.c index f6c7be9435f4..321d40d50cd2 100644 --- a/contrib/wpa/src/fst/fst_group.c +++ b/contrib/wpa/src/fst/fst_group.c @@ -18,22 +18,6 @@ struct dl_list fst_global_groups_list; -#ifndef HOSTAPD -static Boolean fst_has_fst_peer(struct fst_iface *iface, Boolean *has_peer) -{ - const u8 *bssid; - - bssid = fst_iface_get_bssid(iface); - if (!bssid) { - *has_peer = FALSE; - return FALSE; - } - - *has_peer = TRUE; - return fst_iface_get_peer_mb_ie(iface, bssid) != NULL; -} -#endif /* HOSTAPD */ - static void fst_dump_mb_ies(const char *group_id, const char *ifname, struct wpabuf *mbies) @@ -147,16 +131,6 @@ static struct wpabuf * fst_group_create_mb_ie(struct fst_group *g, struct fst_iface *f; unsigned int nof_mbies = 0; unsigned int nof_ifaces_added = 0; -#ifndef HOSTAPD - Boolean has_peer; - Boolean has_fst_peer; - - foreach_fst_group_iface(g, f) { - has_fst_peer = fst_has_fst_peer(f, &has_peer); - if (has_peer && !has_fst_peer) - return NULL; - } -#endif /* HOSTAPD */ foreach_fst_group_iface(g, f) { if (f == i) @@ -222,43 +196,35 @@ static const u8 * fst_mbie_get_peer_addr(const struct multi_band_ie *mbie) } -static struct fst_iface * -fst_group_get_new_iface_by_mbie_and_band_id(struct fst_group *g, - const u8 *mb_ies_buff, - size_t mb_ies_size, - u8 band_id, - u8 *iface_peer_addr) +static const u8 * fst_mbie_get_peer_addr_for_band(const struct wpabuf *mbies, + u8 band_id) { - while (mb_ies_size >= 2) { + const u8 *p = wpabuf_head(mbies); + size_t s = wpabuf_len(mbies); + + while (s >= 2) { const struct multi_band_ie *mbie = - (const struct multi_band_ie *) mb_ies_buff; + (const struct multi_band_ie *) p; - if (mbie->eid != WLAN_EID_MULTI_BAND || - (size_t) 2 + mbie->len < sizeof(*mbie)) - break; - - if (mbie->band_id == band_id) { - struct fst_iface *iface; - - foreach_fst_group_iface(g, iface) { - const u8 *peer_addr = - fst_mbie_get_peer_addr(mbie); - - if (peer_addr && - fst_iface_is_connected(iface, peer_addr) && - band_id == fst_iface_get_band_id(iface)) { - os_memcpy(iface_peer_addr, peer_addr, - ETH_ALEN); - return iface; - } - } - break; + if (mbie->eid != WLAN_EID_MULTI_BAND) { + fst_printf(MSG_INFO, "unexpected eid %d", mbie->eid); + return NULL; } - mb_ies_buff += 2 + mbie->len; - mb_ies_size -= 2 + mbie->len; + if (mbie->len < sizeof(*mbie) - 2 || mbie->len > s - 2) { + fst_printf(MSG_INFO, "invalid mbie len %d", + mbie->len); + return NULL; + } + + if (mbie->band_id == band_id) + return fst_mbie_get_peer_addr(mbie); + + p += 2 + mbie->len; + s -= 2 + mbie->len; } + fst_printf(MSG_INFO, "mbie doesn't contain band %d", band_id); return NULL; } @@ -295,78 +261,172 @@ u32 fst_group_assign_fsts_id(struct fst_group *g) } -static Boolean -fst_group_does_iface_appear_in_other_mbies(struct fst_group *g, - struct fst_iface *iface, - struct fst_iface *other, - u8 *peer_addr) +/** + * fst_group_get_peer_other_connection_1 - Find peer's "other" connection + * (iface, MAC tuple) by using peer's MB IE on iface. + * + * @iface: iface on which FST Setup Request was received + * @peer_addr: Peer address on iface + * @band_id: "other" connection band id + * @other_peer_addr (out): Peer's MAC address on the "other" connection (on the + * "other" iface) + * + * This function parses peer's MB IE on iface. It looks for peer's MAC address + * on band_id (tmp_peer_addr). Next all interfaces are iterated to find an + * interface which correlates with band_id. If such interface is found, peer + * database is iterated to see if tmp_peer_addr is connected over it. + */ +static struct fst_iface * +fst_group_get_peer_other_connection_1(struct fst_iface *iface, + const u8 *peer_addr, u8 band_id, + u8 *other_peer_addr) { - struct fst_get_peer_ctx *ctx; - const u8 *addr; - const u8 *iface_addr; - enum mb_band_id iface_band_id; + const struct wpabuf *mbies; + struct fst_iface *other_iface; + const u8 *tmp_peer_addr; - WPA_ASSERT(g == fst_iface_get_group(iface)); - WPA_ASSERT(g == fst_iface_get_group(other)); + /* Get peer's MB IEs on iface */ + mbies = fst_iface_get_peer_mb_ie(iface, peer_addr); + if (!mbies) + return NULL; - iface_addr = fst_iface_get_addr(iface); - iface_band_id = fst_iface_get_band_id(iface); + /* Get peer's MAC address on the "other" interface */ + tmp_peer_addr = fst_mbie_get_peer_addr_for_band(mbies, band_id); + if (!tmp_peer_addr) { + fst_printf(MSG_INFO, + "couldn't extract other peer addr from mbies"); + return NULL; + } - addr = fst_iface_get_peer_first(other, &ctx, TRUE); - for (; addr; addr = fst_iface_get_peer_next(other, &ctx, TRUE)) { - const struct wpabuf *mbies; - u8 other_iface_peer_addr[ETH_ALEN]; - struct fst_iface *other_new_iface; + fst_printf(MSG_DEBUG, "found other peer addr from mbies: " MACSTR, + MAC2STR(tmp_peer_addr)); - mbies = fst_iface_get_peer_mb_ie(other, addr); - if (!mbies) + foreach_fst_group_iface(fst_iface_get_group(iface), other_iface) { + if (other_iface == iface || + band_id != fst_iface_get_band_id(other_iface)) continue; - - other_new_iface = fst_group_get_new_iface_by_mbie_and_band_id( - g, wpabuf_head(mbies), wpabuf_len(mbies), - iface_band_id, other_iface_peer_addr); - if (other_new_iface == iface && - os_memcmp(iface_addr, other_iface_peer_addr, - ETH_ALEN) != 0) { - os_memcpy(peer_addr, addr, ETH_ALEN); - return TRUE; + if (fst_iface_is_connected(other_iface, tmp_peer_addr, FALSE)) { + os_memcpy(other_peer_addr, tmp_peer_addr, ETH_ALEN); + return other_iface; } } - return FALSE; -} - - -struct fst_iface * -fst_group_find_new_iface_by_stie(struct fst_group *g, - struct fst_iface *iface, - const u8 *peer_addr, - const struct session_transition_ie *stie, - u8 *iface_peer_addr) -{ - struct fst_iface *i; - - foreach_fst_group_iface(g, i) { - if (i == iface || - stie->new_band_id != fst_iface_get_band_id(i)) - continue; - if (fst_group_does_iface_appear_in_other_mbies(g, iface, i, - iface_peer_addr)) - return i; - break; - } return NULL; } -struct fst_iface * -fst_group_get_new_iface_by_stie_and_mbie( - struct fst_group *g, const u8 *mb_ies_buff, size_t mb_ies_size, - const struct session_transition_ie *stie, u8 *iface_peer_addr) +/** + * fst_group_get_peer_other_connection_2 - Find peer's "other" connection + * (iface, MAC tuple) by using MB IEs of other peers. + * + * @iface: iface on which FST Setup Request was received + * @peer_addr: Peer address on iface + * @band_id: "other" connection band id + * @other_peer_addr (out): Peer's MAC address on the "other" connection (on the + * "other" iface) + * + * This function iterates all connection (other_iface, cur_peer_addr tuples). + * For each connection, MB IE (of cur_peer_addr on other_iface) is parsed and + * MAC address on iface's band_id is extracted (this_peer_addr). + * this_peer_addr is then compared to peer_addr. A match indicates we have + * found the "other" connection. + */ +static struct fst_iface * +fst_group_get_peer_other_connection_2(struct fst_iface *iface, + const u8 *peer_addr, u8 band_id, + u8 *other_peer_addr) { - return fst_group_get_new_iface_by_mbie_and_band_id( - g, mb_ies_buff, mb_ies_size, stie->new_band_id, - iface_peer_addr); + u8 this_band_id = fst_iface_get_band_id(iface); + const u8 *cur_peer_addr, *this_peer_addr; + struct fst_get_peer_ctx *ctx; + struct fst_iface *other_iface; + const struct wpabuf *cur_mbie; + + foreach_fst_group_iface(fst_iface_get_group(iface), other_iface) { + if (other_iface == iface || + band_id != fst_iface_get_band_id(other_iface)) + continue; + cur_peer_addr = fst_iface_get_peer_first(other_iface, &ctx, + TRUE); + for (; cur_peer_addr; + cur_peer_addr = fst_iface_get_peer_next(other_iface, &ctx, + TRUE)) { + cur_mbie = fst_iface_get_peer_mb_ie(other_iface, + cur_peer_addr); + if (!cur_mbie) + continue; + this_peer_addr = fst_mbie_get_peer_addr_for_band( + cur_mbie, this_band_id); + if (!this_peer_addr) + continue; + if (os_memcmp(this_peer_addr, peer_addr, ETH_ALEN) == + 0) { + os_memcpy(other_peer_addr, cur_peer_addr, + ETH_ALEN); + return other_iface; + } + } + } + + return NULL; +} + + +/** + * fst_group_get_peer_other_connection - Find peer's "other" connection (iface, + * MAC tuple). + * + * @iface: iface on which FST Setup Request was received + * @peer_addr: Peer address on iface + * @band_id: "other" connection band id + * @other_peer_addr (out): Peer's MAC address on the "other" connection (on the + * "other" iface) + * + * This function is called upon receiving FST Setup Request from some peer who + * has peer_addr on iface. It searches for another connection of the same peer + * on different interface which correlates with band_id. MB IEs received from + * peer (on the two different interfaces) are used to identify same peer. + */ +struct fst_iface * +fst_group_get_peer_other_connection(struct fst_iface *iface, + const u8 *peer_addr, u8 band_id, + u8 *other_peer_addr) +{ + struct fst_iface *other_iface; + + fst_printf(MSG_DEBUG, "%s: %s:" MACSTR ", %d", __func__, + fst_iface_get_name(iface), MAC2STR(peer_addr), band_id); + + /* + * Two search methods are used: + * 1. Use peer's MB IE on iface to extract peer's MAC address on + * "other" connection. Then check if such "other" connection exists. + * 2. Iterate peer database, examine each MB IE to see if it points to + * (iface, peer_addr) tuple + */ + + other_iface = fst_group_get_peer_other_connection_1(iface, peer_addr, + band_id, + other_peer_addr); + if (other_iface) { + fst_printf(MSG_DEBUG, "found by method #1. %s:" MACSTR, + fst_iface_get_name(other_iface), + MAC2STR(other_peer_addr)); + return other_iface; + } + + other_iface = fst_group_get_peer_other_connection_2(iface, peer_addr, + band_id, + other_peer_addr); + if (other_iface) { + fst_printf(MSG_DEBUG, "found by method #2. %s:" MACSTR, + fst_iface_get_name(other_iface), + MAC2STR(other_peer_addr)); + return other_iface; + } + + fst_printf(MSG_INFO, "%s: other connection not found", __func__); + return NULL; } diff --git a/contrib/wpa/src/fst/fst_group.h b/contrib/wpa/src/fst/fst_group.h index 3a87c0bc91c9..00aee9c8c25c 100644 --- a/contrib/wpa/src/fst/fst_group.h +++ b/contrib/wpa/src/fst/fst_group.h @@ -48,15 +48,9 @@ Boolean fst_group_delete_if_empty(struct fst_group *group); struct fst_iface * fst_group_get_iface_by_name(struct fst_group *g, const char *ifname); struct fst_iface * -fst_group_find_new_iface_by_stie(struct fst_group *g, - struct fst_iface *iface, - const u8 *peer_addr, - const struct session_transition_ie *stie, - u8 *iface_peer_addr); -struct fst_iface * -fst_group_get_new_iface_by_stie_and_mbie( - struct fst_group *g, const u8 *mb_ies_buff, size_t mb_ies_size, - const struct session_transition_ie *stie, u8 *iface_peer_addr); +fst_group_get_peer_other_connection(struct fst_iface *iface, + const u8 *peer_addr, u8 band_id, + u8 *other_peer_addr); u8 fst_group_assign_dialog_token(struct fst_group *g); u32 fst_group_assign_fsts_id(struct fst_group *g); diff --git a/contrib/wpa/src/fst/fst_iface.c b/contrib/wpa/src/fst/fst_iface.c index 5a92d2c33e42..35e83cb7b471 100644 --- a/contrib/wpa/src/fst/fst_iface.c +++ b/contrib/wpa/src/fst/fst_iface.c @@ -49,12 +49,13 @@ void fst_iface_delete(struct fst_iface *i) } -Boolean fst_iface_is_connected(struct fst_iface *iface, const u8 *addr) +Boolean fst_iface_is_connected(struct fst_iface *iface, const u8 *addr, + Boolean mb_only) { struct fst_get_peer_ctx *ctx; - const u8 *a = fst_iface_get_peer_first(iface, &ctx, TRUE); + const u8 *a = fst_iface_get_peer_first(iface, &ctx, mb_only); - for (; a != NULL; a = fst_iface_get_peer_next(iface, &ctx, TRUE)) + for (; a != NULL; a = fst_iface_get_peer_next(iface, &ctx, mb_only)) if (os_memcmp(addr, a, ETH_ALEN) == 0) return TRUE; diff --git a/contrib/wpa/src/fst/fst_iface.h b/contrib/wpa/src/fst/fst_iface.h index 4670d894f7cb..0eb27325a2b8 100644 --- a/contrib/wpa/src/fst/fst_iface.h +++ b/contrib/wpa/src/fst/fst_iface.h @@ -123,7 +123,8 @@ static inline const u8 * fst_iface_get_peer_next(struct fst_iface *i, return i->iface_obj.get_peer_next(i->iface_obj.ctx, ctx, mb_only); } -Boolean fst_iface_is_connected(struct fst_iface *iface, const u8 *addr); +Boolean fst_iface_is_connected(struct fst_iface *iface, const u8 *addr, + Boolean mb_only); void fst_iface_attach_mbie(struct fst_iface *i, struct wpabuf *mbie); enum mb_band_id fst_iface_get_band_id(struct fst_iface *i); diff --git a/contrib/wpa/src/fst/fst_session.c b/contrib/wpa/src/fst/fst_session.c index 55fa69495e99..76e2c78f4ff6 100644 --- a/contrib/wpa/src/fst/fst_session.c +++ b/contrib/wpa/src/fst/fst_session.c @@ -44,7 +44,7 @@ #define FST_LLT_MS_DEFAULT 50 #define FST_ACTION_MAX_SUPPORTED FST_ACTION_ON_CHANNEL_TUNNEL -const char * const fst_action_names[] = { +static const char * const fst_action_names[] = { [FST_ACTION_SETUP_REQUEST] = "Setup Request", [FST_ACTION_SETUP_RESPONSE] = "Setup Response", [FST_ACTION_TEAR_DOWN] = "Tear Down", @@ -181,7 +181,8 @@ static void fst_session_timeout_handler(void *eloop_data, void *user_ctx) static void fst_session_stt_arm(struct fst_session *s) { - eloop_register_timeout(0, TU_TO_US(FST_DEFAULT_SESSION_TIMEOUT_TU), + /* Action frames sometimes get delayed. Use relaxed timeout (2*) */ + eloop_register_timeout(0, 2 * TU_TO_US(FST_DEFAULT_SESSION_TIMEOUT_TU), fst_session_timeout_handler, NULL, s); s->stt_armed = TRUE; } @@ -363,7 +364,6 @@ static void fst_session_handle_setup_request(struct fst_iface *iface, struct fst_iface *new_iface = NULL; struct fst_group *g; u8 new_iface_peer_addr[ETH_ALEN]; - const struct wpabuf *peer_mbies; size_t plen; if (frame_len < IEEE80211_HDRLEN + 1 + sizeof(*req)) { @@ -399,36 +399,18 @@ static void fst_session_handle_setup_request(struct fst_iface *iface, MAC2STR(mgmt->sa)); } - peer_mbies = fst_iface_get_peer_mb_ie(iface, mgmt->sa); - if (peer_mbies) { - new_iface = fst_group_get_new_iface_by_stie_and_mbie( - g, wpabuf_head(peer_mbies), wpabuf_len(peer_mbies), - &req->stie, new_iface_peer_addr); - if (new_iface) - fst_printf_iface(iface, MSG_INFO, - "FST Request: new iface (%s:" MACSTR - ") found by MB IEs", - fst_iface_get_name(new_iface), - MAC2STR(new_iface_peer_addr)); - } - - if (!new_iface) { - new_iface = fst_group_find_new_iface_by_stie( - g, iface, mgmt->sa, &req->stie, - new_iface_peer_addr); - if (new_iface) - fst_printf_iface(iface, MSG_INFO, - "FST Request: new iface (%s:" MACSTR - ") found by others", - fst_iface_get_name(new_iface), - MAC2STR(new_iface_peer_addr)); - } - + new_iface = fst_group_get_peer_other_connection(iface, mgmt->sa, + req->stie.new_band_id, + new_iface_peer_addr); if (!new_iface) { fst_printf_iface(iface, MSG_WARNING, "FST Request dropped: new iface not found"); return; } + fst_printf_iface(iface, MSG_INFO, + "FST Request: new iface (%s:" MACSTR ") found", + fst_iface_get_name(new_iface), + MAC2STR(new_iface_peer_addr)); s = fst_find_session_in_progress(mgmt->sa, g); if (s) { @@ -447,7 +429,9 @@ static void fst_session_handle_setup_request(struct fst_iface *iface, * the initiator’s MAC address, in which case, the responder * shall delete the received FST Setup Request. */ - if (os_memcmp(mgmt->da, mgmt->sa, ETH_ALEN) > 0) { + if (fst_session_is_ready_pending(s) && + /* waiting for Setup Response */ + os_memcmp(mgmt->da, mgmt->sa, ETH_ALEN) > 0) { fst_printf_session(s, MSG_WARNING, "FST Request dropped due to MAC comparison (our MAC is " MACSTR ")", @@ -455,23 +439,26 @@ static void fst_session_handle_setup_request(struct fst_iface *iface, return; } - if (!fst_session_is_ready_pending(s)) { - fst_printf_session(s, MSG_WARNING, - "FST Request from " MACSTR - " dropped due to inappropriate state %s", - MAC2STR(mgmt->da), - fst_session_state_name(s->state)); - return; - } + /* + * State is SETUP_COMPLETION (either in transition or not) or + * TRANSITION_DONE (in transition). + * Setup Request arriving in this state could mean: + * 1. peer sent it before receiving our Setup Request (race + * condition) + * 2. peer didn't receive our Setup Response. Peer is retrying + * after STT timeout + * 3. peer's FST state machines are out of sync due to some + * other reason + * + * We will reset our session and create a new one instead. + */ + fst_printf_session(s, MSG_WARNING, + "resetting due to FST request"); /* * If FST Setup Request arrived with the same FSTS ID as one we - * initialized before, it means the other side either didn't - * receive our FST Request or skipped it for some reason (for - * example, due to numerical MAC comparison). - * - * In this case, there's no need to tear down the session. + * initialized before, there's no need to tear down the session. * Moreover, as FSTS ID is the same, the other side will * associate this tear down with the session it initiated that * will break the sync. @@ -483,7 +470,6 @@ static void fst_session_handle_setup_request(struct fst_iface *iface, "Skipping TearDown as the FST request has the same FSTS ID as initiated"); fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); fst_session_stt_disarm(s); - fst_printf_session(s, MSG_WARNING, "reset due to FST request"); } s = fst_session_create(g); @@ -521,7 +507,9 @@ static void fst_session_handle_setup_response(struct fst_session *s, enum hostapd_hw_mode hw_mode; u8 channel; union fst_session_state_switch_extra evext = { - .to_initial = {0}, + .to_initial = { + .reject_code = 0, + }, }; if (iface != s->data.old_iface) { @@ -863,13 +851,15 @@ int fst_session_initiate_setup(struct fst_session *s) return -EINVAL; } - if (!fst_iface_is_connected(s->data.old_iface, s->data.old_peer_addr)) { + if (!fst_iface_is_connected(s->data.old_iface, s->data.old_peer_addr, + FALSE)) { fst_printf_session(s, MSG_ERROR, "The preset old peer address is not connected"); return -EINVAL; } - if (!fst_iface_is_connected(s->data.new_iface, s->data.new_peer_addr)) { + if (!fst_iface_is_connected(s->data.new_iface, s->data.new_peer_addr, + FALSE)) { fst_printf_session(s, MSG_ERROR, "The preset new peer address is not connected"); return -EINVAL; @@ -966,7 +956,8 @@ int fst_session_respond(struct fst_session *s, u8 status_code) return -EINVAL; } - if (!fst_iface_is_connected(s->data.old_iface, s->data.old_peer_addr)) { + if (!fst_iface_is_connected(s->data.old_iface, + s->data.old_peer_addr, FALSE)) { fst_printf_session(s, MSG_ERROR, "The preset peer address is not in the peer list"); return -EINVAL; @@ -984,7 +975,7 @@ int fst_session_respond(struct fst_session *s, u8 status_code) res.stie.length = sizeof(res.stie) - 2; if (status_code == WLAN_STATUS_SUCCESS) { - res.stie.fsts_id = s->data.fsts_id; + res.stie.fsts_id = host_to_le32(s->data.fsts_id); res.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0); fst_iface_get_channel_info(s->data.new_iface, &hw_mode, @@ -1458,7 +1449,7 @@ int fst_test_req_send_fst_response(const char *params) res.stie.length = sizeof(res.stie) - 2; if (res.status_code == WLAN_STATUS_SUCCESS) { - res.stie.fsts_id = fsts_id; + res.stie.fsts_id = host_to_le32(fsts_id); res.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0); fst_iface_get_channel_info(s.data.new_iface, &hw_mode, @@ -1507,7 +1498,7 @@ int fst_test_req_send_ack_request(const char *params) os_memset(&req, 0, sizeof(req)); req.action = FST_ACTION_ACK_REQUEST; req.dialog_token = g->dialog_token; - req.fsts_id = fsts_id; + req.fsts_id = host_to_le32(fsts_id); return fst_session_send_action(&s, FALSE, &req, sizeof(req), NULL); } @@ -1535,7 +1526,7 @@ int fst_test_req_send_ack_response(const char *params) os_memset(&res, 0, sizeof(res)); res.action = FST_ACTION_ACK_RESPONSE; res.dialog_token = g->dialog_token; - res.fsts_id = fsts_id; + res.fsts_id = host_to_le32(fsts_id); return fst_session_send_action(&s, FALSE, &res, sizeof(res), NULL); } @@ -1562,7 +1553,7 @@ int fst_test_req_send_tear_down(const char *params) os_memset(&td, 0, sizeof(td)); td.action = FST_ACTION_TEAR_DOWN; - td.fsts_id = fsts_id; + td.fsts_id = host_to_le32(fsts_id); return fst_session_send_action(&s, TRUE, &td, sizeof(td), NULL); } diff --git a/contrib/wpa/src/p2p/p2p.c b/contrib/wpa/src/p2p/p2p.c index 767706c01d6b..996b4e824986 100644 --- a/contrib/wpa/src/p2p/p2p.c +++ b/contrib/wpa/src/p2p/p2p.c @@ -10,6 +10,7 @@ #include "common.h" #include "eloop.h" +#include "common/defs.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "common/wpa_ctrl.h" @@ -445,8 +446,9 @@ static struct p2p_device * p2p_create_device(struct p2p_data *p2p, static void p2p_copy_client_info(struct p2p_device *dev, struct p2p_client_info *cli) { - os_memcpy(dev->info.device_name, cli->dev_name, cli->dev_name_len); - dev->info.device_name[cli->dev_name_len] = '\0'; + p2p_copy_filter_devname(dev->info.device_name, + sizeof(dev->info.device_name), + cli->dev_name, cli->dev_name_len); dev->info.dev_capab = cli->dev_capab; dev->info.config_methods = cli->config_methods; os_memcpy(dev->info.pri_dev_type, cli->pri_dev_type, 8); @@ -636,11 +638,11 @@ static void p2p_update_peer_vendor_elems(struct p2p_device *dev, const u8 *ies, end = ies + ies_len; - for (pos = ies; pos + 1 < end; pos += len) { + for (pos = ies; end - pos > 1; pos += len) { id = *pos++; len = *pos++; - if (pos + len > end) + if (len > end - pos) break; if (id != WLAN_EID_VENDOR_SPECIFIC || len < 3) @@ -786,11 +788,11 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, dev->oper_ssid_len = msg.ssid[1]; } - if (msg.adv_service_instance && msg.adv_service_instance_len) { - wpabuf_free(dev->info.p2ps_instance); + wpabuf_free(dev->info.p2ps_instance); + dev->info.p2ps_instance = NULL; + if (msg.adv_service_instance && msg.adv_service_instance_len) dev->info.p2ps_instance = wpabuf_alloc_copy( msg.adv_service_instance, msg.adv_service_instance_len); - } if (freq >= 2412 && freq <= 2484 && msg.ds_params && *msg.ds_params >= 1 && *msg.ds_params <= 14) { @@ -1220,9 +1222,14 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING; p2p_clear_timeout(p2p); + if (p2p->pending_listen_freq) { + p2p_dbg(p2p, "Clear pending_listen_freq for p2p_find"); + p2p->pending_listen_freq = 0; + } p2p->cfg->stop_listen(p2p->cfg->cb_ctx); p2p->find_type = type; p2p_device_clear_reported(p2p); + os_memset(p2p->sd_query_no_ack, 0, ETH_ALEN); p2p_set_state(p2p, P2P_SEARCH); p2p->search_delay = search_delay; p2p->in_search_delay = 0; @@ -1459,7 +1466,7 @@ static void p2p_prepare_channel_best(struct p2p_data *p2p) /** - * p2p_prepare_channel - Select operating channel for GO Negotiation + * p2p_prepare_channel - Select operating channel for GO Negotiation or P2PS PD * @p2p: P2P module context from p2p_init() * @dev: Selected peer device * @force_freq: Forced frequency in MHz or 0 if not forced @@ -1468,9 +1475,9 @@ static void p2p_prepare_channel_best(struct p2p_data *p2p) * Returns: 0 on success, -1 on failure (channel not supported for P2P) * * This function is used to do initial operating channel selection for GO - * Negotiation prior to having received peer information. The selected channel - * may be further optimized in p2p_reselect_channel() once the peer information - * is available. + * Negotiation prior to having received peer information or for P2PS PD + * signalling. The selected channel may be further optimized in + * p2p_reselect_channel() once the peer information is available. */ int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev, unsigned int force_freq, unsigned int pref_freq, int go) @@ -2028,8 +2035,23 @@ static void p2p_add_dev_from_probe_req(struct p2p_data *p2p, const u8 *addr, dev = p2p_get_device(p2p, addr); if (dev) { - if (dev->country[0] == 0 && msg.listen_channel) - os_memcpy(dev->country, msg.listen_channel, 3); + if (msg.listen_channel) { + int freq; + + if (dev->country[0] == 0) + os_memcpy(dev->country, msg.listen_channel, 3); + + freq = p2p_channel_to_freq(msg.listen_channel[3], + msg.listen_channel[4]); + + if (freq > 0 && dev->listen_freq != freq) { + p2p_dbg(p2p, + "Updated peer " MACSTR " Listen channel (Probe Request): %d -> %d MHz", + MAC2STR(addr), dev->listen_freq, freq); + dev->listen_freq = freq; + } + } + os_get_reltime(&dev->last_seen); p2p_parse_free(&msg); return; /* already known */ @@ -2212,6 +2234,58 @@ struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p, return buf; } +static int p2p_build_probe_resp_buf(struct p2p_data *p2p, struct wpabuf *buf, + struct wpabuf *ies, + const u8 *addr, int rx_freq) +{ + struct ieee80211_mgmt *resp; + u8 channel, op_class; + + resp = wpabuf_put(buf, offsetof(struct ieee80211_mgmt, + u.probe_resp.variable)); + + resp->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) | + (WLAN_FC_STYPE_PROBE_RESP << 4)); + os_memcpy(resp->da, addr, ETH_ALEN); + os_memcpy(resp->sa, p2p->cfg->dev_addr, ETH_ALEN); + os_memcpy(resp->bssid, p2p->cfg->dev_addr, ETH_ALEN); + resp->u.probe_resp.beacon_int = host_to_le16(100); + /* hardware or low-level driver will setup seq_ctrl and timestamp */ + resp->u.probe_resp.capab_info = + host_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE | + WLAN_CAPABILITY_PRIVACY | + WLAN_CAPABILITY_SHORT_SLOT_TIME); + + wpabuf_put_u8(buf, WLAN_EID_SSID); + wpabuf_put_u8(buf, P2P_WILDCARD_SSID_LEN); + wpabuf_put_data(buf, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN); + + wpabuf_put_u8(buf, WLAN_EID_SUPP_RATES); + wpabuf_put_u8(buf, 8); + wpabuf_put_u8(buf, (60 / 5) | 0x80); + wpabuf_put_u8(buf, 90 / 5); + wpabuf_put_u8(buf, (120 / 5) | 0x80); + wpabuf_put_u8(buf, 180 / 5); + wpabuf_put_u8(buf, (240 / 5) | 0x80); + wpabuf_put_u8(buf, 360 / 5); + wpabuf_put_u8(buf, 480 / 5); + wpabuf_put_u8(buf, 540 / 5); + + if (!rx_freq) { + channel = p2p->cfg->channel; + } else if (p2p_freq_to_channel(rx_freq, &op_class, &channel)) { + p2p_err(p2p, "Failed to convert freq to channel"); + return -1; + } + + wpabuf_put_u8(buf, WLAN_EID_DS_PARAMS); + wpabuf_put_u8(buf, 1); + wpabuf_put_u8(buf, channel); + + wpabuf_put_buf(buf, ies); + + return 0; +} static int p2p_service_find_asp(struct p2p_data *p2p, const u8 *hash) { @@ -2245,10 +2319,8 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, { struct ieee802_11_elems elems; struct wpabuf *buf; - struct ieee80211_mgmt *resp; struct p2p_message msg; struct wpabuf *ies; - u8 channel, op_class; if (ieee802_11_parse_elems((u8 *) ie, ie_len, &elems, 0) == ParseFailed) { @@ -2392,49 +2464,12 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, return P2P_PREQ_NOT_PROCESSED; } - resp = wpabuf_put(buf, offsetof(struct ieee80211_mgmt, - u.probe_resp.variable)); - - resp->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) | - (WLAN_FC_STYPE_PROBE_RESP << 4)); - os_memcpy(resp->da, addr, ETH_ALEN); - os_memcpy(resp->sa, p2p->cfg->dev_addr, ETH_ALEN); - os_memcpy(resp->bssid, p2p->cfg->dev_addr, ETH_ALEN); - resp->u.probe_resp.beacon_int = host_to_le16(100); - /* hardware or low-level driver will setup seq_ctrl and timestamp */ - resp->u.probe_resp.capab_info = - host_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE | - WLAN_CAPABILITY_PRIVACY | - WLAN_CAPABILITY_SHORT_SLOT_TIME); - - wpabuf_put_u8(buf, WLAN_EID_SSID); - wpabuf_put_u8(buf, P2P_WILDCARD_SSID_LEN); - wpabuf_put_data(buf, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN); - - wpabuf_put_u8(buf, WLAN_EID_SUPP_RATES); - wpabuf_put_u8(buf, 8); - wpabuf_put_u8(buf, (60 / 5) | 0x80); - wpabuf_put_u8(buf, 90 / 5); - wpabuf_put_u8(buf, (120 / 5) | 0x80); - wpabuf_put_u8(buf, 180 / 5); - wpabuf_put_u8(buf, (240 / 5) | 0x80); - wpabuf_put_u8(buf, 360 / 5); - wpabuf_put_u8(buf, 480 / 5); - wpabuf_put_u8(buf, 540 / 5); - - if (!rx_freq) { - channel = p2p->cfg->channel; - } else if (p2p_freq_to_channel(rx_freq, &op_class, &channel)) { + if (p2p_build_probe_resp_buf(p2p, buf, ies, addr, rx_freq)) { wpabuf_free(ies); wpabuf_free(buf); return P2P_PREQ_NOT_PROCESSED; } - wpabuf_put_u8(buf, WLAN_EID_DS_PARAMS); - wpabuf_put_u8(buf, 1); - wpabuf_put_u8(buf, channel); - - wpabuf_put_buf(buf, ies); wpabuf_free(ies); p2p->cfg->send_probe_resp(p2p->cfg->cb_ctx, buf, rx_freq); @@ -2448,12 +2483,18 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, enum p2p_probe_req_status p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, const u8 *bssid, const u8 *ie, size_t ie_len, - unsigned int rx_freq) + unsigned int rx_freq, int p2p_lo_started) { enum p2p_probe_req_status res; p2p_add_dev_from_probe_req(p2p, addr, ie, ie_len); + if (p2p_lo_started) { + p2p_dbg(p2p, + "Probe Response is offloaded, do not reply Probe Request"); + return P2P_PREQ_PROCESSED; + } + res = p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len, rx_freq); if (res != P2P_PREQ_PROCESSED && res != P2P_PREQ_NOT_PROCESSED) return res; @@ -2944,7 +2985,6 @@ void p2p_deinit(struct p2p_data *p2p) wpabuf_free(p2p->wfd_coupled_sink_info); #endif /* CONFIG_WIFI_DISPLAY */ - eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL); eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); eloop_cancel_timeout(p2p_go_neg_start, p2p, NULL); eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL); @@ -2959,7 +2999,6 @@ void p2p_deinit(struct p2p_data *p2p) os_free(p2p->groups); p2ps_prov_free(p2p); wpabuf_free(p2p->sd_resp); - os_free(p2p->after_scan_tx); p2p_remove_wps_vendor_extensions(p2p); os_free(p2p->no_go_freq.range); p2p_service_flush_asp(p2p); @@ -2971,6 +3010,8 @@ void p2p_deinit(struct p2p_data *p2p) void p2p_flush(struct p2p_data *p2p) { struct p2p_device *dev, *prev; + + p2p_ext_listen(p2p, 0, 0); p2p_stop_find(p2p); dl_list_for_each_safe(dev, prev, &p2p->devices, struct p2p_device, list) { @@ -3157,13 +3198,18 @@ int p2p_set_country(struct p2p_data *p2p, const char *country) static int p2p_pre_find_operation(struct p2p_data *p2p, struct p2p_device *dev) { + int res; + if (dev->sd_pending_bcast_queries == 0) { /* Initialize with total number of registered broadcast * SD queries. */ dev->sd_pending_bcast_queries = p2p->num_p2p_sd_queries; } - if (p2p_start_sd(p2p, dev) == 0) + res = p2p_start_sd(p2p, dev); + if (res == -2) + return -2; + if (res == 0) return 1; if (dev->req_config_methods && @@ -3183,7 +3229,7 @@ static int p2p_pre_find_operation(struct p2p_data *p2p, struct p2p_device *dev) void p2p_continue_find(struct p2p_data *p2p) { struct p2p_device *dev; - int found; + int found, res; p2p_set_state(p2p, P2P_SEARCH); @@ -3196,10 +3242,13 @@ void p2p_continue_find(struct p2p_data *p2p) } if (!found) continue; - if (p2p_pre_find_operation(p2p, dev) > 0) { + res = p2p_pre_find_operation(p2p, dev); + if (res > 0) { p2p->last_p2p_find_oper = dev; return; } + if (res == -2) + goto skip_sd; } /* @@ -3207,14 +3256,19 @@ void p2p_continue_find(struct p2p_data *p2p) * iteration device. */ dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { - if (p2p_pre_find_operation(p2p, dev) > 0) { + res = p2p_pre_find_operation(p2p, dev); + if (res > 0) { p2p->last_p2p_find_oper = dev; return; } + if (res == -2) + goto skip_sd; if (dev == p2p->last_p2p_find_oper) break; } +skip_sd: + os_memset(p2p->sd_query_no_ack, 0, ETH_ALEN); p2p_listen_in_find(p2p, 1); } @@ -3226,8 +3280,17 @@ static void p2p_sd_cb(struct p2p_data *p2p, int success) p2p->pending_action_state = P2P_NO_PENDING_ACTION; if (!success) { - if (p2p->sd_peer) + if (p2p->sd_peer) { + if (is_zero_ether_addr(p2p->sd_query_no_ack)) { + os_memcpy(p2p->sd_query_no_ack, + p2p->sd_peer->info.p2p_device_addr, + ETH_ALEN); + p2p_dbg(p2p, + "First SD Query no-ACK in this search iteration: " + MACSTR, MAC2STR(p2p->sd_query_no_ack)); + } p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + } p2p->sd_peer = NULL; if (p2p->state != P2P_IDLE) p2p_continue_find(p2p); @@ -3325,6 +3388,43 @@ static void p2p_prov_disc_cb(struct p2p_data *p2p, int success) return; } + /* + * If after PD Request the peer doesn't expect to receive PD Response + * the PD Request ACK indicates a completion of the current PD. This + * happens only on the advertiser side sending the follow-on PD Request + * with the status different than 12 (Success: accepted by user). + */ + if (p2p->p2ps_prov && !p2p->p2ps_prov->pd_seeker && + p2p->p2ps_prov->status != P2P_SC_SUCCESS_DEFERRED) { + p2p_dbg(p2p, "P2PS PD completion on Follow-on PD Request ACK"); + + if (p2p->send_action_in_progress) { + p2p->send_action_in_progress = 0; + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + } + + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + + if (p2p->cfg->p2ps_prov_complete) { + p2p->cfg->p2ps_prov_complete( + p2p->cfg->cb_ctx, + p2p->p2ps_prov->status, + p2p->p2ps_prov->adv_mac, + p2p->p2ps_prov->adv_mac, + p2p->p2ps_prov->session_mac, + NULL, p2p->p2ps_prov->adv_id, + p2p->p2ps_prov->session_id, + 0, 0, NULL, 0, 0, 0, + NULL, NULL, 0, 0, NULL, 0); + } + + if (p2p->user_initiated_pd) + p2p_reset_pending_pd(p2p); + + p2ps_prov_free(p2p); + return; + } + /* * This postponing, of resetting pending_action_state, needs to be * done only for user initiated PD requests and not internal ones. @@ -3399,9 +3499,11 @@ int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq, * operation was started. */ p2p_dbg(p2p, "Ignore old scan result for " MACSTR - " (rx_time=%u.%06u)", + " (rx_time=%u.%06u find_start=%u.%06u)", MAC2STR(bssid), (unsigned int) rx_time->sec, - (unsigned int) rx_time->usec); + (unsigned int) rx_time->usec, + (unsigned int) p2p->find_start.sec, + (unsigned int) p2p->find_start.usec); return 0; } @@ -3426,7 +3528,8 @@ void p2p_scan_res_handled(struct p2p_data *p2p) } -void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id) +void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id, + unsigned int bands) { u8 dev_capab; u8 *len; @@ -3460,6 +3563,9 @@ void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id) p2p_buf_add_ext_listen_timing(ies, p2p->ext_listen_period, p2p->ext_listen_interval); + if (bands & BAND_60_GHZ) + p2p_buf_add_device_info(ies, p2p, NULL); + if (p2p->p2ps_seek && p2p->p2ps_seek_count) p2p_buf_add_service_hash(ies, p2p); @@ -3694,6 +3800,8 @@ void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst, break; case P2P_PENDING_INVITATION_RESPONSE: p2p_invitation_resp_cb(p2p, success); + if (p2p->inv_status != P2P_SC_SUCCESS) + p2p_check_after_scan_tx_continuation(p2p); break; case P2P_PENDING_DEV_DISC_REQUEST: p2p_dev_disc_req_cb(p2p, success); @@ -5400,3 +5508,34 @@ void p2p_set_own_pref_freq_list(struct p2p_data *p2p, i, p2p->pref_freq_list[i]); } } + + +struct wpabuf * p2p_build_probe_resp_template(struct p2p_data *p2p, + unsigned int freq) +{ + struct wpabuf *ies, *buf; + u8 addr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + int ret; + + ies = p2p_build_probe_resp_ies(p2p, NULL, 0); + if (!ies) { + wpa_printf(MSG_ERROR, + "CTRL: Failed to build Probe Response IEs"); + return NULL; + } + + buf = wpabuf_alloc(200 + wpabuf_len(ies)); + if (!buf) { + wpabuf_free(ies); + return NULL; + } + + ret = p2p_build_probe_resp_buf(p2p, buf, ies, addr, freq); + wpabuf_free(ies); + if (ret) { + wpabuf_free(buf); + return NULL; + } + + return buf; +} diff --git a/contrib/wpa/src/p2p/p2p.h b/contrib/wpa/src/p2p/p2p.h index b4060be477b6..7b18dcfc3ff3 100644 --- a/contrib/wpa/src/p2p/p2p.h +++ b/contrib/wpa/src/p2p/p2p.h @@ -31,7 +31,7 @@ /** * P2P_MAX_REG_CLASSES - Maximum number of regulatory classes */ -#define P2P_MAX_REG_CLASSES 10 +#define P2P_MAX_REG_CLASSES 15 /** * P2P_MAX_REG_CLASS_CHANNELS - Maximum number of channels per regulatory class @@ -99,6 +99,10 @@ struct p2p_go_neg_results { int vht; + u8 max_oper_chwidth; + + unsigned int vht_center_freq2; + /** * ssid - SSID of the group */ @@ -223,6 +227,16 @@ struct p2ps_provision { */ u8 cpt_priority[P2PS_FEATURE_CAPAB_CPT_MAX + 1]; + /** + * force_freq - The only allowed channel frequency in MHz or 0. + */ + unsigned int force_freq; + + /** + * pref_freq - Preferred operating frequency in MHz or 0. + */ + unsigned int pref_freq; + /** * info - Vendor defined extra Provisioning information */ @@ -1024,6 +1038,8 @@ struct p2p_config { * @ssid_len: Buffer for returning length of @ssid * @group_iface: Buffer for returning whether a separate group interface * would be used + * @freq: Variable for returning the current operating frequency of a + * currently running P2P GO. * Returns: 1 if GO info found, 0 otherwise * * This is used to compose New Group settings (SSID, and intended @@ -1031,7 +1047,8 @@ struct p2p_config { * result in our being an autonomous GO. */ int (*get_go_info)(void *ctx, u8 *intended_addr, - u8 *ssid, size_t *ssid_len, int *group_iface); + u8 *ssid, size_t *ssid_len, int *group_iface, + unsigned int *freq); /** * remove_stale_groups - Remove stale P2PS groups @@ -1056,7 +1073,9 @@ struct p2p_config { const u8 *persist_ssid, size_t persist_ssid_size, int response_done, int prov_start, const char *session_info, - const u8 *feat_cap, size_t feat_cap_len); + const u8 *feat_cap, size_t feat_cap_len, + unsigned int freq, const u8 *group_ssid, + size_t group_ssid_len); /** * prov_disc_resp_cb - Callback for indicating completion of PD Response @@ -1070,14 +1089,20 @@ struct p2p_config { /** * p2ps_group_capability - Determine group capability + * @ctx: Callback context from cb_ctx + * @incoming: Peer requested roles, expressed with P2PS_SETUP_* bitmap. + * @role: Local roles, expressed with P2PS_SETUP_* bitmap. + * @force_freq: Variable for returning forced frequency for the group. + * @pref_freq: Variable for returning preferred frequency for the group. + * Returns: P2PS_SETUP_* bitmap of group capability result. * - * This function can be used to determine group capability based on - * information from P2PS PD exchange and the current state of ongoing - * groups and driver capabilities. - * - * P2PS_SETUP_* bitmap is used as the parameters and return value. + * This function can be used to determine group capability and + * frequencies based on information from P2PS PD exchange and the + * current state of ongoing groups and driver capabilities. */ - u8 (*p2ps_group_capability)(void *ctx, u8 incoming, u8 role); + u8 (*p2ps_group_capability)(void *ctx, u8 incoming, u8 role, + unsigned int *force_freq, + unsigned int *pref_freq); /** * get_pref_freq_list - Get preferred frequency list for an interface @@ -1530,12 +1555,13 @@ enum p2p_probe_req_status { * @ie: Information elements from the Probe Request frame body * @ie_len: Length of ie buffer in octets * @rx_freq: Probe Request frame RX frequency + * @p2p_lo_started: Whether P2P Listen Offload is started * Returns: value indicating the type and status of the probe request */ enum p2p_probe_req_status p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, const u8 *bssid, const u8 *ie, size_t ie_len, - unsigned int rx_freq); + unsigned int rx_freq, int p2p_lo_started); /** * p2p_rx_action - Report received Action frame @@ -1690,6 +1716,12 @@ struct p2p_group_config { */ int freq; + /** + * ip_addr_alloc - Whether IP address allocation within 4-way handshake + * is supported + */ + int ip_addr_alloc; + /** * cb_ctx - Context to use with callback functions */ @@ -1877,8 +1909,10 @@ int p2p_assoc_req_ie(struct p2p_data *p2p, const u8 *bssid, u8 *buf, * @p2p: P2P module context from p2p_init() * @ies: Buffer for writing P2P IE * @dev_id: Device ID to search for or %NULL for any + * @bands: Frequency bands used in the scan (enum wpa_radio_work_band bitmap) */ -void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id); +void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id, + unsigned int bands); /** * p2p_scan_ie_buf_len - Get maximum buffer length needed for p2p_scan_ie @@ -2099,6 +2133,16 @@ int p2p_client_limit_reached(struct p2p_group *group); */ const u8 * p2p_iterate_group_members(struct p2p_group *group, void **next); +/** + * p2p_group_get_client_interface_addr - Get P2P Interface Address of a client in a group + * @group: P2P group context from p2p_group_init() + * @dev_addr: P2P Device Address of the client + * Returns: P2P Interface Address of the client if found or %NULL if no match + * found + */ +const u8 * p2p_group_get_client_interface_addr(struct p2p_group *group, + const u8 *dev_addr); + /** * p2p_group_get_dev_addr - Get a P2P Device Address of a client in a group * @group: P2P group context from p2p_group_init() @@ -2241,7 +2285,7 @@ struct wpabuf * wifi_display_encaps(struct wpabuf *subelems); * discovery (p2p_find). A random number of 100 TU units is picked for each * Listen state iteration from [min_disc_int,max_disc_int] range. * - * max_disc_tu can be used to futher limit the discoverable duration. However, + * max_disc_tu can be used to further limit the discoverable duration. However, * it should be noted that use of this parameter is not recommended since it * would not be compliant with the P2P specification. */ @@ -2340,4 +2384,7 @@ void p2p_set_own_pref_freq_list(struct p2p_data *p2p, int p2p_group_get_common_freqs(struct p2p_group *group, int *common_freqs, unsigned int *num); +struct wpabuf * p2p_build_probe_resp_template(struct p2p_data *p2p, + unsigned int freq); + #endif /* P2P_H */ diff --git a/contrib/wpa/src/p2p/p2p_build.c b/contrib/wpa/src/p2p/p2p_build.c index 793d28ba7bdd..2882c6ad02e7 100644 --- a/contrib/wpa/src/p2p/p2p_build.c +++ b/contrib/wpa/src/p2p/p2p_build.c @@ -202,11 +202,11 @@ void p2p_buf_add_device_info(struct wpabuf *buf, struct p2p_data *p2p, if (peer && peer->wps_method != WPS_NOT_READY) { if (peer->wps_method == WPS_PBC) methods |= WPS_CONFIG_PUSHBUTTON; - else if (peer->wps_method == WPS_PIN_DISPLAY || - peer->wps_method == WPS_PIN_KEYPAD) { - methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD; + else if (peer->wps_method == WPS_P2PS) methods |= WPS_CONFIG_P2PS; - } + else if (peer->wps_method == WPS_PIN_DISPLAY || + peer->wps_method == WPS_PIN_KEYPAD) + methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD; } else if (p2p->cfg->config_methods) { methods |= p2p->cfg->config_methods & (WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_DISPLAY | diff --git a/contrib/wpa/src/p2p/p2p_go_neg.c b/contrib/wpa/src/p2p/p2p_go_neg.c index 83b43563d945..9f0b3f3d37a4 100644 --- a/contrib/wpa/src/p2p/p2p_go_neg.c +++ b/contrib/wpa/src/p2p/p2p_go_neg.c @@ -38,7 +38,7 @@ int p2p_peer_channels_check(struct p2p_data *p2p, struct p2p_channels *own, { const u8 *pos, *end; struct p2p_channels *ch; - size_t channels; + u8 channels; struct p2p_channels intersection; ch = &dev->channels; @@ -58,14 +58,14 @@ int p2p_peer_channels_check(struct p2p_data *p2p, struct p2p_channels *own, } pos += 3; - while (pos + 2 < end) { + while (end - pos > 2) { struct p2p_reg_class *cl = &ch->reg_class[ch->reg_classes]; cl->reg_class = *pos++; - if (pos + 1 + pos[0] > end) { + channels = *pos++; + if (channels > end - pos) { p2p_info(p2p, "Invalid peer Channel List"); return -1; } - channels = *pos++; cl->channels = channels > P2P_MAX_REG_CLASS_CHANNELS ? P2P_MAX_REG_CLASS_CHANNELS : channels; os_memcpy(cl->channel, pos, cl->channels); @@ -384,7 +384,7 @@ void p2p_reselect_channel(struct p2p_data *p2p, unsigned int i; const int op_classes_5ghz[] = { 124, 125, 115, 0 }; const int op_classes_ht40[] = { 126, 127, 116, 117, 0 }; - const int op_classes_vht[] = { 128, 0 }; + const int op_classes_vht[] = { 128, 129, 130, 0 }; if (p2p->own_freq_preference > 0 && p2p_freq_to_channel(p2p->own_freq_preference, @@ -901,6 +901,14 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, return; } + if (dev->go_neg_req_sent && + (dev->flags & P2P_DEV_PEER_WAITING_RESPONSE)) { + p2p_dbg(p2p, + "Do not reply since peer is waiting for us to start a new GO Negotiation and GO Neg Request already sent"); + p2p_parse_free(&msg); + return; + } + go = p2p_go_det(p2p->go_intent, *msg.go_intent); if (go < 0) { p2p_dbg(p2p, "Incompatible GO Intent"); @@ -1052,7 +1060,7 @@ fail: P2P_PENDING_GO_NEG_RESPONSE_FAILURE; if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, p2p->cfg->dev_addr, - wpabuf_head(resp), wpabuf_len(resp), 500) < 0) { + wpabuf_head(resp), wpabuf_len(resp), 100) < 0) { p2p_dbg(p2p, "Failed to send Action frame"); } @@ -1260,6 +1268,11 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, dev->client_timeout = msg.config_timeout[1]; } + if (msg.wfd_subelems) { + wpabuf_free(dev->info.wfd_subelems); + dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); + } + if (!msg.operating_channel && !go) { /* * Note: P2P Client may omit Operating Channel attribute to @@ -1386,7 +1399,7 @@ fail: if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, sa, wpabuf_head(dev->go_neg_conf), - wpabuf_len(dev->go_neg_conf), 200) < 0) { + wpabuf_len(dev->go_neg_conf), 50) < 0) { p2p_dbg(p2p, "Failed to send Action frame"); p2p_go_neg_failed(p2p, -1); p2p->cfg->send_action_done(p2p->cfg->cb_ctx); diff --git a/contrib/wpa/src/p2p/p2p_group.c b/contrib/wpa/src/p2p/p2p_group.c index 0d6699346568..051b4e391505 100644 --- a/contrib/wpa/src/p2p/p2p_group.c +++ b/contrib/wpa/src/p2p/p2p_group.c @@ -155,7 +155,8 @@ static void p2p_group_add_common_ies(struct p2p_group *group, group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; if (group->num_members >= group->cfg->max_clients) group_capab |= P2P_GROUP_CAPAB_GROUP_LIMIT; - group_capab |= P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION; + if (group->cfg->ip_addr_alloc) + group_capab |= P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION; p2p_buf_add_capability(ie, dev_capab, group_capab); } @@ -296,14 +297,14 @@ static int wifi_display_add_dev_info_descr(struct wpabuf *buf, os_memset(zero_addr, 0, ETH_ALEN); pos = wpabuf_head_u8(m->wfd_ie); end = pos + wpabuf_len(m->wfd_ie); - while (pos + 1 < end) { + while (end - pos >= 3) { u8 id; u16 len; id = *pos++; len = WPA_GET_BE16(pos); pos += 2; - if (pos + len > end) + if (len > end - pos) break; switch (id) { @@ -849,6 +850,20 @@ static struct p2p_group_member * p2p_group_get_client(struct p2p_group *group, } +const u8 * p2p_group_get_client_interface_addr(struct p2p_group *group, + const u8 *dev_addr) +{ + struct p2p_group_member *m; + + if (!group) + return NULL; + m = p2p_group_get_client(group, dev_addr); + if (m) + return m->addr; + return NULL; +} + + static struct p2p_group_member * p2p_group_get_client_iface( struct p2p_group *group, const u8 *interface_addr) { @@ -1097,7 +1112,7 @@ int p2p_group_get_common_freqs(struct p2p_group *group, int *common_freqs, struct p2p_device *dev; dev = p2p_get_device(group->p2p, m->dev_addr); - if (!dev) + if (!dev || dev->channels.reg_classes == 0) continue; p2p_channels_intersect(&intersect, &dev->channels, &res); diff --git a/contrib/wpa/src/p2p/p2p_i.h b/contrib/wpa/src/p2p/p2p_i.h index 0ce4058fe3e6..47524d4991a5 100644 --- a/contrib/wpa/src/p2p/p2p_i.h +++ b/contrib/wpa/src/p2p/p2p_i.h @@ -308,6 +308,18 @@ struct p2p_data { */ int num_p2p_sd_queries; + /** + * sd_query_no_ack - The first peer (Dev Addr) that did not ACK SD Query + * + * This is used to track the first peer that did not ACK an SD Query + * within a single P2P Search iteration. All zeros address means no such + * peer was yet seen. This information is used to allow a new Listen and + * Search phases to be once every pending SD Query has been sent once to + * each peer instead of looping all pending attempts continuously until + * running out of retry maximums. + */ + u8 sd_query_no_ack[ETH_ALEN]; + /* GO Negotiation data */ /** @@ -691,6 +703,8 @@ int p2p_channel_random_social(struct p2p_channels *chans, u8 *op_class, u8 *op_channel); /* p2p_parse.c */ +void p2p_copy_filter_devname(char *dst, size_t dst_len, + const void *src, size_t src_len); int p2p_parse_p2p_ie(const struct wpabuf *buf, struct p2p_message *msg); int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg); int p2p_parse(const u8 *data, size_t len, struct p2p_message *msg); diff --git a/contrib/wpa/src/p2p/p2p_invitation.c b/contrib/wpa/src/p2p/p2p_invitation.c index 108e5b7f93e4..bbba001a7c93 100644 --- a/contrib/wpa/src/p2p/p2p_invitation.c +++ b/contrib/wpa/src/p2p/p2p_invitation.c @@ -284,7 +284,7 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, if (!p2p_channels_includes(&intersection, reg_class, channel)) { - p2p_dbg(p2p, "forced freq %d MHz not in the supported channels interaction", + p2p_dbg(p2p, "forced freq %d MHz not in the supported channels intersection", op_freq); status = P2P_SC_FAIL_NO_COMMON_CHANNELS; goto fail; @@ -418,7 +418,7 @@ fail: p2p->pending_action_state = P2P_PENDING_INVITATION_RESPONSE; if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, p2p->cfg->dev_addr, - wpabuf_head(resp), wpabuf_len(resp), 200) < 0) { + wpabuf_head(resp), wpabuf_len(resp), 50) < 0) { p2p_dbg(p2p, "Failed to send Action frame"); } diff --git a/contrib/wpa/src/p2p/p2p_parse.c b/contrib/wpa/src/p2p/p2p_parse.c index bd1e68bd4241..5d2299cb25c2 100644 --- a/contrib/wpa/src/p2p/p2p_parse.c +++ b/contrib/wpa/src/p2p/p2p_parse.c @@ -15,11 +15,29 @@ #include "p2p_i.h" +void p2p_copy_filter_devname(char *dst, size_t dst_len, + const void *src, size_t src_len) +{ + size_t i; + + if (src_len >= dst_len) + src_len = dst_len - 1; + os_memcpy(dst, src, src_len); + dst[src_len] = '\0'; + for (i = 0; i < src_len; i++) { + if (dst[i] == '\0') + break; + if (is_ctrl_char(dst[i])) + dst[i] = '_'; + } +} + + static int p2p_parse_attribute(u8 id, const u8 *data, u16 len, struct p2p_message *msg) { const u8 *pos; - size_t i, nlen; + u16 nlen; char devtype[WPS_DEV_TYPE_BUFSIZE]; switch (id) { @@ -149,21 +167,14 @@ static int p2p_parse_attribute(u8 id, const u8 *data, u16 len, pos += 2; nlen = WPA_GET_BE16(pos); pos += 2; - if (data + len - pos < (int) nlen || - nlen > WPS_DEV_NAME_MAX_LEN) { + if (nlen > data + len - pos || nlen > WPS_DEV_NAME_MAX_LEN) { wpa_printf(MSG_DEBUG, "P2P: Invalid Device Name " - "length %d (buf len %d)", (int) nlen, + "length %u (buf len %d)", nlen, (int) (data + len - pos)); return -1; } - os_memcpy(msg->device_name, pos, nlen); - msg->device_name[nlen] = '\0'; - for (i = 0; i < nlen; i++) { - if (msg->device_name[i] == '\0') - break; - if (is_ctrl_char(msg->device_name[i])) - msg->device_name[i] = '_'; - } + p2p_copy_filter_devname(msg->device_name, + sizeof(msg->device_name), pos, nlen); wpa_printf(MSG_DEBUG, "P2P: * Device Info: addr " MACSTR " primary device type %s device name '%s' " "config methods 0x%x", @@ -637,49 +648,48 @@ int p2p_group_info_parse(const u8 *gi, size_t gi_len, gend = gi + gi_len; while (g < gend) { struct p2p_client_info *cli; - const u8 *t, *cend; - int count; + const u8 *cend; + u16 count; + u8 len; cli = &info->client[info->num_clients]; - cend = g + 1 + g[0]; - if (cend > gend) + len = *g++; + if (len > gend - g || len < 2 * ETH_ALEN + 1 + 2 + 8 + 1) return -1; /* invalid data */ + cend = g + len; /* g at start of P2P Client Info Descriptor */ - /* t at Device Capability Bitmap */ - t = g + 1 + 2 * ETH_ALEN; - if (t > cend) - return -1; /* invalid data */ - cli->p2p_device_addr = g + 1; - cli->p2p_interface_addr = g + 1 + ETH_ALEN; - cli->dev_capab = t[0]; + cli->p2p_device_addr = g; + g += ETH_ALEN; + cli->p2p_interface_addr = g; + g += ETH_ALEN; + cli->dev_capab = *g++; - if (t + 1 + 2 + 8 + 1 > cend) - return -1; /* invalid data */ + cli->config_methods = WPA_GET_BE16(g); + g += 2; + cli->pri_dev_type = g; + g += 8; - cli->config_methods = WPA_GET_BE16(&t[1]); - cli->pri_dev_type = &t[3]; - - t += 1 + 2 + 8; - /* t at Number of Secondary Device Types */ - cli->num_sec_dev_types = *t++; - if (t + 8 * cli->num_sec_dev_types > cend) + /* g at Number of Secondary Device Types */ + len = *g++; + if (8 * len > cend - g) return -1; /* invalid data */ - cli->sec_dev_types = t; - t += 8 * cli->num_sec_dev_types; + cli->num_sec_dev_types = len; + cli->sec_dev_types = g; + g += 8 * len; - /* t at Device Name in WPS TLV format */ - if (t + 2 + 2 > cend) + /* g at Device Name in WPS TLV format */ + if (cend - g < 2 + 2) return -1; /* invalid data */ - if (WPA_GET_BE16(t) != ATTR_DEV_NAME) + if (WPA_GET_BE16(g) != ATTR_DEV_NAME) return -1; /* invalid Device Name TLV */ - t += 2; - count = WPA_GET_BE16(t); - t += 2; - if (count > cend - t) + g += 2; + count = WPA_GET_BE16(g); + g += 2; + if (count > cend - g) return -1; /* invalid Device Name TLV */ if (count >= WPS_DEV_NAME_MAX_LEN) count = WPS_DEV_NAME_MAX_LEN; - cli->dev_name = (const char *) t; + cli->dev_name = (const char *) g; cli->dev_name_len = count; g = cend; diff --git a/contrib/wpa/src/p2p/p2p_pd.c b/contrib/wpa/src/p2p/p2p_pd.c index 890094551821..93a0535f873a 100644 --- a/contrib/wpa/src/p2p/p2p_pd.c +++ b/contrib/wpa/src/p2p/p2p_pd.c @@ -40,21 +40,31 @@ static void p2p_build_wps_ie_config_methods(struct wpabuf *buf, } -static void p2ps_add_new_group_info(struct p2p_data *p2p, struct wpabuf *buf) +static void p2ps_add_new_group_info(struct p2p_data *p2p, + struct p2p_device *dev, + struct wpabuf *buf) { int found; u8 intended_addr[ETH_ALEN]; u8 ssid[SSID_MAX_LEN]; size_t ssid_len; int group_iface; + unsigned int force_freq; if (!p2p->cfg->get_go_info) return; found = p2p->cfg->get_go_info( p2p->cfg->cb_ctx, intended_addr, ssid, - &ssid_len, &group_iface); + &ssid_len, &group_iface, &force_freq); if (found) { + if (force_freq > 0) { + p2p->p2ps_prov->force_freq = force_freq; + p2p->p2ps_prov->pref_freq = 0; + + if (dev) + p2p_prepare_channel(p2p, dev, force_freq, 0, 0); + } p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, ssid, ssid_len); @@ -92,62 +102,62 @@ static void p2ps_add_pd_req_attrs(struct p2p_data *p2p, struct p2p_device *dev, size_t ssid_len; u8 go_dev_addr[ETH_ALEN]; u8 intended_addr[ETH_ALEN]; + int follow_on_req_fail = prov->status >= 0 && + prov->status != P2P_SC_SUCCESS_DEFERRED; /* If we might be explicite group owner, add GO details */ - if (prov->conncap & (P2PS_SETUP_GROUP_OWNER | - P2PS_SETUP_NEW)) - p2ps_add_new_group_info(p2p, buf); + if (!follow_on_req_fail && + (prov->conncap & (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW))) + p2ps_add_new_group_info(p2p, dev, buf); if (prov->status >= 0) p2p_buf_add_status(buf, (u8) prov->status); else prov->method = config_methods; - if (p2p->cfg->get_persistent_group) { - shared_group = p2p->cfg->get_persistent_group( - p2p->cfg->cb_ctx, dev->info.p2p_device_addr, NULL, 0, - go_dev_addr, ssid, &ssid_len, intended_addr); - } + if (!follow_on_req_fail) { + if (p2p->cfg->get_persistent_group) { + shared_group = p2p->cfg->get_persistent_group( + p2p->cfg->cb_ctx, dev->info.p2p_device_addr, + NULL, 0, go_dev_addr, ssid, &ssid_len, + intended_addr); + } - /* Add Operating Channel if conncap includes GO */ - if (shared_group || - (prov->conncap & (P2PS_SETUP_GROUP_OWNER | - P2PS_SETUP_NEW))) { - u8 tmp; + if (shared_group || + (prov->conncap & (P2PS_SETUP_CLIENT | P2PS_SETUP_NEW))) + p2p_buf_add_channel_list(buf, p2p->cfg->country, + &p2p->channels); - p2p_go_select_channel(p2p, dev, &tmp); - - if (p2p->op_reg_class && p2p->op_channel) + if ((shared_group && !is_zero_ether_addr(intended_addr)) || + (prov->conncap & (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW))) p2p_buf_add_operating_channel(buf, p2p->cfg->country, p2p->op_reg_class, p2p->op_channel); - else - p2p_buf_add_operating_channel(buf, p2p->cfg->country, - p2p->cfg->op_reg_class, - p2p->cfg->op_channel); } - p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->cfg->channels); - - if (prov->info[0]) + if (prov->status < 0 && prov->info[0]) p2p_buf_add_session_info(buf, prov->info); - p2p_buf_add_connection_capability(buf, prov->conncap); + if (!follow_on_req_fail) + p2p_buf_add_connection_capability(buf, prov->conncap); p2p_buf_add_advertisement_id(buf, prov->adv_id, prov->adv_mac); - if (shared_group || prov->conncap == P2PS_SETUP_NEW || - prov->conncap == - (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW) || - prov->conncap == - (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT)) { - /* Add Config Timeout */ - p2p_buf_add_config_timeout(buf, p2p->go_timeout, - p2p->client_timeout); - } + if (!follow_on_req_fail) { + if (shared_group || prov->conncap == P2PS_SETUP_NEW || + prov->conncap == + (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW) || + prov->conncap == + (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT)) { + /* Add Config Timeout */ + p2p_buf_add_config_timeout(buf, p2p->go_timeout, + p2p->client_timeout); + } - p2p_buf_add_listen_channel(buf, p2p->cfg->country, p2p->cfg->reg_class, - p2p->cfg->channel); + p2p_buf_add_listen_channel(buf, p2p->cfg->country, + p2p->cfg->reg_class, + p2p->cfg->channel); + } p2p_buf_add_session_id(buf, prov->session_id, prov->session_mac); @@ -285,6 +295,11 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, u8 *len = p2p_buf_add_ie_hdr(buf); struct p2ps_provision *prov = p2p->p2ps_prov; u8 group_capab; + u8 conncap = 0; + + if (status == P2P_SC_SUCCESS || + status == P2P_SC_SUCCESS_DEFERRED) + conncap = prov->conncap; if (!status && prov->status != -1) status = prov->status; @@ -301,7 +316,7 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, group_capab); p2p_buf_add_device_info(buf, p2p, NULL); - if (persist_ssid && p2p->cfg->get_persistent_group && + if (persist_ssid && p2p->cfg->get_persistent_group && dev && (status == P2P_SC_SUCCESS || status == P2P_SC_SUCCESS_DEFERRED)) { u8 ssid[SSID_MAX_LEN]; @@ -323,16 +338,11 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, } } - if (!persist && (prov->conncap & P2PS_SETUP_GROUP_OWNER)) - p2ps_add_new_group_info(p2p, buf); + if (!persist && (conncap & P2PS_SETUP_GROUP_OWNER)) + p2ps_add_new_group_info(p2p, dev, buf); /* Add Operating Channel if conncap indicates GO */ - if (persist || (prov->conncap & P2PS_SETUP_GROUP_OWNER)) { - u8 tmp; - - if (dev) - p2p_go_select_channel(p2p, dev, &tmp); - + if (persist || (conncap & P2PS_SETUP_GROUP_OWNER)) { if (p2p->op_reg_class && p2p->op_channel) p2p_buf_add_operating_channel( buf, p2p->cfg->country, @@ -345,17 +355,20 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, p2p->cfg->op_channel); } - p2p_buf_add_channel_list(buf, p2p->cfg->country, - &p2p->cfg->channels); + if (persist || + (conncap & (P2PS_SETUP_CLIENT | P2PS_SETUP_GROUP_OWNER))) + p2p_buf_add_channel_list(buf, p2p->cfg->country, + &p2p->channels); - if (!persist && (status == P2P_SC_SUCCESS || - status == P2P_SC_SUCCESS_DEFERRED)) - p2p_buf_add_connection_capability(buf, prov->conncap); + if (!persist && conncap) + p2p_buf_add_connection_capability(buf, conncap); p2p_buf_add_advertisement_id(buf, adv_id, prov->adv_mac); - p2p_buf_add_config_timeout(buf, p2p->go_timeout, - p2p->client_timeout); + if (persist || + (conncap & (P2PS_SETUP_CLIENT | P2PS_SETUP_GROUP_OWNER))) + p2p_buf_add_config_timeout(buf, p2p->go_timeout, + p2p->client_timeout); p2p_buf_add_session_id(buf, prov->session_id, prov->session_mac); @@ -427,6 +440,117 @@ static u8 p2ps_own_preferred_cpt(const u8 *cpt_priority, u8 req_cpt_mask) } +/* Check if the message contains a valid P2PS PD Request */ +static int p2ps_validate_pd_req(struct p2p_data *p2p, struct p2p_message *msg, + const u8 *addr) +{ + u8 group_id = 0; + u8 intended_addr = 0; + u8 operating_channel = 0; + u8 channel_list = 0; + u8 config_timeout = 0; + u8 listen_channel = 0; + +#define P2PS_PD_REQ_CHECK(_val, _attr) \ +do { \ + if ((_val) && !msg->_attr) { \ + p2p_dbg(p2p, "Not P2PS PD Request. Missing %s", #_attr); \ + return -1; \ + } \ +} while (0) + + P2PS_PD_REQ_CHECK(1, adv_id); + P2PS_PD_REQ_CHECK(1, session_id); + P2PS_PD_REQ_CHECK(1, session_mac); + P2PS_PD_REQ_CHECK(1, adv_mac); + P2PS_PD_REQ_CHECK(1, capability); + P2PS_PD_REQ_CHECK(1, p2p_device_info); + P2PS_PD_REQ_CHECK(1, feature_cap); + + /* + * We don't need to check Connection Capability, Persistent Group, + * and related attributes for follow-on PD Request with a status + * other than SUCCESS_DEFERRED. + */ + if (msg->status && *msg->status != P2P_SC_SUCCESS_DEFERRED) + return 0; + + P2PS_PD_REQ_CHECK(1, conn_cap); + + /* + * Note 1: A feature capability attribute structure can be changed + * in the future. The assumption is that such modifications are + * backward compatible, therefore we allow processing of msg.feature_cap + * exceeding the size of the p2ps_feature_capab structure. + * Note 2: Verification of msg.feature_cap_len below has to be changed + * to allow 2 byte feature capability processing if + * struct p2ps_feature_capab is extended to include additional fields + * and it affects the structure size. + */ + if (msg->feature_cap_len < sizeof(struct p2ps_feature_capab)) { + p2p_dbg(p2p, "P2PS: Invalid feature capability len"); + return -1; + } + + switch (*msg->conn_cap) { + case P2PS_SETUP_NEW: + group_id = 1; + intended_addr = 1; + operating_channel = 1; + channel_list = 1; + config_timeout = 1; + listen_channel = 1; + break; + case P2PS_SETUP_CLIENT: + channel_list = 1; + listen_channel = 1; + break; + case P2PS_SETUP_GROUP_OWNER: + group_id = 1; + intended_addr = 1; + operating_channel = 1; + break; + case P2PS_SETUP_NEW | P2PS_SETUP_GROUP_OWNER: + group_id = 1; + operating_channel = 1; + intended_addr = 1; + channel_list = 1; + config_timeout = 1; + break; + case P2PS_SETUP_CLIENT | P2PS_SETUP_GROUP_OWNER: + group_id = 1; + intended_addr = 1; + operating_channel = 1; + channel_list = 1; + config_timeout = 1; + break; + default: + p2p_dbg(p2p, "Invalid P2PS PD connection capability"); + return -1; + } + + if (msg->persistent_dev) { + channel_list = 1; + config_timeout = 1; + if (os_memcmp(msg->persistent_dev, addr, ETH_ALEN) == 0) { + intended_addr = 1; + operating_channel = 1; + } + } + + P2PS_PD_REQ_CHECK(group_id, group_id); + P2PS_PD_REQ_CHECK(intended_addr, intended_addr); + P2PS_PD_REQ_CHECK(operating_channel, operating_channel); + P2PS_PD_REQ_CHECK(channel_list, channel_list); + P2PS_PD_REQ_CHECK(config_timeout, config_timeout); + P2PS_PD_REQ_CHECK(listen_channel, listen_channel); + +#undef P2PS_PD_REQ_CHECK + + return 0; +} + + void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq) { @@ -440,14 +564,16 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, u8 conncap = P2PS_SETUP_NEW; u8 auto_accept = 0; u32 session_id = 0; - u8 session_mac[ETH_ALEN]; - u8 adv_mac[ETH_ALEN]; + u8 session_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; + u8 adv_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; const u8 *group_mac; int passwd_id = DEV_PW_DEFAULT; u16 config_methods; u16 allowed_config_methods = WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD; struct p2ps_feature_capab resp_fcap = { 0, 0 }; - struct p2ps_feature_capab *req_fcap; + struct p2ps_feature_capab *req_fcap = NULL; + u8 remote_conncap; + u16 method; if (p2p_parse(data, len, &msg)) return; @@ -466,90 +592,59 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, 0)) { p2p_dbg(p2p, "Provision Discovery Request add device failed " MACSTR, MAC2STR(sa)); + goto out; + } + + if (!dev) { + dev = p2p_get_device(p2p, sa); + if (!dev) { + p2p_dbg(p2p, + "Provision Discovery device not found " + MACSTR, MAC2STR(sa)); + goto out; + } } } else if (msg.wfd_subelems) { wpabuf_free(dev->info.wfd_subelems); dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); } - if (msg.adv_id) - allowed_config_methods |= WPS_CONFIG_P2PS; - else + if (!msg.adv_id) { allowed_config_methods |= WPS_CONFIG_PUSHBUTTON; - - if (!(msg.wps_config_methods & allowed_config_methods)) { - p2p_dbg(p2p, "Unsupported Config Methods in Provision Discovery Request"); - goto out; - } - - /* Legacy (non-P2PS) - Unknown groups allowed for P2PS */ - if (!msg.adv_id && msg.group_id) { - size_t i; - for (i = 0; i < p2p->num_groups; i++) { - if (p2p_group_is_group_id_match(p2p->groups[i], - msg.group_id, - msg.group_id_len)) - break; - } - if (i == p2p->num_groups) { - p2p_dbg(p2p, "PD request for unknown P2P Group ID - reject"); + if (!(msg.wps_config_methods & allowed_config_methods)) { + p2p_dbg(p2p, + "Unsupported Config Methods in Provision Discovery Request"); goto out; } - } - if (dev) { - dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY | - P2P_DEV_PD_PEER_KEYPAD | - P2P_DEV_PD_PEER_P2PS); + /* Legacy (non-P2PS) - Unknown groups allowed for P2PS */ + if (msg.group_id) { + size_t i; - /* Remove stale persistent groups */ - if (p2p->cfg->remove_stale_groups) { - p2p->cfg->remove_stale_groups( - p2p->cfg->cb_ctx, dev->info.p2p_device_addr, - msg.persistent_dev, - msg.persistent_ssid, msg.persistent_ssid_len); + for (i = 0; i < p2p->num_groups; i++) { + if (p2p_group_is_group_id_match( + p2p->groups[i], + msg.group_id, msg.group_id_len)) + break; + } + if (i == p2p->num_groups) { + p2p_dbg(p2p, + "PD request for unknown P2P Group ID - reject"); + goto out; + } } - } - if (msg.wps_config_methods & WPS_CONFIG_DISPLAY) { - p2p_dbg(p2p, "Peer " MACSTR - " requested us to show a PIN on display", MAC2STR(sa)); - if (dev) - dev->flags |= P2P_DEV_PD_PEER_KEYPAD; - passwd_id = DEV_PW_USER_SPECIFIED; - } else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) { - p2p_dbg(p2p, "Peer " MACSTR - " requested us to write its PIN using keypad", - MAC2STR(sa)); - if (dev) - dev->flags |= P2P_DEV_PD_PEER_DISPLAY; - passwd_id = DEV_PW_REGISTRAR_SPECIFIED; - } else if (msg.wps_config_methods & WPS_CONFIG_P2PS) { - p2p_dbg(p2p, "Peer " MACSTR " requesting P2PS PIN", - MAC2STR(sa)); - if (dev) - dev->flags |= P2P_DEV_PD_PEER_P2PS; - passwd_id = DEV_PW_P2PS_DEFAULT; - } + } else { + allowed_config_methods |= WPS_CONFIG_P2PS; - reject = P2P_SC_SUCCESS; - - os_memset(session_mac, 0, ETH_ALEN); - os_memset(adv_mac, 0, ETH_ALEN); - - /* Note 1: A feature capability attribute structure can be changed - * in the future. The assumption is that such modifications are - * backwards compatible, therefore we allow processing of - * msg.feature_cap exceeding the size of the p2ps_feature_capab - * structure. - * Note 2: Vverification of msg.feature_cap_len below has to be changed - * to allow 2 byte feature capability processing if struct - * p2ps_feature_capab is extended to include additional fields and it - * affects the structure size. - */ - if (msg.adv_id && msg.session_id && msg.session_mac && msg.adv_mac && - msg.feature_cap && msg.feature_cap_len >= sizeof(*req_fcap) && - (msg.status || msg.conn_cap)) { - u8 remote_conncap; + /* + * Set adv_id here, so in case of an error, a P2PS PD Response + * will be sent. + */ + adv_id = WPA_GET_LE32(msg.adv_id); + if (p2ps_validate_pd_req(p2p, &msg, sa) < 0) { + reject = P2P_SC_FAIL_INVALID_PARAMS; + goto out; + } req_fcap = (struct p2ps_feature_capab *) msg.feature_cap; @@ -557,209 +652,371 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN); session_id = WPA_GET_LE32(msg.session_id); - adv_id = WPA_GET_LE32(msg.adv_id); - - if (!msg.status) - p2ps_adv = p2p_service_p2ps_id(p2p, adv_id); - - p2p_dbg(p2p, "adv_id: %x - p2ps_adv - %p", adv_id, p2ps_adv); if (msg.conn_cap) conncap = *msg.conn_cap; - remote_conncap = conncap; - - if (p2ps_adv) { - auto_accept = p2ps_adv->auto_accept; - conncap = p2p->cfg->p2ps_group_capability( - p2p->cfg->cb_ctx, conncap, auto_accept); - - p2p_dbg(p2p, "Conncap: local:%d remote:%d result:%d", - auto_accept, remote_conncap, conncap); - - resp_fcap.cpt = - p2ps_own_preferred_cpt(p2ps_adv->cpt_priority, - req_fcap->cpt); + /* + * We need to verify a P2PS config methog in an initial PD + * request or in a follow-on PD request with the status + * SUCCESS_DEFERRED. + */ + if ((!msg.status || *msg.status == P2P_SC_SUCCESS_DEFERRED) && + !(msg.wps_config_methods & allowed_config_methods)) { p2p_dbg(p2p, - "cpt: service:0x%x remote:0x%x result:0x%x", - p2ps_adv->cpt_mask, req_fcap->cpt, - resp_fcap.cpt); + "Unsupported Config Methods in Provision Discovery Request"); + goto out; + } - if (!resp_fcap.cpt) { - p2p_dbg(p2p, - "Incompatible P2PS feature capability CPT bitmask"); - reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; - } else if (p2ps_adv->config_methods && - !(msg.wps_config_methods & - p2ps_adv->config_methods)) { - p2p_dbg(p2p, - "Unsupported config methods in Provision Discovery Request (own=0x%x peer=0x%x)", - p2ps_adv->config_methods, - msg.wps_config_methods); - reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; - } else if (!p2ps_adv->state) { - p2p_dbg(p2p, "P2PS state unavailable"); - reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; - } else if (!conncap) { - p2p_dbg(p2p, "Conncap resolution failed"); - reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; - } + /* + * TODO: since we don't support multiple PD, reject PD request + * if we are in the middle of P2PS PD with some other peer + */ + } - if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) { - p2p_dbg(p2p, "Keypad - always defer"); - auto_accept = 0; - } + dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY | + P2P_DEV_PD_PEER_KEYPAD | + P2P_DEV_PD_PEER_P2PS); - if (auto_accept || reject != P2P_SC_SUCCESS) { - struct p2ps_provision *tmp; + if (msg.wps_config_methods & WPS_CONFIG_DISPLAY) { + p2p_dbg(p2p, "Peer " MACSTR + " requested us to show a PIN on display", MAC2STR(sa)); + dev->flags |= P2P_DEV_PD_PEER_KEYPAD; + passwd_id = DEV_PW_USER_SPECIFIED; + } else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) { + p2p_dbg(p2p, "Peer " MACSTR + " requested us to write its PIN using keypad", + MAC2STR(sa)); + dev->flags |= P2P_DEV_PD_PEER_DISPLAY; + passwd_id = DEV_PW_REGISTRAR_SPECIFIED; + } else if (msg.wps_config_methods & WPS_CONFIG_P2PS) { + p2p_dbg(p2p, "Peer " MACSTR " requesting P2PS PIN", + MAC2STR(sa)); + dev->flags |= P2P_DEV_PD_PEER_P2PS; + passwd_id = DEV_PW_P2PS_DEFAULT; + } - if (reject == P2P_SC_SUCCESS && !conncap) { - reject = - P2P_SC_FAIL_INCOMPATIBLE_PARAMS; - } + /* Remove stale persistent groups */ + if (p2p->cfg->remove_stale_groups) { + p2p->cfg->remove_stale_groups( + p2p->cfg->cb_ctx, dev->info.p2p_device_addr, + msg.persistent_dev, + msg.persistent_ssid, msg.persistent_ssid_len); + } - if (p2ps_setup_p2ps_prov( - p2p, adv_id, session_id, - msg.wps_config_methods, - session_mac, adv_mac) < 0) { - reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; - goto out; - } + reject = P2P_SC_SUCCESS; - tmp = p2p->p2ps_prov; - if (conncap) { - tmp->conncap = conncap; - tmp->status = P2P_SC_SUCCESS; - } else { - tmp->conncap = auto_accept; - tmp->status = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; - } + /* + * End of a legacy P2P PD Request processing, from this point continue + * with P2PS one. + */ + if (!msg.adv_id) + goto out; - if (reject != P2P_SC_SUCCESS) - goto out; - } - } else if (!msg.status) { + remote_conncap = conncap; + + if (!msg.status) { + unsigned int forced_freq, pref_freq; + + if (os_memcmp(p2p->cfg->dev_addr, msg.adv_mac, ETH_ALEN)) { + p2p_dbg(p2p, + "P2PS PD adv mac does not match the local one"); reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; goto out; } - if (!msg.status && !auto_accept && - (!p2p->p2ps_prov || p2p->p2ps_prov->adv_id != adv_id)) { - struct p2ps_provision *tmp; + p2ps_adv = p2p_service_p2ps_id(p2p, adv_id); + if (!p2ps_adv) { + p2p_dbg(p2p, "P2PS PD invalid adv_id=0x%X", adv_id); + reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + goto out; + } + p2p_dbg(p2p, "adv_id: 0x%X, p2ps_adv: %p", adv_id, p2ps_adv); - if (!conncap) { - reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; - goto out; + auto_accept = p2ps_adv->auto_accept; + conncap = p2p->cfg->p2ps_group_capability(p2p->cfg->cb_ctx, + conncap, auto_accept, + &forced_freq, + &pref_freq); + + p2p_dbg(p2p, "Conncap: local:%d remote:%d result:%d", + auto_accept, remote_conncap, conncap); + + p2p_prepare_channel(p2p, dev, forced_freq, pref_freq, 0); + + resp_fcap.cpt = p2ps_own_preferred_cpt(p2ps_adv->cpt_priority, + req_fcap->cpt); + + p2p_dbg(p2p, "cpt: service:0x%x remote:0x%x result:0x%x", + p2ps_adv->cpt_mask, req_fcap->cpt, resp_fcap.cpt); + + if (!resp_fcap.cpt) { + p2p_dbg(p2p, + "Incompatible P2PS feature capability CPT bitmask"); + reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + } else if (p2ps_adv->config_methods && + !(msg.wps_config_methods & + p2ps_adv->config_methods)) { + p2p_dbg(p2p, + "Unsupported config methods in Provision Discovery Request (own=0x%x peer=0x%x)", + p2ps_adv->config_methods, + msg.wps_config_methods); + reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + } else if (!p2ps_adv->state) { + p2p_dbg(p2p, "P2PS state unavailable"); + reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; + } else if (!conncap) { + p2p_dbg(p2p, "Conncap resolution failed"); + reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + } + + if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) { + p2p_dbg(p2p, "Keypad - always defer"); + auto_accept = 0; + } + + if ((remote_conncap & (P2PS_SETUP_NEW | P2PS_SETUP_CLIENT) || + msg.persistent_dev) && conncap != P2PS_SETUP_NEW && + msg.channel_list && msg.channel_list_len && + p2p_peer_channels_check(p2p, &p2p->channels, dev, + msg.channel_list, + msg.channel_list_len) < 0) { + p2p_dbg(p2p, + "No common channels - force deferred flow"); + auto_accept = 0; + } + + if (((remote_conncap & P2PS_SETUP_GROUP_OWNER) || + msg.persistent_dev) && msg.operating_channel) { + struct p2p_channels intersect; + + /* + * There are cases where only the operating channel is + * provided. This requires saving the channel as the + * supported channel list, and verifying that it is + * supported. + */ + if (dev->channels.reg_classes == 0 || + !p2p_channels_includes(&dev->channels, + msg.operating_channel[3], + msg.operating_channel[4])) { + struct p2p_channels *ch = &dev->channels; + + os_memset(ch, 0, sizeof(*ch)); + ch->reg_class[0].reg_class = + msg.operating_channel[3]; + ch->reg_class[0].channel[0] = + msg.operating_channel[4]; + ch->reg_class[0].channels = 1; + ch->reg_classes = 1; } + p2p_channels_intersect(&p2p->channels, &dev->channels, + &intersect); + + if (intersect.reg_classes == 0) { + p2p_dbg(p2p, + "No common channels - force deferred flow"); + auto_accept = 0; + } + } + + if (auto_accept || reject != P2P_SC_SUCCESS) { + struct p2ps_provision *tmp; + if (p2ps_setup_p2ps_prov(p2p, adv_id, session_id, msg.wps_config_methods, session_mac, adv_mac) < 0) { reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; goto out; } + tmp = p2p->p2ps_prov; - reject = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; - tmp->status = reject; - } - - if (msg.status) { - if (*msg.status && - *msg.status != P2P_SC_SUCCESS_DEFERRED) { - reject = *msg.status; - } else if (*msg.status == P2P_SC_SUCCESS_DEFERRED && - p2p->p2ps_prov) { - u16 method = p2p->p2ps_prov->method; - - conncap = p2p->cfg->p2ps_group_capability( - p2p->cfg->cb_ctx, remote_conncap, - p2p->p2ps_prov->conncap); - - p2p_dbg(p2p, - "Conncap: local:%d remote:%d result:%d", - p2p->p2ps_prov->conncap, - remote_conncap, conncap); - - resp_fcap.cpt = p2ps_own_preferred_cpt( - p2p->p2ps_prov->cpt_priority, - req_fcap->cpt); - - p2p_dbg(p2p, - "cpt: local:0x%x remote:0x%x result:0x%x", - p2p->p2ps_prov->cpt_mask, - req_fcap->cpt, resp_fcap.cpt); - - /* - * Ensure that if we asked for PIN originally, - * our method is consistent with original - * request. - */ - if (method & WPS_CONFIG_DISPLAY) - method = WPS_CONFIG_KEYPAD; - else if (method & WPS_CONFIG_KEYPAD) - method = WPS_CONFIG_DISPLAY; - - if (!conncap || - !(msg.wps_config_methods & method)) { - /* - * Reject this "Deferred Accept* - * if incompatible conncap or method - */ - reject = - P2P_SC_FAIL_INCOMPATIBLE_PARAMS; - } else if (!resp_fcap.cpt) { - p2p_dbg(p2p, - "Incompatible P2PS feature capability CPT bitmask"); - reject = - P2P_SC_FAIL_INCOMPATIBLE_PARAMS; - } else { - reject = P2P_SC_SUCCESS; - } - - p2p->p2ps_prov->status = reject; - p2p->p2ps_prov->conncap = conncap; + tmp->force_freq = forced_freq; + tmp->pref_freq = pref_freq; + if (conncap) { + tmp->conncap = conncap; + tmp->status = P2P_SC_SUCCESS; + } else { + tmp->conncap = auto_accept; + tmp->status = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; } + + if (reject != P2P_SC_SUCCESS) + goto out; } } + if (!msg.status && !auto_accept && + (!p2p->p2ps_prov || p2p->p2ps_prov->adv_id != adv_id)) { + struct p2ps_provision *tmp; + + if (!conncap) { + reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + goto out; + } + + if (p2ps_setup_p2ps_prov(p2p, adv_id, session_id, + msg.wps_config_methods, + session_mac, adv_mac) < 0) { + reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; + goto out; + } + tmp = p2p->p2ps_prov; + reject = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; + tmp->status = reject; + } + + /* Not a P2PS Follow-on PD */ + if (!msg.status) + goto out; + + if (*msg.status && *msg.status != P2P_SC_SUCCESS_DEFERRED) { + reject = *msg.status; + goto out; + } + + if (*msg.status != P2P_SC_SUCCESS_DEFERRED || !p2p->p2ps_prov) + goto out; + + if (p2p->p2ps_prov->adv_id != adv_id || + os_memcmp(p2p->p2ps_prov->adv_mac, msg.adv_mac, ETH_ALEN)) { + p2p_dbg(p2p, + "P2PS Follow-on PD with mismatch Advertisement ID/MAC"); + goto out; + } + + if (p2p->p2ps_prov->session_id != session_id || + os_memcmp(p2p->p2ps_prov->session_mac, msg.session_mac, ETH_ALEN)) { + p2p_dbg(p2p, "P2PS Follow-on PD with mismatch Session ID/MAC"); + goto out; + } + + method = p2p->p2ps_prov->method; + + conncap = p2p->cfg->p2ps_group_capability(p2p->cfg->cb_ctx, + remote_conncap, + p2p->p2ps_prov->conncap, + &p2p->p2ps_prov->force_freq, + &p2p->p2ps_prov->pref_freq); + + resp_fcap.cpt = p2ps_own_preferred_cpt(p2p->p2ps_prov->cpt_priority, + req_fcap->cpt); + + p2p_dbg(p2p, "cpt: local:0x%x remote:0x%x result:0x%x", + p2p->p2ps_prov->cpt_mask, req_fcap->cpt, resp_fcap.cpt); + + p2p_prepare_channel(p2p, dev, p2p->p2ps_prov->force_freq, + p2p->p2ps_prov->pref_freq, 0); + + /* + * Ensure that if we asked for PIN originally, our method is consistent + * with original request. + */ + if (method & WPS_CONFIG_DISPLAY) + method = WPS_CONFIG_KEYPAD; + else if (method & WPS_CONFIG_KEYPAD) + method = WPS_CONFIG_DISPLAY; + + if (!conncap || !(msg.wps_config_methods & method)) { + /* + * Reject this "Deferred Accept* + * if incompatible conncap or method + */ + reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + } else if (!resp_fcap.cpt) { + p2p_dbg(p2p, + "Incompatible P2PS feature capability CPT bitmask"); + reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + } else if ((remote_conncap & (P2PS_SETUP_NEW | P2PS_SETUP_CLIENT) || + msg.persistent_dev) && conncap != P2PS_SETUP_NEW && + msg.channel_list && msg.channel_list_len && + p2p_peer_channels_check(p2p, &p2p->channels, dev, + msg.channel_list, + msg.channel_list_len) < 0) { + p2p_dbg(p2p, + "No common channels in Follow-On Provision Discovery Request"); + reject = P2P_SC_FAIL_NO_COMMON_CHANNELS; + } else { + reject = P2P_SC_SUCCESS; + } + + dev->oper_freq = 0; + if (reject == P2P_SC_SUCCESS || reject == P2P_SC_SUCCESS_DEFERRED) { + u8 tmp; + + if (msg.operating_channel) + dev->oper_freq = + p2p_channel_to_freq(msg.operating_channel[3], + msg.operating_channel[4]); + + if ((conncap & P2PS_SETUP_GROUP_OWNER) && + p2p_go_select_channel(p2p, dev, &tmp) < 0) + reject = P2P_SC_FAIL_NO_COMMON_CHANNELS; + } + + p2p->p2ps_prov->status = reject; + p2p->p2ps_prov->conncap = conncap; + out: if (reject == P2P_SC_SUCCESS || reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) config_methods = msg.wps_config_methods; else config_methods = 0; - resp = p2p_build_prov_disc_resp(p2p, dev, msg.dialog_token, reject, - config_methods, adv_id, - msg.group_id, msg.group_id_len, - msg.persistent_ssid, - msg.persistent_ssid_len, - (const u8 *) &resp_fcap, - sizeof(resp_fcap)); - if (resp == NULL) { - p2p_parse_free(&msg); - return; - } - p2p_dbg(p2p, "Sending Provision Discovery Response"); - if (rx_freq > 0) - freq = rx_freq; - else - freq = p2p_channel_to_freq(p2p->cfg->reg_class, - p2p->cfg->channel); - if (freq < 0) { - p2p_dbg(p2p, "Unknown regulatory class/channel"); - wpabuf_free(resp); - p2p_parse_free(&msg); - return; - } - p2p->pending_action_state = P2P_PENDING_PD_RESPONSE; - if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, - p2p->cfg->dev_addr, - wpabuf_head(resp), wpabuf_len(resp), 200) < 0) { - p2p_dbg(p2p, "Failed to send Action frame"); - } else - p2p->send_action_in_progress = 1; - wpabuf_free(resp); + /* + * Send PD Response for an initial PD Request or for follow-on + * PD Request with P2P_SC_SUCCESS_DEFERRED status. + */ + if (!msg.status || *msg.status == P2P_SC_SUCCESS_DEFERRED) { + resp = p2p_build_prov_disc_resp(p2p, dev, msg.dialog_token, + reject, config_methods, adv_id, + msg.group_id, msg.group_id_len, + msg.persistent_ssid, + msg.persistent_ssid_len, + (const u8 *) &resp_fcap, + sizeof(resp_fcap)); + if (!resp) { + p2p_parse_free(&msg); + return; + } + p2p_dbg(p2p, "Sending Provision Discovery Response"); + if (rx_freq > 0) + freq = rx_freq; + else + freq = p2p_channel_to_freq(p2p->cfg->reg_class, + p2p->cfg->channel); + if (freq < 0) { + p2p_dbg(p2p, "Unknown regulatory class/channel"); + wpabuf_free(resp); + p2p_parse_free(&msg); + return; + } + p2p->pending_action_state = P2P_PENDING_PD_RESPONSE; + if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, + p2p->cfg->dev_addr, + wpabuf_head(resp), wpabuf_len(resp), + 50) < 0) + p2p_dbg(p2p, "Failed to send Action frame"); + else + p2p->send_action_in_progress = 1; + + wpabuf_free(resp); + } + + if (!dev) { + p2p_parse_free(&msg); + return; + } + + freq = 0; + if (reject == P2P_SC_SUCCESS && conncap == P2PS_SETUP_GROUP_OWNER) { + freq = p2p_channel_to_freq(p2p->op_reg_class, + p2p->op_channel); + if (freq < 0) + freq = 0; + } if (!p2p->cfg->p2ps_prov_complete) { /* Don't emit anything */ @@ -771,7 +1028,8 @@ out: NULL, adv_id, session_id, 0, 0, msg.persistent_ssid, msg.persistent_ssid_len, - 0, 0, NULL, NULL, 0); + 0, 0, NULL, NULL, 0, freq, + NULL, 0); } else if (msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED && p2p->p2ps_prov) { p2p->p2ps_prov->status = reject; @@ -784,7 +1042,8 @@ out: session_id, conncap, 0, msg.persistent_ssid, msg.persistent_ssid_len, 0, - 0, NULL, NULL, 0); + 0, NULL, NULL, 0, freq, + NULL, 0); else p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, *msg.status, @@ -796,7 +1055,8 @@ out: msg.persistent_ssid_len, 0, 0, NULL, (const u8 *) &resp_fcap, - sizeof(resp_fcap)); + sizeof(resp_fcap), freq, + NULL, 0); } else if (msg.status && p2p->p2ps_prov) { p2p->p2ps_prov->status = P2P_SC_SUCCESS; p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, *msg.status, sa, @@ -807,7 +1067,7 @@ out: msg.persistent_ssid_len, 0, 0, NULL, (const u8 *) &resp_fcap, - sizeof(resp_fcap)); + sizeof(resp_fcap), freq, NULL, 0); } else if (msg.status) { } else if (auto_accept && reject == P2P_SC_SUCCESS) { p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, P2P_SC_SUCCESS, @@ -818,7 +1078,11 @@ out: msg.persistent_ssid_len, 0, 0, NULL, (const u8 *) &resp_fcap, - sizeof(resp_fcap)); + sizeof(resp_fcap), freq, + msg.group_id ? + msg.group_id + ETH_ALEN : NULL, + msg.group_id ? + msg.group_id_len - ETH_ALEN : 0); } else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE && (!msg.session_info || !msg.session_info_len)) { p2p->p2ps_prov->method = msg.wps_config_methods; @@ -831,7 +1095,7 @@ out: msg.persistent_ssid_len, 0, 1, NULL, (const u8 *) &resp_fcap, - sizeof(resp_fcap)); + sizeof(resp_fcap), freq, NULL, 0); } else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) { size_t buf_len = msg.session_info_len; char *buf = os_malloc(2 * buf_len + 1); @@ -848,7 +1112,8 @@ out: session_id, conncap, passwd_id, msg.persistent_ssid, msg.persistent_ssid_len, 0, 1, buf, - (const u8 *) &resp_fcap, sizeof(resp_fcap)); + (const u8 *) &resp_fcap, sizeof(resp_fcap), + freq, NULL, 0); os_free(buf); } @@ -898,7 +1163,7 @@ out: msg.group_id, msg.group_id_len); } - if (dev && reject == P2P_SC_SUCCESS) { + if (reject == P2P_SC_SUCCESS) { switch (config_methods) { case WPS_CONFIG_DISPLAY: dev->wps_prov_info = WPS_CONFIG_KEYPAD; @@ -1084,6 +1349,9 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, " with no pending request", MAC2STR(sa)); p2p_parse_free(&msg); return; + } else if (msg.wfd_subelems) { + wpabuf_free(dev->info.wfd_subelems); + dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); } if (dev->dialog_token != msg.dialog_token) { @@ -1148,17 +1416,71 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, passwd_id = DEV_PW_P2PS_DEFAULT; } - if ((msg.conn_cap || msg.persistent_dev) && - (status == P2P_SC_SUCCESS || status == P2P_SC_SUCCESS_DEFERRED) && + if ((status == P2P_SC_SUCCESS || status == P2P_SC_SUCCESS_DEFERRED) && p2p->p2ps_prov) { + dev->oper_freq = 0; + + /* + * Save the reported channel list and operating frequency. + * Note that the specification mandates that the responder + * should include in the channel list only channels reported by + * the initiator, so this is only a sanity check, and if this + * fails the flow would continue, although it would probably + * fail. Same is true for the operating channel. + */ + if (msg.channel_list && msg.channel_list_len && + p2p_peer_channels_check(p2p, &p2p->channels, dev, + msg.channel_list, + msg.channel_list_len) < 0) + p2p_dbg(p2p, "P2PS PD Response - no common channels"); + + if (msg.operating_channel) { + if (p2p_channels_includes(&p2p->channels, + msg.operating_channel[3], + msg.operating_channel[4]) && + p2p_channels_includes(&dev->channels, + msg.operating_channel[3], + msg.operating_channel[4])) { + dev->oper_freq = + p2p_channel_to_freq( + msg.operating_channel[3], + msg.operating_channel[4]); + } else { + p2p_dbg(p2p, + "P2PS PD Response - invalid operating channel"); + } + } + if (p2p->cfg->p2ps_prov_complete) { + int freq = 0; + + if (conncap == P2PS_SETUP_GROUP_OWNER) { + u8 tmp; + + /* + * Re-select the operating channel as it is + * possible that original channel is no longer + * valid. This should not really fail. + */ + if (p2p_go_select_channel(p2p, dev, &tmp) < 0) + p2p_dbg(p2p, + "P2PS PD channel selection failed"); + + freq = p2p_channel_to_freq(p2p->op_reg_class, + p2p->op_channel); + if (freq < 0) + freq = 0; + } + p2p->cfg->p2ps_prov_complete( p2p->cfg->cb_ctx, status, sa, adv_mac, p2p->p2ps_prov->session_mac, group_mac, adv_id, p2p->p2ps_prov->session_id, conncap, passwd_id, msg.persistent_ssid, msg.persistent_ssid_len, 1, 0, NULL, - msg.feature_cap, msg.feature_cap_len); + msg.feature_cap, msg.feature_cap_len, freq, + msg.group_id ? msg.group_id + ETH_ALEN : NULL, + msg.group_id ? msg.group_id_len - ETH_ALEN : 0); } p2ps_prov_free(p2p); } else if (status != P2P_SC_SUCCESS && @@ -1169,7 +1491,7 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, p2p->cfg->cb_ctx, status, sa, adv_mac, p2p->p2ps_prov->session_mac, group_mac, adv_id, p2p->p2ps_prov->session_id, - 0, 0, NULL, 0, 1, 0, NULL, NULL, 0); + 0, 0, NULL, 0, 1, 0, NULL, NULL, 0, 0, NULL, 0); p2ps_prov_free(p2p); } @@ -1318,6 +1640,10 @@ int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev, "Building PD Request based on P2PS config method 0x%x status %d --> req_config_methods 0x%x", p2p->p2ps_prov->method, p2p->p2ps_prov->status, dev->req_config_methods); + + if (p2p_prepare_channel(p2p, dev, p2p->p2ps_prov->force_freq, + p2p->p2ps_prov->pref_freq, 1) < 0) + return -1; } req = p2p_build_prov_disc_req(p2p, dev, join); diff --git a/contrib/wpa/src/p2p/p2p_sd.c b/contrib/wpa/src/p2p/p2p_sd.c index 1a2af04b8004..a8bc5ba7f344 100644 --- a/contrib/wpa/src/p2p/p2p_sd.c +++ b/contrib/wpa/src/p2p/p2p_sd.c @@ -28,11 +28,11 @@ static int wfd_wsd_supported(struct wpabuf *wfd) pos = wpabuf_head(wfd); end = pos + wpabuf_len(wfd); - while (pos + 3 <= end) { + while (end - pos >= 3) { subelem = *pos++; len = WPA_GET_BE16(pos); pos += 2; - if (pos + len > end) + if (len > end - pos) break; if (subelem == WFD_SUBELEM_DEVICE_INFO && len >= 6) { @@ -288,6 +288,14 @@ int p2p_start_sd(struct p2p_data *p2p, struct p2p_device *dev) query = p2p_pending_sd_req(p2p, dev); if (query == NULL) return -1; + if (p2p->state == P2P_SEARCH && + os_memcmp(p2p->sd_query_no_ack, dev->info.p2p_device_addr, + ETH_ALEN) == 0) { + p2p_dbg(p2p, "Do not start Service Discovery with " MACSTR + " due to it being the first no-ACK peer in this search iteration", + MAC2STR(dev->info.p2p_device_addr)); + return -2; + } p2p_dbg(p2p, "Start Service Discovery with " MACSTR, MAC2STR(dev->info.p2p_device_addr)); @@ -355,11 +363,11 @@ void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa, pos++; slen = *pos++; - next = pos + slen; - if (next > end || slen < 2) { + if (slen > end - pos || slen < 2) { p2p_dbg(p2p, "Invalid IE in GAS Initial Request"); return; } + next = pos + slen; pos++; /* skip QueryRespLenLimit and PAME-BI */ if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { @@ -370,16 +378,16 @@ void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa, pos = next; /* Query Request */ - if (pos + 2 > end) + if (end - pos < 2) return; slen = WPA_GET_LE16(pos); pos += 2; - if (pos + slen > end) + if (slen > end - pos) return; end = pos + slen; /* ANQP Query Request */ - if (pos + 4 > end) + if (end - pos < 4) return; if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) { p2p_dbg(p2p, "Unsupported ANQP Info ID %u", WPA_GET_LE16(pos)); @@ -389,7 +397,7 @@ void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa, slen = WPA_GET_LE16(pos); pos += 2; - if (pos + slen > end || slen < 3 + 1) { + if (slen > end - pos || slen < 3 + 1) { p2p_dbg(p2p, "Invalid ANQP Query Request length"); return; } @@ -401,7 +409,7 @@ void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa, } pos += 4; - if (pos + 2 > end) + if (end - pos < 2) return; update_indic = WPA_GET_LE16(pos); p2p_dbg(p2p, "Service Update Indicator: %u", update_indic); @@ -417,9 +425,16 @@ void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst, u8 dialog_token, const struct wpabuf *resp_tlvs) { struct wpabuf *resp; + size_t max_len; + + /* + * In the 60 GHz, we have a smaller maximum frame length for management + * frames. + */ + max_len = (freq > 56160) ? 928 : 1400; /* TODO: fix the length limit to match with the maximum frame length */ - if (wpabuf_len(resp_tlvs) > 1400) { + if (wpabuf_len(resp_tlvs) > max_len) { p2p_dbg(p2p, "SD response long enough to require fragmentation"); if (p2p->sd_resp) { /* @@ -512,11 +527,11 @@ void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa, pos++; slen = *pos++; - next = pos + slen; - if (next > end || slen < 2) { + if (slen > end - pos || slen < 2) { p2p_dbg(p2p, "Invalid IE in GAS Initial Response"); return; } + next = pos + slen; pos++; /* skip QueryRespLenLimit and PAME-BI */ if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { @@ -527,14 +542,14 @@ void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa, pos = next; /* Query Response */ - if (pos + 2 > end) { + if (end - pos < 2) { p2p_dbg(p2p, "Too short Query Response"); return; } slen = WPA_GET_LE16(pos); pos += 2; p2p_dbg(p2p, "Query Response Length: %d", slen); - if (pos + slen > end) { + if (slen > end - pos) { p2p_dbg(p2p, "Not enough Query Response data"); return; } @@ -552,7 +567,7 @@ void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa, } /* ANQP Query Response */ - if (pos + 4 > end) + if (end - pos < 4) return; if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) { p2p_dbg(p2p, "Unsupported ANQP Info ID %u", WPA_GET_LE16(pos)); @@ -562,7 +577,7 @@ void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa, slen = WPA_GET_LE16(pos); pos += 2; - if (pos + slen > end || slen < 3 + 1) { + if (slen > end - pos || slen < 3 + 1) { p2p_dbg(p2p, "Invalid ANQP Query Response length"); return; } @@ -574,7 +589,7 @@ void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa, } pos += 4; - if (pos + 2 > end) + if (end - pos < 2) return; update_indic = WPA_GET_LE16(pos); p2p_dbg(p2p, "Service Update Indicator: %u", update_indic); @@ -606,7 +621,7 @@ void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa, { struct wpabuf *resp; u8 dialog_token; - size_t frag_len; + size_t frag_len, max_len; int more = 0; wpa_hexdump(MSG_DEBUG, "P2P: RX GAS Comeback Request", data, len); @@ -630,9 +645,14 @@ void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa, return; } + /* + * In the 60 GHz, we have a smaller maximum frame length for management + * frames. + */ + max_len = (rx_freq > 56160) ? 928 : 1400; frag_len = wpabuf_len(p2p->sd_resp) - p2p->sd_resp_pos; - if (frag_len > 1400) { - frag_len = 1400; + if (frag_len > max_len) { + frag_len = max_len; more = 1; } resp = p2p_build_gas_comeback_resp(dialog_token, WLAN_STATUS_SUCCESS, @@ -727,11 +747,11 @@ void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa, pos++; slen = *pos++; - next = pos + slen; - if (next > end || slen < 2) { + if (slen > end - pos || slen < 2) { p2p_dbg(p2p, "Invalid IE in GAS Comeback Response"); return; } + next = pos + slen; pos++; /* skip QueryRespLenLimit and PAME-BI */ if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { @@ -742,14 +762,14 @@ void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa, pos = next; /* Query Response */ - if (pos + 2 > end) { + if (end - pos < 2) { p2p_dbg(p2p, "Too short Query Response"); return; } slen = WPA_GET_LE16(pos); pos += 2; p2p_dbg(p2p, "Query Response Length: %d", slen); - if (pos + slen > end) { + if (slen > end - pos) { p2p_dbg(p2p, "Not enough Query Response data"); return; } @@ -768,7 +788,7 @@ void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa, } /* ANQP Query Response */ - if (pos + 4 > end) + if (end - pos < 4) return; if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) { p2p_dbg(p2p, "Unsupported ANQP Info ID %u", WPA_GET_LE16(pos)); @@ -783,7 +803,7 @@ void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa, p2p_dbg(p2p, "Invalid ANQP Query Response length"); return; } - if (pos + 4 > end) + if (end - pos < 4) return; if (WPA_GET_BE32(pos) != P2P_IE_VENDOR_TYPE) { @@ -793,7 +813,7 @@ void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa, } pos += 4; - if (pos + 2 > end) + if (end - pos < 2) return; p2p->sd_rx_update_indic = WPA_GET_LE16(pos); p2p_dbg(p2p, "Service Update Indicator: %u", p2p->sd_rx_update_indic); diff --git a/contrib/wpa/src/pae/ieee802_1x_cp.c b/contrib/wpa/src/pae/ieee802_1x_cp.c index cf43c594c402..e294e6466285 100644 --- a/contrib/wpa/src/pae/ieee802_1x_cp.c +++ b/contrib/wpa/src/pae/ieee802_1x_cp.c @@ -20,7 +20,7 @@ #define STATE_MACHINE_DATA struct ieee802_1x_cp_sm #define STATE_MACHINE_DEBUG_PREFIX "CP" -static u8 default_cs_id[] = CS_ID_GCM_AES_128; +static u64 default_cs_id = CS_ID_GCM_AES_128; /* The variable defined in clause 12 in IEEE Std 802.1X-2010 */ enum connect_type { PENDING, UNAUTHENTICATED, AUTHENTICATED, SECURE }; @@ -45,7 +45,7 @@ struct ieee802_1x_cp_sm { Boolean elected_self; u8 *authorization_data1; enum confidentiality_offset cipher_offset; - u8 *cipher_suite; + u64 cipher_suite; Boolean new_sak; /* clear by CP */ struct ieee802_1x_mka_ki distributed_ki; u8 distributed_an; @@ -71,7 +71,7 @@ struct ieee802_1x_cp_sm { Boolean replay_protect; u32 replay_window; - u8 *current_cipher_suite; + u64 current_cipher_suite; enum confidentiality_offset confidentiality_offset; Boolean controlled_port_enabled; @@ -97,8 +97,7 @@ static void ieee802_1x_cp_transmit_when_timeout(void *eloop_ctx, static int changed_cipher(struct ieee802_1x_cp_sm *sm) { return sm->confidentiality_offset != sm->cipher_offset || - os_memcmp(sm->current_cipher_suite, sm->cipher_suite, - CS_ID_LEN) != 0; + sm->current_cipher_suite != sm->cipher_suite; } @@ -185,21 +184,17 @@ SM_STATE(CP, AUTHENTICATED) SM_STATE(CP, SECURED) { - struct ieee802_1x_cp_conf conf; - SM_ENTRY(CP, SECURED); sm->chgd_server = FALSE; - ieee802_1x_kay_cp_conf(sm->kay, &conf); - sm->protect_frames = conf.protect; - sm->replay_protect = conf.replay_protect; - sm->validate_frames = conf.validate; + sm->protect_frames = sm->kay->macsec_protect; + sm->replay_protect = sm->kay->macsec_replay_protect; + sm->validate_frames = sm->kay->macsec_validate; - /* NOTE: now no other than default cipher suiter(AES-GCM-128) */ - os_memcpy(sm->current_cipher_suite, sm->cipher_suite, CS_ID_LEN); - secy_cp_control_current_cipher_suite(sm->kay, sm->current_cipher_suite, - CS_ID_LEN); + /* NOTE: now no other than default cipher suite (AES-GCM-128) */ + sm->current_cipher_suite = sm->cipher_suite; + secy_cp_control_current_cipher_suite(sm->kay, sm->current_cipher_suite); sm->confidentiality_offset = sm->cipher_offset; @@ -428,9 +423,7 @@ SM_STEP(CP) /** * ieee802_1x_cp_sm_init - */ -struct ieee802_1x_cp_sm * ieee802_1x_cp_sm_init( - struct ieee802_1x_kay *kay, - struct ieee802_1x_cp_conf *pcp_conf) +struct ieee802_1x_cp_sm * ieee802_1x_cp_sm_init(struct ieee802_1x_kay *kay) { struct ieee802_1x_cp_sm *sm; @@ -446,10 +439,10 @@ struct ieee802_1x_cp_sm * ieee802_1x_cp_sm_init( sm->chgd_server = FALSE; - sm->protect_frames = pcp_conf->protect; - sm->validate_frames = pcp_conf->validate; - sm->replay_protect = pcp_conf->replay_protect; - sm->replay_window = pcp_conf->replay_window; + sm->protect_frames = kay->macsec_protect; + sm->validate_frames = kay->macsec_validate; + sm->replay_protect = kay->macsec_replay_protect; + sm->replay_window = kay->macsec_replay_window; sm->controlled_port_enabled = FALSE; @@ -460,17 +453,8 @@ struct ieee802_1x_cp_sm * ieee802_1x_cp_sm_init( sm->orx = FALSE; sm->otx = FALSE; - sm->cipher_suite = os_zalloc(CS_ID_LEN); - sm->current_cipher_suite = os_zalloc(CS_ID_LEN); - if (!sm->cipher_suite || !sm->current_cipher_suite) { - wpa_printf(MSG_ERROR, "CP-%s: out of memory", __func__); - os_free(sm->cipher_suite); - os_free(sm->current_cipher_suite); - os_free(sm); - return NULL; - } - os_memcpy(sm->current_cipher_suite, default_cs_id, CS_ID_LEN); - os_memcpy(sm->cipher_suite, default_cs_id, CS_ID_LEN); + sm->current_cipher_suite = default_cs_id; + sm->cipher_suite = default_cs_id; sm->cipher_offset = CONFIDENTIALITY_OFFSET_0; sm->confidentiality_offset = sm->cipher_offset; sm->transmit_delay = MKA_LIFE_TIME; @@ -530,8 +514,6 @@ void ieee802_1x_cp_sm_deinit(struct ieee802_1x_cp_sm *sm) eloop_cancel_timeout(ieee802_1x_cp_step_cb, sm, NULL); os_free(sm->lki); os_free(sm->oki); - os_free(sm->cipher_suite); - os_free(sm->current_cipher_suite); os_free(sm->authorization_data); os_free(sm); } @@ -618,10 +600,10 @@ void ieee802_1x_cp_set_authorizationdata(void *cp_ctx, u8 *pdata, int len) /** * ieee802_1x_cp_set_ciphersuite - */ -void ieee802_1x_cp_set_ciphersuite(void *cp_ctx, void *pid) +void ieee802_1x_cp_set_ciphersuite(void *cp_ctx, u64 cs) { struct ieee802_1x_cp_sm *sm = cp_ctx; - os_memcpy(sm->cipher_suite, pid, CS_ID_LEN); + sm->cipher_suite = cs; } diff --git a/contrib/wpa/src/pae/ieee802_1x_cp.h b/contrib/wpa/src/pae/ieee802_1x_cp.h index 773c93052bf6..695629e5c0bc 100644 --- a/contrib/wpa/src/pae/ieee802_1x_cp.h +++ b/contrib/wpa/src/pae/ieee802_1x_cp.h @@ -16,17 +16,7 @@ struct ieee802_1x_cp_sm; struct ieee802_1x_kay; struct ieee802_1x_mka_ki; -struct ieee802_1x_cp_conf { - Boolean protect; - Boolean replay_protect; - enum validate_frames validate; - u32 replay_window; -}; - - -struct ieee802_1x_cp_sm * -ieee802_1x_cp_sm_init(struct ieee802_1x_kay *kay, - struct ieee802_1x_cp_conf *pcp_conf); +struct ieee802_1x_cp_sm * ieee802_1x_cp_sm_init(struct ieee802_1x_kay *kay); void ieee802_1x_cp_sm_deinit(struct ieee802_1x_cp_sm *sm); void ieee802_1x_cp_sm_step(void *cp_ctx); void ieee802_1x_cp_connect_pending(void *cp_ctx); @@ -36,7 +26,7 @@ void ieee802_1x_cp_connect_secure(void *cp_ctx); void ieee802_1x_cp_signal_chgdserver(void *cp_ctx); void ieee802_1x_cp_set_electedself(void *cp_ctx, Boolean status); void ieee802_1x_cp_set_authorizationdata(void *cp_ctx, u8 *pdata, int len); -void ieee802_1x_cp_set_ciphersuite(void *cp_ctx, void *pid); +void ieee802_1x_cp_set_ciphersuite(void *cp_ctx, u64 cs); void ieee802_1x_cp_set_offset(void *cp_ctx, enum confidentiality_offset offset); void ieee802_1x_cp_signal_newsak(void *cp_ctx); void ieee802_1x_cp_set_distributedki(void *cp_ctx, diff --git a/contrib/wpa/src/pae/ieee802_1x_kay.c b/contrib/wpa/src/pae/ieee802_1x_kay.c index ef744304a2bb..a8e7efc9b3bd 100644 --- a/contrib/wpa/src/pae/ieee802_1x_kay.c +++ b/contrib/wpa/src/pae/ieee802_1x_kay.c @@ -29,6 +29,8 @@ #define PENDING_PN_EXHAUSTION 0xC0000000 +#define MKA_ALIGN_LENGTH(len) (((len) + 0x3) & ~0x3) + /* IEEE Std 802.1X-2010, Table 9-1 - MKA Algorithm Agility */ #define MKA_ALGO_AGILITY_2009 { 0x00, 0x80, 0xC2, 0x01 } static u8 mka_algo_agility[4] = MKA_ALGO_AGILITY_2009; @@ -37,12 +39,11 @@ static u8 mka_algo_agility[4] = MKA_ALGO_AGILITY_2009; static struct macsec_ciphersuite cipher_suite_tbl[] = { /* GCM-AES-128 */ { - CS_ID_GCM_AES_128, - CS_NAME_GCM_AES_128, - MACSEC_CAP_INTEG_AND_CONF_0_30_50, - 16, - - 0 /* index */ + .id = CS_ID_GCM_AES_128, + .name = CS_NAME_GCM_AES_128, + .capable = MACSEC_CAP_INTEG_AND_CONF_0_30_50, + .sak_len = DEFAULT_SA_KEY_LEN, + .index = 0, }, }; #define CS_TABLE_SIZE (ARRAY_SIZE(cipher_suite_tbl)) @@ -50,16 +51,21 @@ static struct macsec_ciphersuite cipher_suite_tbl[] = { static struct mka_alg mka_alg_tbl[] = { { - MKA_ALGO_AGILITY_2009, - /* 128-bit CAK, KEK, ICK, ICV */ - 16, 16, 16, 16, - ieee802_1x_cak_128bits_aes_cmac, - ieee802_1x_ckn_128bits_aes_cmac, - ieee802_1x_kek_128bits_aes_cmac, - ieee802_1x_ick_128bits_aes_cmac, - ieee802_1x_icv_128bits_aes_cmac, + .parameter = MKA_ALGO_AGILITY_2009, - 1, /* index */ + /* 128-bit CAK, KEK, ICK, ICV */ + .cak_len = DEFAULT_ICV_LEN, + .kek_len = DEFAULT_ICV_LEN, + .ick_len = DEFAULT_ICV_LEN, + .icv_len = DEFAULT_ICV_LEN, + + .cak_trfm = ieee802_1x_cak_128bits_aes_cmac, + .ckn_trfm = ieee802_1x_ckn_128bits_aes_cmac, + .kek_trfm = ieee802_1x_kek_128bits_aes_cmac, + .ick_trfm = ieee802_1x_ick_128bits_aes_cmac, + .icv_hash = ieee802_1x_icv_128bits_aes_cmac, + + .index = 1, }, }; #define MKA_ALG_TABLE_SIZE (ARRAY_SIZE(mka_alg_tbl)) @@ -73,16 +79,6 @@ static int is_ki_equal(struct ieee802_1x_mka_ki *ki1, } -struct mka_param_body_handler { - int (*body_tx)(struct ieee802_1x_mka_participant *participant, - struct wpabuf *buf); - int (*body_rx)(struct ieee802_1x_mka_participant *participant, - const u8 *mka_msg, size_t msg_len); - int (*body_length)(struct ieee802_1x_mka_participant *participant); - Boolean (*body_present)(struct ieee802_1x_mka_participant *participant); -}; - - static void set_mka_param_body_len(void *body, unsigned int len) { struct ieee802_1x_mka_hdr *hdr = body; @@ -98,7 +94,7 @@ static unsigned int get_mka_param_body_len(const void *body) } -static int get_mka_param_body_type(const void *body) +static u8 get_mka_param_body_type(const void *body) { const struct ieee802_1x_mka_hdr *hdr = body; return hdr->type; @@ -122,8 +118,8 @@ ieee802_1x_mka_dump_basic_body(struct ieee802_1x_mka_basic_body *body) wpa_printf(MSG_DEBUG, "\tPriority......: %d", body->priority); wpa_printf(MSG_DEBUG, "\tKeySvr........: %d", body->key_server); wpa_printf(MSG_DEBUG, "\tMACSecDesired.: %d", body->macsec_desired); - wpa_printf(MSG_DEBUG, "\tMACSecCapable.: %d", body->macsec_capbility); - wpa_printf(MSG_DEBUG, "\tBody Length...: %d", (int) body_len); + wpa_printf(MSG_DEBUG, "\tMACSecCapable.: %d", body->macsec_capability); + wpa_printf(MSG_DEBUG, "\tBody Length...: %zu", body_len); wpa_printf(MSG_DEBUG, "\tSCI MAC.......: " MACSTR, MAC2STR(body->actor_sci.addr)); wpa_printf(MSG_DEBUG, "\tSCI Port .....: %d", @@ -148,7 +144,7 @@ ieee802_1x_mka_dump_peer_body(struct ieee802_1x_mka_peer_body *body) size_t body_len; size_t i; u8 *mi; - u32 mn; + be32 mn; if (body == NULL) return; @@ -156,10 +152,10 @@ ieee802_1x_mka_dump_peer_body(struct ieee802_1x_mka_peer_body *body) body_len = get_mka_param_body_len(body); if (body->type == MKA_LIVE_PEER_LIST) { wpa_printf(MSG_DEBUG, "*** Live Peer List ***"); - wpa_printf(MSG_DEBUG, "\tBody Length...: %d", (int) body_len); + wpa_printf(MSG_DEBUG, "\tBody Length...: %zu", body_len); } else if (body->type == MKA_POTENTIAL_PEER_LIST) { wpa_printf(MSG_DEBUG, "*** Potential Live Peer List ***"); - wpa_printf(MSG_DEBUG, "\tBody Length...: %d", (int) body_len); + wpa_printf(MSG_DEBUG, "\tBody Length...: %zu", body_len); } for (i = 0; i < body_len; i += MI_LEN + sizeof(mn)) { @@ -187,7 +183,7 @@ ieee802_1x_mka_dump_dist_sak_body(struct ieee802_1x_mka_dist_sak_body *body) wpa_printf(MSG_INFO, "\tDistributed AN........: %d", body->dan); wpa_printf(MSG_INFO, "\tConfidentiality Offset: %d", body->confid_offset); - wpa_printf(MSG_INFO, "\tBody Length...........: %d", (int) body_len); + wpa_printf(MSG_INFO, "\tBody Length...........: %zu", body_len); if (!body_len) return; @@ -280,7 +276,7 @@ ieee802_1x_kay_get_principal_participant(struct ieee802_1x_kay *kay) return participant; } - wpa_printf(MSG_DEBUG, "KaY: principal participant is not founded"); + wpa_printf(MSG_DEBUG, "KaY: principal participant is not found"); return NULL; } @@ -300,52 +296,12 @@ static struct ieee802_1x_kay_peer * get_peer_mi(struct dl_list *peers, /** - * ieee802_1x_kay_is_in_potential_peer - */ -static Boolean -ieee802_1x_kay_is_in_potential_peer( - struct ieee802_1x_mka_participant *participant, const u8 *mi) -{ - return get_peer_mi(&participant->potential_peers, mi) != NULL; -} - - -/** - * ieee802_1x_kay_is_in_live_peer - */ -static Boolean -ieee802_1x_kay_is_in_live_peer( - struct ieee802_1x_mka_participant *participant, const u8 *mi) -{ - return get_peer_mi(&participant->live_peers, mi) != NULL; -} - - -/** - * ieee802_1x_kay_is_in_peer - */ -static Boolean -ieee802_1x_kay_is_in_peer(struct ieee802_1x_mka_participant *participant, - const u8 *mi) -{ - return ieee802_1x_kay_is_in_live_peer(participant, mi) || - ieee802_1x_kay_is_in_potential_peer(participant, mi); -} - - -/** - * ieee802_1x_kay_get_peer + * ieee802_1x_kay_get_potential_peer */ static struct ieee802_1x_kay_peer * -ieee802_1x_kay_get_peer(struct ieee802_1x_mka_participant *participant, - const u8 *mi) +ieee802_1x_kay_get_potential_peer( + struct ieee802_1x_mka_participant *participant, const u8 *mi) { - struct ieee802_1x_kay_peer *peer; - - peer = get_peer_mi(&participant->live_peers, mi); - if (peer) - return peer; - return get_peer_mi(&participant->potential_peers, mi); } @@ -361,23 +317,72 @@ ieee802_1x_kay_get_live_peer(struct ieee802_1x_mka_participant *participant, } +/** + * ieee802_1x_kay_is_in_potential_peer + */ +static Boolean +ieee802_1x_kay_is_in_potential_peer( + struct ieee802_1x_mka_participant *participant, const u8 *mi) +{ + return ieee802_1x_kay_get_potential_peer(participant, mi) != NULL; +} + + +/** + * ieee802_1x_kay_is_in_live_peer + */ +static Boolean +ieee802_1x_kay_is_in_live_peer( + struct ieee802_1x_mka_participant *participant, const u8 *mi) +{ + return ieee802_1x_kay_get_live_peer(participant, mi) != NULL; +} + + +/** + * ieee802_1x_kay_get_peer + */ +static struct ieee802_1x_kay_peer * +ieee802_1x_kay_get_peer(struct ieee802_1x_mka_participant *participant, + const u8 *mi) +{ + struct ieee802_1x_kay_peer *peer; + + peer = ieee802_1x_kay_get_live_peer(participant, mi); + if (peer) + return peer; + + return ieee802_1x_kay_get_potential_peer(participant, mi); +} + + /** * ieee802_1x_kay_get_cipher_suite */ static struct macsec_ciphersuite * ieee802_1x_kay_get_cipher_suite(struct ieee802_1x_mka_participant *participant, - u8 *cs_id) + const u8 *cs_id) { unsigned int i; + u64 cs; + be64 _cs; + + os_memcpy(&_cs, cs_id, CS_ID_LEN); + cs = be_to_host64(_cs); for (i = 0; i < CS_TABLE_SIZE; i++) { - if (os_memcmp(cipher_suite_tbl[i].id, cs_id, CS_ID_LEN) == 0) - break; + if (cipher_suite_tbl[i].id == cs) + return &cipher_suite_tbl[i]; } - if (i >= CS_TABLE_SIZE) - return NULL; - return &cipher_suite_tbl[i]; + return NULL; +} + + +static Boolean sci_equal(const struct ieee802_1x_mka_sci *a, + const struct ieee802_1x_mka_sci *b) +{ + return os_memcmp(a, b, sizeof(struct ieee802_1x_mka_sci)) == 0; } @@ -392,13 +397,13 @@ ieee802_1x_kay_get_peer_sci(struct ieee802_1x_mka_participant *participant, dl_list_for_each(peer, &participant->live_peers, struct ieee802_1x_kay_peer, list) { - if (os_memcmp(&peer->sci, sci, sizeof(peer->sci)) == 0) + if (sci_equal(&peer->sci, sci)) return peer; } dl_list_for_each(peer, &participant->potential_peers, struct ieee802_1x_kay_peer, list) { - if (os_memcmp(&peer->sci, sci, sizeof(peer->sci)) == 0) + if (sci_equal(&peer->sci, sci)) return peer; } @@ -435,8 +440,8 @@ ieee802_1x_kay_init_receive_sa(struct receive_sc *psc, u8 an, u32 lowest_pn, dl_list_add(&psc->sa_list, &psa->list); wpa_printf(MSG_DEBUG, - "KaY: Create receive SA(AN: %d lowest_pn: %u of SC(channel: %d)", - (int) an, lowest_pn, psc->channel); + "KaY: Create receive SA(AN: %hhu lowest_pn: %u of SC(channel: %d)", + an, lowest_pn, psc->channel); return psa; } @@ -449,8 +454,8 @@ static void ieee802_1x_kay_deinit_receive_sa(struct receive_sa *psa) { psa->pkey = NULL; wpa_printf(MSG_DEBUG, - "KaY: Delete receive SA(an: %d) of SC(channel: %d)", - psa->an, psa->sc->channel); + "KaY: Delete receive SA(an: %hhu) of SC", + psa->an); dl_list_del(&psa->list); os_free(psa); } @@ -509,19 +514,22 @@ ieee802_1x_kay_deinit_receive_sc( } -/** - * ieee802_1x_kay_create_live_peer - */ +static void ieee802_1x_kay_dump_peer(struct ieee802_1x_kay_peer *peer) +{ + wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi, sizeof(peer->mi)); + wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn); + wpa_hexdump(MSG_DEBUG, "\tSCI Addr: ", peer->sci.addr, ETH_ALEN); + wpa_printf(MSG_DEBUG, "\tPort: %d", peer->sci.port); +} + + static struct ieee802_1x_kay_peer * -ieee802_1x_kay_create_live_peer(struct ieee802_1x_mka_participant *participant, - u8 *mi, u32 mn) +ieee802_1x_kay_create_peer(const u8 *mi, u32 mn) { struct ieee802_1x_kay_peer *peer; - struct receive_sc *rxsc; - u32 sc_ch = 0; peer = os_zalloc(sizeof(*peer)); - if (peer == NULL) { + if (!peer) { wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__); return NULL; } @@ -530,24 +538,43 @@ ieee802_1x_kay_create_live_peer(struct ieee802_1x_mka_participant *participant, peer->mn = mn; peer->expire = time(NULL) + MKA_LIFE_TIME / 1000; peer->sak_used = FALSE; + + return peer; +} + + +/** + * ieee802_1x_kay_create_live_peer + */ +static struct ieee802_1x_kay_peer * +ieee802_1x_kay_create_live_peer(struct ieee802_1x_mka_participant *participant, + const u8 *mi, u32 mn) +{ + struct ieee802_1x_kay_peer *peer; + struct receive_sc *rxsc; + u32 sc_ch = 0; + + peer = ieee802_1x_kay_create_peer(mi, mn); + if (!peer) + return NULL; + os_memcpy(&peer->sci, &participant->current_peer_sci, sizeof(peer->sci)); - dl_list_add(&participant->live_peers, &peer->list); secy_get_available_receive_sc(participant->kay, &sc_ch); rxsc = ieee802_1x_kay_init_receive_sc(&peer->sci, sc_ch); - if (!rxsc) + if (!rxsc) { + os_free(peer); return NULL; + } + dl_list_add(&participant->live_peers, &peer->list); dl_list_add(&participant->rxsc_list, &rxsc->list); secy_create_receive_sc(participant->kay, rxsc); wpa_printf(MSG_DEBUG, "KaY: Live peer created"); - wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi, sizeof(peer->mi)); - wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn); - wpa_hexdump(MSG_DEBUG, "\tSCI Addr: ", peer->sci.addr, ETH_ALEN); - wpa_printf(MSG_DEBUG, "\tPort: %d", peer->sci.port); + ieee802_1x_kay_dump_peer(peer); return peer; } @@ -562,24 +589,14 @@ ieee802_1x_kay_create_potential_peer( { struct ieee802_1x_kay_peer *peer; - peer = os_zalloc(sizeof(*peer)); - if (peer == NULL) { - wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__); + peer = ieee802_1x_kay_create_peer(mi, mn); + if (!peer) return NULL; - } - - os_memcpy(peer->mi, mi, MI_LEN); - peer->mn = mn; - peer->expire = time(NULL) + MKA_LIFE_TIME / 1000; - peer->sak_used = FALSE; dl_list_add(&participant->potential_peers, &peer->list); wpa_printf(MSG_DEBUG, "KaY: potential peer created"); - wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi, sizeof(peer->mi)); - wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn); - wpa_hexdump(MSG_DEBUG, "\tSCI Addr: ", peer->sci.addr, ETH_ALEN); - wpa_printf(MSG_DEBUG, "\tPort: %d", peer->sci.port); + ieee802_1x_kay_dump_peer(peer); return peer; } @@ -596,11 +613,12 @@ ieee802_1x_kay_move_live_peer(struct ieee802_1x_mka_participant *participant, struct receive_sc *rxsc; u32 sc_ch = 0; - dl_list_for_each(peer, &participant->potential_peers, - struct ieee802_1x_kay_peer, list) { - if (os_memcmp(peer->mi, mi, MI_LEN) == 0) - break; - } + peer = ieee802_1x_kay_get_potential_peer(participant, mi); + + rxsc = ieee802_1x_kay_init_receive_sc(&participant->current_peer_sci, + sc_ch); + if (!rxsc) + return NULL; os_memcpy(&peer->sci, &participant->current_peer_sci, sizeof(peer->sci)); @@ -608,20 +626,13 @@ ieee802_1x_kay_move_live_peer(struct ieee802_1x_mka_participant *participant, peer->expire = time(NULL) + MKA_LIFE_TIME / 1000; wpa_printf(MSG_DEBUG, "KaY: move potential peer to live peer"); - wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi, sizeof(peer->mi)); - wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn); - wpa_hexdump(MSG_DEBUG, "\tSCI Addr: ", peer->sci.addr, ETH_ALEN); - wpa_printf(MSG_DEBUG, "\tPort: %d", peer->sci.port); + ieee802_1x_kay_dump_peer(peer); dl_list_del(&peer->list); dl_list_add_tail(&participant->live_peers, &peer->list); secy_get_available_receive_sc(participant->kay, &sc_ch); - rxsc = ieee802_1x_kay_init_receive_sc(&peer->sci, sc_ch); - if (!rxsc) - return NULL; - dl_list_add(&participant->rxsc_list, &rxsc->list); secy_create_receive_sc(participant->kay, rxsc); @@ -651,7 +662,7 @@ ieee802_1x_mka_basic_body_length(struct ieee802_1x_mka_participant *participant) length = sizeof(struct ieee802_1x_mka_basic_body); length += participant->ckn.len; - return (length + 0x3) & ~0x3; + return MKA_ALIGN_LENGTH(length); } @@ -677,17 +688,17 @@ ieee802_1x_mka_encode_basic_body( body->key_server = participant->can_be_key_server; body->macsec_desired = kay->macsec_desired; - body->macsec_capbility = kay->macsec_capable; + body->macsec_capability = kay->macsec_capable; set_mka_param_body_len(body, length - MKA_HDR_LEN); os_memcpy(body->actor_sci.addr, kay->actor_sci.addr, sizeof(kay->actor_sci.addr)); - body->actor_sci.port = host_to_be16(kay->actor_sci.port); + body->actor_sci.port = kay->actor_sci.port; os_memcpy(body->actor_mi, participant->mi, sizeof(body->actor_mi)); participant->mn = participant->mn + 1; body->actor_mn = host_to_be32(participant->mn); - os_memcpy(body->algo_agility, participant->kay->algo_agility, + os_memcpy(body->algo_agility, kay->algo_agility, sizeof(body->algo_agility)); os_memcpy(body->ckn, participant->ckn.name, participant->ckn.len); @@ -698,6 +709,17 @@ ieee802_1x_mka_encode_basic_body( } +static Boolean +reset_participant_mi(struct ieee802_1x_mka_participant *participant) +{ + if (os_get_random(participant->mi, sizeof(participant->mi)) < 0) + return FALSE; + participant->mn = 0; + + return TRUE; +} + + /** * ieee802_1x_mka_decode_basic_body - */ @@ -729,16 +751,15 @@ ieee802_1x_mka_decode_basic_body(struct ieee802_1x_kay *kay, const u8 *mka_msg, /* If the peer's MI is my MI, I will choose new MI */ if (os_memcmp(body->actor_mi, participant->mi, MI_LEN) == 0) { - if (os_get_random(participant->mi, sizeof(participant->mi)) < 0) + if (!reset_participant_mi(participant)) return NULL; - participant->mn = 0; } os_memcpy(participant->current_peer_id.mi, body->actor_mi, MI_LEN); - participant->current_peer_id.mn = be_to_host32(body->actor_mn); + participant->current_peer_id.mn = body->actor_mn; os_memcpy(participant->current_peer_sci.addr, body->actor_sci.addr, sizeof(participant->current_peer_sci.addr)); - participant->current_peer_sci.port = be_to_host16(body->actor_sci.port); + participant->current_peer_sci.port = body->actor_sci.port; /* handler peer */ peer = ieee802_1x_kay_get_peer(participant, body->actor_mi); @@ -763,14 +784,14 @@ ieee802_1x_mka_decode_basic_body(struct ieee802_1x_kay *kay, const u8 *mka_msg, return NULL; peer->macsec_desired = body->macsec_desired; - peer->macsec_capbility = body->macsec_capbility; + peer->macsec_capability = body->macsec_capability; peer->is_key_server = (Boolean) body->key_server; peer->key_server_priority = body->priority; } else if (peer->mn < be_to_host32(body->actor_mn)) { peer->mn = be_to_host32(body->actor_mn); peer->expire = time(NULL) + MKA_LIFE_TIME / 1000; peer->macsec_desired = body->macsec_desired; - peer->macsec_capbility = body->macsec_capbility; + peer->macsec_capability = body->macsec_capability; peer->is_key_server = (Boolean) body->key_server; peer->key_server_priority = body->priority; } else { @@ -807,7 +828,7 @@ ieee802_1x_mka_get_live_peer_length( struct ieee802_1x_kay_peer, list) len += sizeof(struct ieee802_1x_mka_peer_id); - return (len + 0x3) & ~0x3; + return MKA_ALIGN_LENGTH(len); } @@ -836,7 +857,6 @@ ieee802_1x_mka_encode_live_peer_body( sizeof(struct ieee802_1x_mka_peer_id)); os_memcpy(body_peer->mi, peer->mi, MI_LEN); body_peer->mn = host_to_be32(peer->mn); - body_peer++; } ieee802_1x_mka_dump_peer_body(body); @@ -868,7 +888,7 @@ ieee802_1x_mka_get_potential_peer_length( struct ieee802_1x_kay_peer, list) len += sizeof(struct ieee802_1x_mka_peer_id); - return (len + 0x3) & ~0x3; + return MKA_ALIGN_LENGTH(len); } @@ -897,7 +917,6 @@ ieee802_1x_mka_encode_potential_peer_body( sizeof(struct ieee802_1x_mka_peer_id)); os_memcpy(body_peer->mi, peer->mi, MI_LEN); body_peer->mn = host_to_be32(peer->mn); - body_peer++; } ieee802_1x_mka_dump_peer_body(body); @@ -912,62 +931,54 @@ static Boolean ieee802_1x_mka_i_in_peerlist(struct ieee802_1x_mka_participant *participant, const u8 *mka_msg, size_t msg_len) { - Boolean included = FALSE; struct ieee802_1x_mka_hdr *hdr; size_t body_len; size_t left_len; - int body_type; - u32 peer_mn; - const u8 *peer_mi; + u8 body_type; const u8 *pos; size_t i; - pos = mka_msg; - left_len = msg_len; - while (left_len > (MKA_HDR_LEN + DEFAULT_ICV_LEN)) { + for (pos = mka_msg, left_len = msg_len; + left_len > MKA_HDR_LEN + DEFAULT_ICV_LEN; + left_len -= body_len + MKA_HDR_LEN, + pos += body_len + MKA_HDR_LEN) { hdr = (struct ieee802_1x_mka_hdr *) pos; body_len = get_mka_param_body_len(hdr); body_type = get_mka_param_body_type(hdr); if (body_type != MKA_LIVE_PEER_LIST && body_type != MKA_POTENTIAL_PEER_LIST) - goto SKIP_PEER; + continue; ieee802_1x_mka_dump_peer_body( (struct ieee802_1x_mka_peer_body *)pos); if (left_len < (MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN)) { wpa_printf(MSG_ERROR, - "KaY: MKA Peer Packet Body Length (%d bytes) is less than the Parameter Set Header Length (%d bytes) + the Parameter Set Body Length (%d bytes) + %d bytes of ICV", - (int) left_len, (int) MKA_HDR_LEN, - (int) body_len, DEFAULT_ICV_LEN); - goto SKIP_PEER; + "KaY: MKA Peer Packet Body Length (%zu bytes) is less than the Parameter Set Header Length (%zu bytes) + the Parameter Set Body Length (%zu bytes) + %d bytes of ICV", + left_len, MKA_HDR_LEN, + body_len, DEFAULT_ICV_LEN); + continue; } if ((body_len % 16) != 0) { wpa_printf(MSG_ERROR, - "KaY: MKA Peer Packet Body Length (%d bytes) should multiple of 16 octets", - (int) body_len); - goto SKIP_PEER; + "KaY: MKA Peer Packet Body Length (%zu bytes) should be a multiple of 16 octets", + body_len); + continue; } - for (i = 0; i < body_len; i += MI_LEN + sizeof(peer_mn)) { - peer_mi = MKA_HDR_LEN + pos + i; - os_memcpy(&peer_mn, peer_mi + MI_LEN, sizeof(peer_mn)); - peer_mn = be_to_host32(peer_mn); - if (os_memcmp(peer_mi, participant->mi, MI_LEN) == 0 && - peer_mn == participant->mn) { - included = TRUE; - break; - } + for (i = 0; i < body_len; + i += sizeof(struct ieee802_1x_mka_peer_id)) { + const struct ieee802_1x_mka_peer_id *peer_mi; + + peer_mi = (const struct ieee802_1x_mka_peer_id *) + (pos + MKA_HDR_LEN + i); + if (os_memcmp(peer_mi->mi, participant->mi, + MI_LEN) == 0 && + be_to_host32(peer_mi->mn) == participant->mn) + return TRUE; } - - if (included) - return TRUE; - -SKIP_PEER: - left_len -= body_len + MKA_HDR_LEN; - pos += body_len + MKA_HDR_LEN; } return FALSE; @@ -984,8 +995,6 @@ static int ieee802_1x_mka_decode_live_peer_body( const struct ieee802_1x_mka_hdr *hdr; struct ieee802_1x_kay_peer *peer; size_t body_len; - u32 peer_mn; - const u8 *peer_mi; size_t i; Boolean is_included; @@ -994,36 +1003,40 @@ static int ieee802_1x_mka_decode_live_peer_body( hdr = (const struct ieee802_1x_mka_hdr *) peer_msg; body_len = get_mka_param_body_len(hdr); + if (body_len % 16 != 0) { + wpa_printf(MSG_ERROR, + "KaY: MKA Peer Packet Body Length (%zu bytes) should be a multiple of 16 octets", + body_len); + return -1; + } - for (i = 0; i < body_len; i += MI_LEN + sizeof(peer_mn)) { - peer_mi = MKA_HDR_LEN + peer_msg + i; - os_memcpy(&peer_mn, peer_mi + MI_LEN, sizeof(peer_mn)); - peer_mn = be_to_host32(peer_mn); + for (i = 0; i < body_len; i += sizeof(struct ieee802_1x_mka_peer_id)) { + const struct ieee802_1x_mka_peer_id *peer_mi; + u32 peer_mn; + + peer_mi = (const struct ieee802_1x_mka_peer_id *) + (peer_msg + MKA_HDR_LEN + i); + peer_mn = be_to_host32(peer_mi->mn); /* it is myself */ if (os_memcmp(peer_mi, participant->mi, MI_LEN) == 0) { /* My message id is used by other participant */ - if (peer_mn > participant->mn) { - if (os_get_random(participant->mi, - sizeof(participant->mi)) < 0) - wpa_printf(MSG_DEBUG, - "KaY: Could not update mi"); - participant->mn = 0; - } + if (peer_mn > participant->mn && + !reset_participant_mi(participant)) + wpa_printf(MSG_DEBUG, "KaY: Could not update mi"); continue; } + if (!is_included) continue; - peer = ieee802_1x_kay_get_peer(participant, peer_mi); - if (NULL != peer) { + peer = ieee802_1x_kay_get_peer(participant, peer_mi->mi); + if (peer) { peer->mn = peer_mn; peer->expire = time(NULL) + MKA_LIFE_TIME / 1000; - } else { - if (!ieee802_1x_kay_create_potential_peer( - participant, peer_mi, peer_mn)) { - return -1; - } + } else if (!ieee802_1x_kay_create_potential_peer( + participant, peer_mi->mi, peer_mn)) { + return -1; } } @@ -1039,30 +1052,33 @@ ieee802_1x_mka_decode_potential_peer_body( struct ieee802_1x_mka_participant *participant, const u8 *peer_msg, size_t msg_len) { - struct ieee802_1x_mka_hdr *hdr; + const struct ieee802_1x_mka_hdr *hdr; size_t body_len; - u32 peer_mn; - const u8 *peer_mi; size_t i; - hdr = (struct ieee802_1x_mka_hdr *) peer_msg; + hdr = (const struct ieee802_1x_mka_hdr *) peer_msg; body_len = get_mka_param_body_len(hdr); + if (body_len % 16 != 0) { + wpa_printf(MSG_ERROR, + "KaY: MKA Peer Packet Body Length (%zu bytes) should be a multiple of 16 octets", + body_len); + return -1; + } - for (i = 0; i < body_len; i += MI_LEN + sizeof(peer_mn)) { - peer_mi = MKA_HDR_LEN + peer_msg + i; - os_memcpy(&peer_mn, peer_mi + MI_LEN, sizeof(peer_mn)); - peer_mn = be_to_host32(peer_mn); + for (i = 0; i < body_len; i += sizeof(struct ieee802_1x_mka_peer_id)) { + const struct ieee802_1x_mka_peer_id *peer_mi; + u32 peer_mn; + + peer_mi = (struct ieee802_1x_mka_peer_id *) + (peer_msg + MKA_HDR_LEN + i); + peer_mn = be_to_host32(peer_mi->mn); /* it is myself */ if (os_memcmp(peer_mi, participant->mi, MI_LEN) == 0) { /* My message id is used by other participant */ - if (peer_mn > participant->mn) { - if (os_get_random(participant->mi, - sizeof(participant->mi)) < 0) - wpa_printf(MSG_DEBUG, - "KaY: Could not update mi"); - participant->mn = 0; - } + if (peer_mn > participant->mn && + !reset_participant_mi(participant)) + wpa_printf(MSG_DEBUG, "KaY: Could not update mi"); continue; } } @@ -1078,10 +1094,7 @@ static Boolean ieee802_1x_mka_sak_use_body_present( struct ieee802_1x_mka_participant *participant) { - if (participant->to_use_sak) - return TRUE; - else - return FALSE; + return participant->to_use_sak; } @@ -1096,12 +1109,8 @@ ieee802_1x_mka_get_sak_use_length( if (participant->kay->macsec_desired && participant->advised_desired) length = sizeof(struct ieee802_1x_mka_sak_use_body); - else - length = MKA_HDR_LEN; - length = (length + 0x3) & ~0x3; - - return length; + return MKA_ALIGN_LENGTH(length); } @@ -1146,11 +1155,12 @@ ieee802_1x_mka_encode_sak_use_body( struct wpabuf *buf) { struct ieee802_1x_mka_sak_use_body *body; + struct ieee802_1x_kay *kay = participant->kay; unsigned int length; u32 pn = 1; length = ieee802_1x_mka_get_sak_use_length(participant); - body = wpabuf_put(buf, sizeof(struct ieee802_1x_mka_sak_use_body)); + body = wpabuf_put(buf, length); body->type = MKA_SAK_USE; set_mka_param_body_len(body, length - MKA_HDR_LEN); @@ -1166,9 +1176,9 @@ ieee802_1x_mka_encode_sak_use_body( } /* data protect, lowest accept packet number */ - body->delay_protect = participant->kay->macsec_replay_protect; + body->delay_protect = kay->macsec_replay_protect; pn = ieee802_1x_mka_get_lpn(participant, &participant->lki); - if (pn > participant->kay->pn_exhaustion) { + if (pn > kay->pn_exhaustion) { wpa_printf(MSG_WARNING, "KaY: My LPN exhaustion"); if (participant->is_key_server) participant->new_sak = TRUE; @@ -1179,20 +1189,12 @@ ieee802_1x_mka_encode_sak_use_body( body->olpn = host_to_be32(pn); /* plain tx, plain rx */ - if (participant->kay->macsec_protect) - body->ptx = FALSE; - else - body->ptx = TRUE; - - if (participant->kay->macsec_validate == Strict) - body->prx = FALSE; - else - body->prx = TRUE; + body->ptx = !kay->macsec_protect; + body->prx = kay->macsec_validate != Strict; /* latest key: rx, tx, key server member identifier key number */ body->lan = participant->lan; - os_memcpy(body->lsrv_mi, participant->lki.mi, - sizeof(body->lsrv_mi)); + os_memcpy(body->lsrv_mi, participant->lki.mi, sizeof(body->lsrv_mi)); body->lkn = host_to_be32(participant->lki.kn); body->lrx = participant->lrx; body->ltx = participant->ltx; @@ -1213,16 +1215,11 @@ ieee802_1x_mka_encode_sak_use_body( /* set CP's variable */ if (body->ltx) { - if (!participant->kay->tx_enable) - participant->kay->tx_enable = TRUE; - - if (!participant->kay->port_enable) - participant->kay->port_enable = TRUE; - } - if (body->lrx) { - if (!participant->kay->rx_enable) - participant->kay->rx_enable = TRUE; + kay->tx_enable = TRUE; + kay->port_enable = TRUE; } + if (body->lrx) + kay->rx_enable = TRUE; ieee802_1x_mka_dump_sak_use_body(body); return 0; @@ -1246,7 +1243,8 @@ ieee802_1x_mka_decode_sak_use_body( struct ieee802_1x_mka_ki ki; u32 lpn; Boolean all_receiving; - Boolean founded; + Boolean found; + struct ieee802_1x_kay *kay = participant->kay; if (!participant->principal) { wpa_printf(MSG_WARNING, "KaY: Participant is not principal"); @@ -1266,8 +1264,8 @@ ieee802_1x_mka_decode_sak_use_body( if ((body_len != 0) && (body_len < 40)) { wpa_printf(MSG_ERROR, - "KaY: MKA Use SAK Packet Body Length (%d bytes) should be 0, 40, or more octets", - (int) body_len); + "KaY: MKA Use SAK Packet Body Length (%zu bytes) should be 0, 40, or more octets", + body_len); return -1; } @@ -1288,30 +1286,29 @@ ieee802_1x_mka_decode_sak_use_body( /* check latest key is valid */ if (body->ltx || body->lrx) { - founded = FALSE; + found = FALSE; os_memcpy(ki.mi, body->lsrv_mi, sizeof(ki.mi)); - ki.kn = ntohl(body->lkn); + ki.kn = be_to_host32(body->lkn); dl_list_for_each(sa_key, &participant->sak_list, struct data_key, list) { if (is_ki_equal(&sa_key->key_identifier, &ki)) { - founded = TRUE; + found = TRUE; break; } } - if (!founded) { + if (!found) { wpa_printf(MSG_WARNING, "KaY: Latest key is invalid"); return -1; } if (os_memcmp(participant->lki.mi, body->lsrv_mi, sizeof(participant->lki.mi)) == 0 && - ntohl(body->lkn) == participant->lki.kn && + be_to_host32(body->lkn) == participant->lki.kn && body->lan == participant->lan) { peer->sak_used = TRUE; } if (body->ltx && peer->is_key_server) { - ieee802_1x_cp_set_servertransmitting( - participant->kay->cp, TRUE); - ieee802_1x_cp_sm_step(participant->kay->cp); + ieee802_1x_cp_set_servertransmitting(kay->cp, TRUE); + ieee802_1x_cp_sm_step(kay->cp); } } @@ -1319,7 +1316,7 @@ ieee802_1x_mka_decode_sak_use_body( if (body->otx || body->orx) { if (os_memcmp(participant->oki.mi, body->osrv_mi, sizeof(participant->oki.mi)) != 0 || - ntohl(body->okn) != participant->oki.kn || + be_to_host32(body->okn) != participant->oki.kn || body->oan != participant->oan) { wpa_printf(MSG_WARNING, "KaY: Old key is invalid"); return -1; @@ -1327,7 +1324,8 @@ ieee802_1x_mka_decode_sak_use_body( } /* TODO: how to set the MACsec hardware when delay_protect is true */ - if (body->delay_protect && (!ntohl(body->llpn) || !ntohl(body->olpn))) { + if (body->delay_protect && + (!be_to_host32(body->llpn) || !be_to_host32(body->olpn))) { wpa_printf(MSG_WARNING, "KaY: Lowest packet number should greater than 0 when delay_protect is TRUE"); return -1; @@ -1344,28 +1342,28 @@ ieee802_1x_mka_decode_sak_use_body( } if (all_receiving) { participant->to_dist_sak = FALSE; - ieee802_1x_cp_set_allreceiving(participant->kay->cp, TRUE); - ieee802_1x_cp_sm_step(participant->kay->cp); + ieee802_1x_cp_set_allreceiving(kay->cp, TRUE); + ieee802_1x_cp_sm_step(kay->cp); } /* if i'm key server, and detects peer member pn exhaustion, rekey.*/ - lpn = ntohl(body->llpn); - if (lpn > participant->kay->pn_exhaustion) { + lpn = be_to_host32(body->llpn); + if (lpn > kay->pn_exhaustion) { if (participant->is_key_server) { participant->new_sak = TRUE; wpa_printf(MSG_WARNING, "KaY: Peer LPN exhaustion"); } } - founded = FALSE; + found = FALSE; dl_list_for_each(txsa, &participant->txsc->sa_list, struct transmit_sa, list) { if (sa_key != NULL && txsa->pkey == sa_key) { - founded = TRUE; + found = TRUE; break; } } - if (!founded) { + if (!found) { wpa_printf(MSG_WARNING, "KaY: Can't find txsa"); return -1; } @@ -1373,9 +1371,9 @@ ieee802_1x_mka_decode_sak_use_body( /* FIXME: Secy creates txsa with default npn. If MKA detected Latest Key * npn is larger than txsa's npn, set it to txsa. */ - secy_get_transmit_next_pn(participant->kay, txsa); + secy_get_transmit_next_pn(kay, txsa); if (lpn > txsa->next_pn) { - secy_set_transmit_next_pn(participant->kay, txsa); + secy_set_transmit_next_pn(kay, txsa); wpa_printf(MSG_INFO, "KaY: update lpn =0x%x", lpn); } @@ -1390,10 +1388,7 @@ static Boolean ieee802_1x_mka_dist_sak_body_present( struct ieee802_1x_mka_participant *participant) { - if (!participant->to_dist_sak || !participant->new_key) - return FALSE; - - return TRUE; + return participant->to_dist_sak && participant->new_key; } @@ -1404,21 +1399,18 @@ static int ieee802_1x_mka_get_dist_sak_length( struct ieee802_1x_mka_participant *participant) { - int length; - int cs_index = participant->kay->macsec_csindex; + int length = MKA_HDR_LEN; + unsigned int cs_index = participant->kay->macsec_csindex; - if (participant->advised_desired) { + if (participant->advised_desired && cs_index < CS_TABLE_SIZE) { length = sizeof(struct ieee802_1x_mka_dist_sak_body); if (cs_index != DEFAULT_CS_INDEX) length += CS_ID_LEN; length += cipher_suite_tbl[cs_index].sak_len + 8; - } else { - length = MKA_HDR_LEN; } - length = (length + 0x3) & ~0x3; - return length; + return MKA_ALIGN_LENGTH(length); } @@ -1433,7 +1425,7 @@ ieee802_1x_mka_encode_dist_sak_body( struct ieee802_1x_mka_dist_sak_body *body; struct data_key *sak; unsigned int length; - int cs_index; + unsigned int cs_index; int sak_pos; length = ieee802_1x_mka_get_dist_sak_length(participant); @@ -1452,8 +1444,13 @@ ieee802_1x_mka_encode_dist_sak_body( body->kn = host_to_be32(sak->key_identifier.kn); cs_index = participant->kay->macsec_csindex; sak_pos = 0; + if (cs_index >= CS_TABLE_SIZE) + return -1; if (cs_index != DEFAULT_CS_INDEX) { - os_memcpy(body->sak, cipher_suite_tbl[cs_index].id, CS_ID_LEN); + be64 cs; + + cs = host_to_be64(cipher_suite_tbl[cs_index].id); + os_memcpy(body->sak, &cs, CS_ID_LEN); sak_pos = CS_ID_LEN; } if (aes_wrap(participant->kek.key, 16, @@ -1472,39 +1469,13 @@ ieee802_1x_mka_encode_dist_sak_body( /** * ieee802_1x_kay_init_data_key - */ -static struct data_key * -ieee802_1x_kay_init_data_key(const struct key_conf *conf) +static void ieee802_1x_kay_init_data_key(struct data_key *pkey) { - struct data_key *pkey; - - if (!conf) - return NULL; - - pkey = os_zalloc(sizeof(*pkey)); - if (pkey == NULL) { - wpa_printf(MSG_ERROR, "%s: out of memory", __func__); - return NULL; - } - - pkey->key = os_zalloc(conf->key_len); - if (pkey->key == NULL) { - wpa_printf(MSG_ERROR, "%s: out of memory", __func__); - os_free(pkey); - return NULL; - } - - os_memcpy(pkey->key, conf->key, conf->key_len); - os_memcpy(&pkey->key_identifier, &conf->ki, - sizeof(pkey->key_identifier)); - pkey->confidentiality_offset = conf->offset; - pkey->an = conf->an; - pkey->transmits = conf->tx; - pkey->receives = conf->rx; + pkey->transmits = TRUE; + pkey->receives = TRUE; os_get_time(&pkey->created_time); pkey->user = 1; - - return pkey; } @@ -1521,19 +1492,18 @@ ieee802_1x_mka_decode_dist_sak_body( struct ieee802_1x_kay_peer *peer; struct macsec_ciphersuite *cs; size_t body_len; - struct key_conf *conf; struct data_key *sa_key = NULL; - struct ieee802_1x_mka_ki sak_ki; int sak_len; u8 *wrap_sak; u8 *unwrap_sak; + struct ieee802_1x_kay *kay = participant->kay; hdr = (struct ieee802_1x_mka_hdr *) mka_msg; body_len = get_mka_param_body_len(hdr); if ((body_len != 0) && (body_len != 28) && (body_len < 36)) { wpa_printf(MSG_ERROR, - "KaY: MKA Use SAK Packet Body Length (%d bytes) should be 0, 28, 36, or more octets", - (int) body_len); + "KaY: MKA Use SAK Packet Body Length (%zu bytes) should be 0, 28, 36, or more octets", + body_len); return -1; } @@ -1547,8 +1517,8 @@ ieee802_1x_mka_decode_dist_sak_body( "KaY: I can't accept the distributed SAK as myself is key server "); return -1; } - if (!participant->kay->macsec_desired || - participant->kay->macsec_capable == MACSEC_CAP_NOT_IMPLEMENTED) { + if (!kay->macsec_desired || + kay->macsec_capable == MACSEC_CAP_NOT_IMPLEMENTED) { wpa_printf(MSG_ERROR, "KaY: I am not MACsec-desired or without MACsec capable"); return -1; @@ -1561,28 +1531,29 @@ ieee802_1x_mka_decode_dist_sak_body( "KaY: The key server is not in my live peers list"); return -1; } - if (os_memcmp(&participant->kay->key_server_sci, - &peer->sci, sizeof(struct ieee802_1x_mka_sci)) != 0) { + if (!sci_equal(&kay->key_server_sci, &peer->sci)) { wpa_printf(MSG_ERROR, "KaY: The key server is not elected"); return -1; } + if (body_len == 0) { - participant->kay->authenticated = TRUE; - participant->kay->secured = FALSE; - participant->kay->failed = FALSE; + kay->authenticated = TRUE; + kay->secured = FALSE; + kay->failed = FALSE; participant->advised_desired = FALSE; - ieee802_1x_cp_connect_authenticated(participant->kay->cp); - ieee802_1x_cp_sm_step(participant->kay->cp); + ieee802_1x_cp_connect_authenticated(kay->cp); + ieee802_1x_cp_sm_step(kay->cp); wpa_printf(MSG_WARNING, "KaY:The Key server advise no MACsec"); participant->to_use_sak = TRUE; return 0; } + participant->advised_desired = TRUE; - participant->kay->authenticated = FALSE; - participant->kay->secured = TRUE; - participant->kay->failed = FALSE; - ieee802_1x_cp_connect_secure(participant->kay->cp); - ieee802_1x_cp_sm_step(participant->kay->cp); + kay->authenticated = FALSE; + kay->secured = TRUE; + kay->failed = FALSE; + ieee802_1x_cp_connect_secure(kay->cp); + ieee802_1x_cp_sm_step(kay->cp); body = (struct ieee802_1x_mka_dist_sak_body *)mka_msg; ieee802_1x_mka_dump_dist_sak_body(body); @@ -1595,10 +1566,12 @@ ieee802_1x_mka_decode_dist_sak_body( return 0; } } + if (body_len == 28) { sak_len = DEFAULT_SA_KEY_LEN; wrap_sak = body->sak; - participant->kay->macsec_csindex = DEFAULT_CS_INDEX; + kay->macsec_csindex = DEFAULT_CS_INDEX; + cs = &cipher_suite_tbl[kay->macsec_csindex]; } else { cs = ieee802_1x_kay_get_cipher_suite(participant, body->sak); if (!cs) { @@ -1608,7 +1581,7 @@ ieee802_1x_mka_decode_dist_sak_body( } sak_len = cs->sak_len; wrap_sak = body->sak + CS_ID_LEN; - participant->kay->macsec_csindex = cs->index; + kay->macsec_csindex = cs->index; } unwrap_sak = os_zalloc(sak_len); @@ -1624,62 +1597,36 @@ ieee802_1x_mka_decode_dist_sak_body( } wpa_hexdump(MSG_DEBUG, "\tAES Key Unwrap of SAK:", unwrap_sak, sak_len); - conf = os_zalloc(sizeof(*conf)); - if (!conf) { - wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__); - os_free(unwrap_sak); - return -1; - } - conf->key_len = sak_len; - - conf->key = os_zalloc(conf->key_len); - if (!conf->key) { - wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__); - os_free(unwrap_sak); - os_free(conf); - return -1; - } - - os_memcpy(conf->key, unwrap_sak, conf->key_len); - - os_memcpy(&sak_ki.mi, &participant->current_peer_id.mi, - sizeof(sak_ki.mi)); - sak_ki.kn = be_to_host32(body->kn); - - os_memcpy(conf->ki.mi, sak_ki.mi, MI_LEN); - conf->ki.kn = sak_ki.kn; - conf->an = body->dan; - conf->offset = body->confid_offset; - conf->rx = TRUE; - conf->tx = TRUE; - - sa_key = ieee802_1x_kay_init_data_key(conf); + sa_key = os_zalloc(sizeof(*sa_key)); if (!sa_key) { os_free(unwrap_sak); - os_free(conf->key); - os_free(conf); return -1; } + os_memcpy(&sa_key->key_identifier.mi, &participant->current_peer_id.mi, + MI_LEN); + sa_key->key_identifier.kn = be_to_host32(body->kn); + + sa_key->key = unwrap_sak; + sa_key->key_len = sak_len; + + sa_key->confidentiality_offset = body->confid_offset; + sa_key->an = body->dan; + ieee802_1x_kay_init_data_key(sa_key); + dl_list_add(&participant->sak_list, &sa_key->list); - ieee802_1x_cp_set_ciphersuite( - participant->kay->cp, - cipher_suite_tbl[participant->kay->macsec_csindex].id); - ieee802_1x_cp_sm_step(participant->kay->cp); - ieee802_1x_cp_set_offset(participant->kay->cp, body->confid_offset); - ieee802_1x_cp_sm_step(participant->kay->cp); - ieee802_1x_cp_set_distributedki(participant->kay->cp, &sak_ki); - ieee802_1x_cp_set_distributedan(participant->kay->cp, body->dan); - ieee802_1x_cp_signal_newsak(participant->kay->cp); - ieee802_1x_cp_sm_step(participant->kay->cp); + ieee802_1x_cp_set_ciphersuite(kay->cp, cs->id); + ieee802_1x_cp_sm_step(kay->cp); + ieee802_1x_cp_set_offset(kay->cp, body->confid_offset); + ieee802_1x_cp_sm_step(kay->cp); + ieee802_1x_cp_set_distributedki(kay->cp, &sa_key->key_identifier); + ieee802_1x_cp_set_distributedan(kay->cp, body->dan); + ieee802_1x_cp_signal_newsak(kay->cp); + ieee802_1x_cp_sm_step(kay->cp); participant->to_use_sak = TRUE; - os_free(unwrap_sak); - os_free(conf->key); - os_free(conf); - return 0; } @@ -1705,7 +1652,7 @@ ieee802_1x_mka_get_icv_length(struct ieee802_1x_mka_participant *participant) length = sizeof(struct ieee802_1x_mka_icv_body); length += mka_alg_tbl[participant->kay->mka_algindex].icv_len; - return (length + 0x3) & ~0x3; + return MKA_ALIGN_LENGTH(length); } @@ -1733,12 +1680,9 @@ ieee802_1x_mka_encode_icv_body(struct ieee802_1x_mka_participant *participant, return -1; } - if (length != DEFAULT_ICV_LEN) { - os_memcpy(wpabuf_put(buf, length - MKA_HDR_LEN), cmac, - length - MKA_HDR_LEN); - } else { - os_memcpy(wpabuf_put(buf, length), cmac, length); - } + if (length != DEFAULT_ICV_LEN) + length -= MKA_HDR_LEN; + os_memcpy(wpabuf_put(buf, length), cmac, length); return 0; } @@ -1754,7 +1698,7 @@ ieee802_1x_mka_decode_icv_body(struct ieee802_1x_mka_participant *participant, struct ieee802_1x_mka_icv_body *body; size_t body_len; size_t left_len; - int body_type; + u8 body_type; const u8 *pos; pos = mka_msg; @@ -1801,8 +1745,8 @@ ieee802_1x_mka_decode_dist_cak_body( body_len = get_mka_param_body_len(hdr); if (body_len < 28) { wpa_printf(MSG_ERROR, - "KaY: MKA Use SAK Packet Body Length (%d bytes) should be 28 or more octets", - (int) body_len); + "KaY: MKA Use SAK Packet Body Length (%zu bytes) should be 28 or more octets", + body_len); return -1; } @@ -1825,8 +1769,8 @@ ieee802_1x_mka_decode_kmd_body( body_len = get_mka_param_body_len(hdr); if (body_len < 5) { wpa_printf(MSG_ERROR, - "KaY: MKA Use SAK Packet Body Length (%d bytes) should be 5 or more octets", - (int) body_len); + "KaY: MKA Use SAK Packet Body Length (%zu bytes) should be 5 or more octets", + body_len); return -1; } @@ -1845,77 +1789,87 @@ static int ieee802_1x_mka_decode_announce_body( } -static struct mka_param_body_handler mak_body_handler[] = { +struct mka_param_body_handler { + int (*body_tx)(struct ieee802_1x_mka_participant *participant, + struct wpabuf *buf); + int (*body_rx)(struct ieee802_1x_mka_participant *participant, + const u8 *mka_msg, size_t msg_len); + int (*body_length)(struct ieee802_1x_mka_participant *participant); + Boolean (*body_present)(struct ieee802_1x_mka_participant *participant); +}; + + +static struct mka_param_body_handler mka_body_handler[] = { /* basic parameter set */ { - ieee802_1x_mka_encode_basic_body, - NULL, - ieee802_1x_mka_basic_body_length, - ieee802_1x_mka_basic_body_present + .body_tx = ieee802_1x_mka_encode_basic_body, + .body_rx = NULL, + .body_length = ieee802_1x_mka_basic_body_length, + .body_present = ieee802_1x_mka_basic_body_present }, /* live peer list parameter set */ { - ieee802_1x_mka_encode_live_peer_body, - ieee802_1x_mka_decode_live_peer_body, - ieee802_1x_mka_get_live_peer_length, - ieee802_1x_mka_live_peer_body_present + .body_tx = ieee802_1x_mka_encode_live_peer_body, + .body_rx = ieee802_1x_mka_decode_live_peer_body, + .body_length = ieee802_1x_mka_get_live_peer_length, + .body_present = ieee802_1x_mka_live_peer_body_present }, /* potential peer list parameter set */ { - ieee802_1x_mka_encode_potential_peer_body, - ieee802_1x_mka_decode_potential_peer_body, - ieee802_1x_mka_get_potential_peer_length, - ieee802_1x_mka_potential_peer_body_present + .body_tx = ieee802_1x_mka_encode_potential_peer_body, + .body_rx = ieee802_1x_mka_decode_potential_peer_body, + .body_length = ieee802_1x_mka_get_potential_peer_length, + .body_present = ieee802_1x_mka_potential_peer_body_present }, /* sak use parameter set */ { - ieee802_1x_mka_encode_sak_use_body, - ieee802_1x_mka_decode_sak_use_body, - ieee802_1x_mka_get_sak_use_length, - ieee802_1x_mka_sak_use_body_present + .body_tx = ieee802_1x_mka_encode_sak_use_body, + .body_rx = ieee802_1x_mka_decode_sak_use_body, + .body_length = ieee802_1x_mka_get_sak_use_length, + .body_present = ieee802_1x_mka_sak_use_body_present }, /* distribute sak parameter set */ { - ieee802_1x_mka_encode_dist_sak_body, - ieee802_1x_mka_decode_dist_sak_body, - ieee802_1x_mka_get_dist_sak_length, - ieee802_1x_mka_dist_sak_body_present + .body_tx = ieee802_1x_mka_encode_dist_sak_body, + .body_rx = ieee802_1x_mka_decode_dist_sak_body, + .body_length = ieee802_1x_mka_get_dist_sak_length, + .body_present = ieee802_1x_mka_dist_sak_body_present }, /* distribute cak parameter set */ { - NULL, - ieee802_1x_mka_decode_dist_cak_body, - NULL, - NULL + .body_tx = NULL, + .body_rx = ieee802_1x_mka_decode_dist_cak_body, + .body_length = NULL, + .body_present = NULL }, /* kmd parameter set */ { - NULL, - ieee802_1x_mka_decode_kmd_body, - NULL, - NULL + .body_tx = NULL, + .body_rx = ieee802_1x_mka_decode_kmd_body, + .body_length = NULL, + .body_present = NULL }, /* announce parameter set */ { - NULL, - ieee802_1x_mka_decode_announce_body, - NULL, - NULL + .body_tx = NULL, + .body_rx = ieee802_1x_mka_decode_announce_body, + .body_length = NULL, + .body_present = NULL }, /* icv parameter set */ { - ieee802_1x_mka_encode_icv_body, - NULL, - ieee802_1x_mka_get_icv_length, - ieee802_1x_mka_icv_body_present + .body_tx = ieee802_1x_mka_encode_icv_body, + .body_rx = NULL, + .body_length = ieee802_1x_mka_get_icv_length, + .body_present = ieee802_1x_mka_icv_body_present }, }; @@ -1923,7 +1877,7 @@ static struct mka_param_body_handler mak_body_handler[] = { /** * ieee802_1x_kay_deinit_data_key - */ -void ieee802_1x_kay_deinit_data_key(struct data_key *pkey) +static void ieee802_1x_kay_deinit_data_key(struct data_key *pkey) { if (!pkey) return; @@ -1945,11 +1899,13 @@ static int ieee802_1x_kay_generate_new_sak(struct ieee802_1x_mka_participant *participant) { struct data_key *sa_key = NULL; - struct key_conf *conf; struct ieee802_1x_kay_peer *peer; struct ieee802_1x_kay *kay = participant->kay; int ctx_len, ctx_offset; u8 *context; + unsigned int key_len; + u8 *key; + struct macsec_ciphersuite *cs; /* check condition for generating a fresh SAK: * must have one live peer @@ -1976,40 +1932,29 @@ ieee802_1x_kay_generate_new_sak(struct ieee802_1x_mka_participant *participant) return -1; } - conf = os_zalloc(sizeof(*conf)); - if (!conf) { - wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__); - return -1; - } - conf->key_len = cipher_suite_tbl[kay->macsec_csindex].sak_len; - - conf->key = os_zalloc(conf->key_len); - if (!conf->key) { - os_free(conf); + cs = &cipher_suite_tbl[kay->macsec_csindex]; + key_len = cs->sak_len; + key = os_zalloc(key_len); + if (!key) { wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__); return -1; } - ctx_len = conf->key_len + sizeof(kay->dist_kn); + ctx_len = key_len + sizeof(kay->dist_kn); dl_list_for_each(peer, &participant->live_peers, struct ieee802_1x_kay_peer, list) ctx_len += sizeof(peer->mi); ctx_len += sizeof(participant->mi); context = os_zalloc(ctx_len); - if (!context) { - os_free(conf->key); - os_free(conf); - return -1; - } + if (!context) + goto fail; + ctx_offset = 0; - if (os_get_random(context + ctx_offset, conf->key_len) < 0) { - os_free(context); - os_free(conf->key); - os_free(conf); - return -1; - } - ctx_offset += conf->key_len; + if (os_get_random(context + ctx_offset, key_len) < 0) + goto fail; + + ctx_offset += key_len; dl_list_for_each(peer, &participant->live_peers, struct ieee802_1x_kay_peer, list) { os_memcpy(context + ctx_offset, peer->mi, sizeof(peer->mi)); @@ -2020,46 +1965,44 @@ ieee802_1x_kay_generate_new_sak(struct ieee802_1x_mka_participant *participant) ctx_offset += sizeof(participant->mi); os_memcpy(context + ctx_offset, &kay->dist_kn, sizeof(kay->dist_kn)); - if (conf->key_len == 16) { + if (key_len == 16) { ieee802_1x_sak_128bits_aes_cmac(participant->cak.key, - context, ctx_len, conf->key); - } else if (conf->key_len == 32) { + context, ctx_len, key); + } else if (key_len == 32) { ieee802_1x_sak_128bits_aes_cmac(participant->cak.key, - context, ctx_len, conf->key); + context, ctx_len, key); } else { wpa_printf(MSG_ERROR, "KaY: SAK Length not support"); - os_free(conf->key); - os_free(conf); - os_free(context); - return -1; + goto fail; } - wpa_hexdump(MSG_DEBUG, "KaY: generated new SAK", - conf->key, conf->key_len); + wpa_hexdump(MSG_DEBUG, "KaY: generated new SAK", key, key_len); + os_free(context); + context = NULL; - os_memcpy(conf->ki.mi, participant->mi, MI_LEN); - conf->ki.kn = participant->kay->dist_kn; - conf->an = participant->kay->dist_an; - conf->offset = kay->macsec_confidentiality; - conf->rx = TRUE; - conf->tx = TRUE; - - sa_key = ieee802_1x_kay_init_data_key(conf); + sa_key = os_zalloc(sizeof(*sa_key)); if (!sa_key) { - os_free(conf->key); - os_free(conf); - os_free(context); - return -1; + wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__); + goto fail; } + + sa_key->key = key; + sa_key->key_len = key_len; + os_memcpy(sa_key->key_identifier.mi, participant->mi, MI_LEN); + sa_key->key_identifier.kn = kay->dist_kn; + + sa_key->confidentiality_offset = kay->macsec_confidentiality; + sa_key->an = kay->dist_an; + ieee802_1x_kay_init_data_key(sa_key); + participant->new_key = sa_key; dl_list_add(&participant->sak_list, &sa_key->list); - ieee802_1x_cp_set_ciphersuite(participant->kay->cp, - cipher_suite_tbl[kay->macsec_csindex].id); + ieee802_1x_cp_set_ciphersuite(kay->cp, cs->id); ieee802_1x_cp_sm_step(kay->cp); - ieee802_1x_cp_set_offset(kay->cp, conf->offset); + ieee802_1x_cp_set_offset(kay->cp, kay->macsec_confidentiality); ieee802_1x_cp_sm_step(kay->cp); - ieee802_1x_cp_set_distributedki(kay->cp, &conf->ki); - ieee802_1x_cp_set_distributedan(kay->cp, conf->an); + ieee802_1x_cp_set_distributedki(kay->cp, &sa_key->key_identifier); + ieee802_1x_cp_set_distributedan(kay->cp, sa_key->an); ieee802_1x_cp_signal_newsak(kay->cp); ieee802_1x_cp_sm_step(kay->cp); @@ -2067,17 +2010,31 @@ ieee802_1x_kay_generate_new_sak(struct ieee802_1x_mka_participant *participant) struct ieee802_1x_kay_peer, list) peer->sak_used = FALSE; - participant->kay->dist_kn++; - participant->kay->dist_an++; - if (participant->kay->dist_an > 3) - participant->kay->dist_an = 0; + kay->dist_kn++; + kay->dist_an++; + if (kay->dist_an > 3) + kay->dist_an = 0; - participant->kay->dist_time = time(NULL); + kay->dist_time = time(NULL); - os_free(conf->key); - os_free(conf); - os_free(context); return 0; + +fail: + os_free(key); + os_free(context); + return -1; +} + + +static int compare_priorities(const struct ieee802_1x_kay_peer *peer, + const struct ieee802_1x_kay_peer *other) +{ + if (peer->key_server_priority < other->key_server_priority) + return -1; + if (other->key_server_priority < peer->key_server_priority) + return 1; + + return os_memcmp(peer->sci.addr, other->sci.addr, ETH_ALEN); } @@ -2092,7 +2049,6 @@ ieee802_1x_kay_elect_key_server(struct ieee802_1x_mka_participant *participant) struct ieee802_1x_kay_peer *key_server = NULL; struct ieee802_1x_kay *kay = participant->kay; Boolean i_is_key_server; - int i; if (participant->is_obliged_key_server) { participant->new_sak = TRUE; @@ -2112,47 +2068,26 @@ ieee802_1x_kay_elect_key_server(struct ieee802_1x_mka_participant *participant) continue; } - if (peer->key_server_priority < - key_server->key_server_priority) { + if (compare_priorities(peer, key_server) < 0) key_server = peer; - } else if (peer->key_server_priority == - key_server->key_server_priority) { - for (i = 0; i < 6; i++) { - if (peer->sci.addr[i] < - key_server->sci.addr[i]) - key_server = peer; - } - } } /* elect the key server between me and the above elected peer */ i_is_key_server = FALSE; if (key_server && participant->can_be_key_server) { - if (kay->actor_priority - < key_server->key_server_priority) { - i_is_key_server = TRUE; - } else if (kay->actor_priority - == key_server->key_server_priority) { - for (i = 0; i < 6; i++) { - if (kay->actor_sci.addr[i] - < key_server->sci.addr[i]) { - i_is_key_server = TRUE; - } - } - } - } + struct ieee802_1x_kay_peer tmp; - if (!key_server && !i_is_key_server) { - participant->principal = FALSE; - participant->is_key_server = FALSE; - participant->is_elected = FALSE; - return 0; + tmp.key_server_priority = kay->actor_priority; + os_memcpy(&tmp.sci, &kay->actor_sci, sizeof(tmp.sci)); + if (compare_priorities(&tmp, key_server) < 0) + i_is_key_server = TRUE; + } else if (participant->can_be_key_server) { + i_is_key_server = TRUE; } if (i_is_key_server) { ieee802_1x_cp_set_electedself(kay->cp, TRUE); - if (os_memcmp(&kay->key_server_sci, &kay->actor_sci, - sizeof(kay->key_server_sci))) { + if (!sci_equal(&kay->key_server_sci, &kay->actor_sci)) { ieee802_1x_cp_signal_chgdserver(kay->cp); ieee802_1x_cp_sm_step(kay->cp); } @@ -2167,12 +2102,9 @@ ieee802_1x_kay_elect_key_server(struct ieee802_1x_mka_participant *participant) os_memcpy(&kay->key_server_sci, &kay->actor_sci, sizeof(kay->key_server_sci)); kay->key_server_priority = kay->actor_priority; - } - - if (key_server) { + } else if (key_server) { ieee802_1x_cp_set_electedself(kay->cp, FALSE); - if (os_memcmp(&kay->key_server_sci, &key_server->sci, - sizeof(kay->key_server_sci))) { + if (!sci_equal(&kay->key_server_sci, &key_server->sci)) { ieee802_1x_cp_signal_chgdserver(kay->cp); ieee802_1x_cp_sm_step(kay->cp); } @@ -2184,6 +2116,10 @@ ieee802_1x_kay_elect_key_server(struct ieee802_1x_mka_participant *participant) os_memcpy(&kay->key_server_sci, &key_server->sci, sizeof(kay->key_server_sci)); kay->key_server_priority = key_server->key_server_priority; + } else { + participant->principal = FALSE; + participant->is_key_server = FALSE; + participant->is_elected = FALSE; } return 0; @@ -2226,11 +2162,11 @@ ieee802_1x_kay_decide_macsec_use( if (!peer->macsec_desired) continue; - if (peer->macsec_capbility == MACSEC_CAP_NOT_IMPLEMENTED) + if (peer->macsec_capability == MACSEC_CAP_NOT_IMPLEMENTED) continue; - less_capability = (less_capability < peer->macsec_capbility) ? - less_capability : peer->macsec_capbility; + less_capability = (less_capability < peer->macsec_capability) ? + less_capability : peer->macsec_capability; has_peer = TRUE; } @@ -2291,10 +2227,10 @@ ieee802_1x_kay_encode_mkpdu(struct ieee802_1x_mka_participant *participant, eapol_hdr->type = IEEE802_1X_TYPE_EAPOL_MKA; eapol_hdr->length = host_to_be16(pbuf->size - pbuf->used); - for (i = 0; i < ARRAY_SIZE(mak_body_handler); i++) { - if (mak_body_handler[i].body_present && - mak_body_handler[i].body_present(participant)) { - if (mak_body_handler[i].body_tx(participant, pbuf)) + for (i = 0; i < ARRAY_SIZE(mka_body_handler); i++) { + if (mka_body_handler[i].body_present && + mka_body_handler[i].body_present(participant)) { + if (mka_body_handler[i].body_tx(participant, pbuf)) return -1; } } @@ -2316,10 +2252,10 @@ ieee802_1x_participant_send_mkpdu( wpa_printf(MSG_DEBUG, "KaY: to enpacket and send the MKPDU"); length += sizeof(struct ieee802_1x_hdr) + sizeof(struct ieee8023_hdr); - for (i = 0; i < ARRAY_SIZE(mak_body_handler); i++) { - if (mak_body_handler[i].body_present && - mak_body_handler[i].body_present(participant)) - length += mak_body_handler[i].body_length(participant); + for (i = 0; i < ARRAY_SIZE(mka_body_handler); i++) { + if (mka_body_handler[i].body_present && + mka_body_handler[i].body_present(participant)) + length += mka_body_handler[i].body_length(participant); } buf = wpabuf_alloc(length); @@ -2360,27 +2296,16 @@ static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx) participant = (struct ieee802_1x_mka_participant *)eloop_ctx; kay = participant->kay; if (participant->cak_life) { - if (now > participant->cak_life) { - kay->authenticated = FALSE; - kay->secured = FALSE; - kay->failed = TRUE; - ieee802_1x_kay_delete_mka(kay, &participant->ckn); - return; - } + if (now > participant->cak_life) + goto delete_mka; } /* should delete MKA instance if there are not live peers * when the MKA life elapsed since its creating */ if (participant->mka_life) { if (dl_list_empty(&participant->live_peers)) { - if (now > participant->mka_life) { - kay->authenticated = FALSE; - kay->secured = FALSE; - kay->failed = TRUE; - ieee802_1x_kay_delete_mka(kay, - &participant->ckn); - return; - } + if (now > participant->mka_life) + goto delete_mka; } else { participant->mka_life = 0; } @@ -2397,8 +2322,7 @@ static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx) dl_list_for_each_safe(rxsc, pre_rxsc, &participant->rxsc_list, struct receive_sc, list) { - if (os_memcmp(&rxsc->sci, &peer->sci, - sizeof(rxsc->sci)) == 0) { + if (sci_equal(&rxsc->sci, &peer->sci)) { secy_delete_receive_sc(kay, rxsc); ieee802_1x_kay_deinit_receive_sc( participant, rxsc); @@ -2469,6 +2393,14 @@ static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx) eloop_register_timeout(MKA_HELLO_TIME / 1000, 0, ieee802_1x_participant_timer, participant, NULL); + + return; + +delete_mka: + kay->authenticated = FALSE; + kay->secured = FALSE; + kay->failed = TRUE; + ieee802_1x_kay_delete_mka(kay, &participant->ckn); } @@ -2506,8 +2438,8 @@ ieee802_1x_kay_init_transmit_sa(struct transmit_sc *psc, u8 an, u32 next_PN, dl_list_add(&psc->sa_list, &psa->list); wpa_printf(MSG_DEBUG, - "KaY: Create transmit SA(an: %d, next_PN: %u) of SC(channel: %d)", - (int) an, next_PN, psc->channel); + "KaY: Create transmit SA(an: %hhu, next_PN: %u) of SC(channel: %d)", + an, next_PN, psc->channel); return psa; } @@ -2520,8 +2452,8 @@ static void ieee802_1x_kay_deinit_transmit_sa(struct transmit_sa *psa) { psa->pkey = NULL; wpa_printf(MSG_DEBUG, - "KaY: Delete transmit SA(an: %d) of SC(channel: %d)", - psa->an, psa->sc->channel); + "KaY: Delete transmit SA(an: %hhu) of SC", + psa->an); dl_list_del(&psa->list); os_free(psa); } @@ -2836,38 +2768,6 @@ int ieee802_1x_kay_enable_new_info(struct ieee802_1x_kay *kay) } -/** - * ieee802_1x_kay_cp_conf - - */ -int ieee802_1x_kay_cp_conf(struct ieee802_1x_kay *kay, - struct ieee802_1x_cp_conf *pconf) -{ - pconf->protect = kay->macsec_protect; - pconf->replay_protect = kay->macsec_replay_protect; - pconf->validate = kay->macsec_validate; - - return 0; -} - - -/** - * ieee802_1x_kay_alloc_cp_sm - - */ -static struct ieee802_1x_cp_sm * -ieee802_1x_kay_alloc_cp_sm(struct ieee802_1x_kay *kay) -{ - struct ieee802_1x_cp_conf conf; - - os_memset(&conf, 0, sizeof(conf)); - conf.protect = kay->macsec_protect; - conf.replay_protect = kay->macsec_replay_protect; - conf.validate = kay->macsec_validate; - conf.replay_window = kay->macsec_replay_window; - - return ieee802_1x_cp_sm_init(kay, &conf); -} - - /** * ieee802_1x_kay_mkpdu_sanity_check - * sanity check specified in clause 11.11.2 of IEEE802.1X-2010 @@ -2896,13 +2796,13 @@ static int ieee802_1x_kay_mkpdu_sanity_check(struct ieee802_1x_kay *kay, return -1; } - /* MKPDU should not less than 32 octets */ + /* MKPDU should not be less than 32 octets */ mka_msg_len = be_to_host16(eapol_hdr->length); if (mka_msg_len < 32) { wpa_printf(MSG_MSGDUMP, "KaY: MKPDU is less than 32 octets"); return -1; } - /* MKPDU should multiple 4 octets */ + /* MKPDU should be a multiple of 4 octets */ if ((mka_msg_len % 4) != 0) { wpa_printf(MSG_MSGDUMP, "KaY: MKPDU is not multiple of 4 octets"); @@ -2915,9 +2815,9 @@ static int ieee802_1x_kay_mkpdu_sanity_check(struct ieee802_1x_kay *kay, /* EAPOL-MKA body should comprise basic parameter set and ICV */ if (mka_msg_len < MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN) { wpa_printf(MSG_ERROR, - "KaY: Received EAPOL-MKA Packet Body Length (%d bytes) is less than the Basic Parameter Set Header Length (%d bytes) + the Basic Parameter Set Body Length (%d bytes) + %d bytes of ICV", - (int) mka_msg_len, (int) MKA_HDR_LEN, - (int) body_len, DEFAULT_ICV_LEN); + "KaY: Received EAPOL-MKA Packet Body Length (%zu bytes) is less than the Basic Parameter Set Header Length (%zu bytes) + the Basic Parameter Set Body Length (%zu bytes) + %d bytes of ICV", + mka_msg_len, MKA_HDR_LEN, + body_len, DEFAULT_ICV_LEN); return -1; } @@ -2948,21 +2848,19 @@ static int ieee802_1x_kay_mkpdu_sanity_check(struct ieee802_1x_kay *kay, wpa_printf(MSG_ERROR, "KaY: omac1_aes_128 failed"); return -1; } + msg_icv = ieee802_1x_mka_decode_icv_body(participant, (u8 *) mka_hdr, mka_msg_len); - - if (msg_icv) { - if (os_memcmp_const(msg_icv, icv, - mka_alg_tbl[kay->mka_algindex].icv_len) != - 0) { - wpa_printf(MSG_ERROR, - "KaY: Computed ICV is not equal to Received ICV"); - return -1; - } - } else { + if (!msg_icv) { wpa_printf(MSG_ERROR, "KaY: No ICV"); return -1; } + if (os_memcmp_const(msg_icv, icv, + mka_alg_tbl[kay->mka_algindex].icv_len) != 0) { + wpa_printf(MSG_ERROR, + "KaY: Computed ICV is not equal to Received ICV"); + return -1; + } return 0; } @@ -2978,10 +2876,9 @@ static int ieee802_1x_kay_decode_mkpdu(struct ieee802_1x_kay *kay, struct ieee802_1x_mka_hdr *hdr; size_t body_len; size_t left_len; - int body_type; + u8 body_type; int i; const u8 *pos; - Boolean my_included; Boolean handled[256]; if (ieee802_1x_kay_mkpdu_sanity_check(kay, buf, len)) @@ -3002,28 +2899,27 @@ static int ieee802_1x_kay_decode_mkpdu(struct ieee802_1x_kay *kay, left_len -= body_len + MKA_HDR_LEN; /* check i am in the peer's peer list */ - my_included = ieee802_1x_mka_i_in_peerlist(participant, pos, left_len); - if (my_included) { + if (ieee802_1x_mka_i_in_peerlist(participant, pos, left_len) && + !ieee802_1x_kay_is_in_live_peer(participant, + participant->current_peer_id.mi)) { /* accept the peer as live peer */ - if (!ieee802_1x_kay_is_in_peer( - participant, - participant->current_peer_id.mi)) { - if (!ieee802_1x_kay_create_live_peer( - participant, - participant->current_peer_id.mi, - participant->current_peer_id.mn)) - return -1; - ieee802_1x_kay_elect_key_server(participant); - ieee802_1x_kay_decide_macsec_use(participant); - } if (ieee802_1x_kay_is_in_potential_peer( participant, participant->current_peer_id.mi)) { - ieee802_1x_kay_move_live_peer( - participant, participant->current_peer_id.mi, - participant->current_peer_id.mn); - ieee802_1x_kay_elect_key_server(participant); - ieee802_1x_kay_decide_macsec_use(participant); + if (!ieee802_1x_kay_move_live_peer( + participant, + participant->current_peer_id.mi, + be_to_host32(participant-> + current_peer_id.mn))) + return -1; + } else if (!ieee802_1x_kay_create_live_peer( + participant, participant->current_peer_id.mi, + be_to_host32(participant-> + current_peer_id.mn))) { + return -1; } + + ieee802_1x_kay_elect_key_server(participant); + ieee802_1x_kay_decide_macsec_use(participant); } /* @@ -3034,7 +2930,9 @@ static int ieee802_1x_kay_decode_mkpdu(struct ieee802_1x_kay *kay, handled[i] = FALSE; handled[0] = TRUE; - while (left_len > MKA_HDR_LEN + DEFAULT_ICV_LEN) { + for (; left_len > MKA_HDR_LEN + DEFAULT_ICV_LEN; + pos += body_len + MKA_HDR_LEN, + left_len -= body_len + MKA_HDR_LEN) { hdr = (struct ieee802_1x_mka_hdr *) pos; body_len = get_mka_param_body_len(hdr); body_type = get_mka_param_body_type(hdr); @@ -3044,28 +2942,25 @@ static int ieee802_1x_kay_decode_mkpdu(struct ieee802_1x_kay *kay, if (left_len < (MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN)) { wpa_printf(MSG_ERROR, - "KaY: MKA Peer Packet Body Length (%d bytes) is less than the Parameter Set Header Length (%d bytes) + the Parameter Set Body Length (%d bytes) + %d bytes of ICV", - (int) left_len, (int) MKA_HDR_LEN, - (int) body_len, DEFAULT_ICV_LEN); - goto next_para_set; + "KaY: MKA Peer Packet Body Length (%zu bytes) is less than the Parameter Set Header Length (%zu bytes) + the Parameter Set Body Length (%zu bytes) + %d bytes of ICV", + left_len, MKA_HDR_LEN, + body_len, DEFAULT_ICV_LEN); + continue; } if (handled[body_type]) - goto next_para_set; + continue; handled[body_type] = TRUE; - if (mak_body_handler[body_type].body_rx) { - mak_body_handler[body_type].body_rx + if (body_type < ARRAY_SIZE(mka_body_handler) && + mka_body_handler[body_type].body_rx) { + mka_body_handler[body_type].body_rx (participant, pos, left_len); } else { wpa_printf(MSG_ERROR, - "The type %d not supported in this MKA version %d", + "The type %d is not supported in this MKA version %d", body_type, MKA_VERSION_ID); } - -next_para_set: - pos += body_len + MKA_HDR_LEN; - left_len -= body_len + MKA_HDR_LEN; } kay->active = TRUE; @@ -3094,10 +2989,10 @@ static void kay_l2_receive(void *ctx, const u8 *src_addr, const u8 *buf, eth_hdr = (struct ieee8023_hdr *) buf; eapol_hdr = (struct ieee802_1x_hdr *) (eth_hdr + 1); if (len != sizeof(*eth_hdr) + sizeof(*eapol_hdr) + - ntohs(eapol_hdr->length)) { + be_to_host16(eapol_hdr->length)) { wpa_printf(MSG_MSGDUMP, "KAY: EAPOL MPDU is invalid: (%lu-%lu)", (unsigned long) len, - (unsigned long) ntohs(eapol_hdr->length)); + (unsigned long) be_to_host16(eapol_hdr->length)); return; } @@ -3106,7 +3001,7 @@ static void kay_l2_receive(void *ctx, const u8 *src_addr, const u8 *buf, eapol_hdr->version); return; } - if (ntohs(eth_hdr->ethertype) != ETH_P_PAE || + if (be_to_host16(eth_hdr->ethertype) != ETH_P_PAE || eapol_hdr->type != IEEE802_1X_TYPE_EAPOL_MKA) return; @@ -3147,7 +3042,7 @@ ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy, os_strlcpy(kay->if_name, ifname, IFNAMSIZ); os_memcpy(kay->actor_sci.addr, addr, ETH_ALEN); - kay->actor_sci.port = 0x0001; + kay->actor_sci.port = host_to_be16(0x0001); kay->actor_priority = DEFAULT_PRIO_NOT_KEY_SERVER; /* While actor acts as a key server, shall distribute sakey */ @@ -3192,7 +3087,7 @@ ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy, wpa_printf(MSG_DEBUG, "KaY: secy init macsec done"); /* init CP */ - kay->cp = ieee802_1x_kay_alloc_cp_sm(kay); + kay->cp = ieee802_1x_cp_sm_init(kay); if (kay->cp == NULL) { ieee802_1x_kay_deinit(kay); return NULL; @@ -3314,7 +3209,7 @@ ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn, default: participant->is_obliged_key_server = FALSE; participant->can_be_key_server = TRUE; - participant->is_key_server = FALSE; + participant->is_key_server = TRUE; participant->is_elected = FALSE; break; } @@ -3335,9 +3230,8 @@ ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn, participant->retry_count = 0; participant->kay = kay; - if (os_get_random(participant->mi, sizeof(participant->mi)) < 0) + if (!reset_participant_mi(participant)) goto fail; - participant->mn = 0; participant->lrx = FALSE; participant->ltx = FALSE; @@ -3422,6 +3316,7 @@ ieee802_1x_kay_delete_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn) return; } + eloop_cancel_timeout(ieee802_1x_participant_timer, participant, NULL); dl_list_del(&participant->list); /* remove live peer */ @@ -3510,14 +3405,15 @@ ieee802_1x_kay_new_sak(struct ieee802_1x_kay *kay) * ieee802_1x_kay_change_cipher_suite - */ int -ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay *kay, int cs_index) +ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay *kay, + unsigned int cs_index) { struct ieee802_1x_mka_participant *participant; if (!kay) return -1; - if ((unsigned int) cs_index >= CS_TABLE_SIZE) { + if (cs_index >= CS_TABLE_SIZE) { wpa_printf(MSG_ERROR, "KaY: Configured cipher suite index is out of range"); return -1; diff --git a/contrib/wpa/src/pae/ieee802_1x_kay.h b/contrib/wpa/src/pae/ieee802_1x_kay.h index 064417ea51ad..afbaa336cbda 100644 --- a/contrib/wpa/src/pae/ieee802_1x_kay.h +++ b/contrib/wpa/src/pae/ieee802_1x_kay.h @@ -14,7 +14,6 @@ #include "common/ieee802_1x_defs.h" struct macsec_init_params; -struct ieee802_1x_cp_conf; #define MI_LEN 12 #define MAX_KEY_LEN 32 /* 32 bytes, 256 bits */ @@ -32,7 +31,7 @@ struct ieee802_1x_mka_ki { struct ieee802_1x_mka_sci { u8 addr[ETH_ALEN]; - u16 port; + be16 port; }; struct mka_key { @@ -48,8 +47,6 @@ struct mka_key_name { enum mka_created_mode { PSK, EAP_EXCHANGE, - DISTRIBUTED, - CACHED, }; struct ieee802_1x_kay_ctx { @@ -61,7 +58,7 @@ struct ieee802_1x_kay_ctx { int (*macsec_deinit)(void *ctx); int (*enable_protect_frames)(void *ctx, Boolean enabled); int (*set_replay_protect)(void *ctx, Boolean enabled, u32 window); - int (*set_current_cipher_suite)(void *ctx, const u8 *cs, size_t cs_len); + int (*set_current_cipher_suite)(void *ctx, u64 cs); int (*enable_controlled_port)(void *ctx, Boolean enabled); int (*get_receive_lowest_pn)(void *ctx, u32 channel, u8 an, u32 *lowest_pn); @@ -126,7 +123,7 @@ struct ieee802_1x_kay { Boolean is_obliged_key_server; char if_name[IFNAMSIZ]; - int macsec_csindex; /* MACsec cipher suite table index */ + unsigned int macsec_csindex; /* MACsec cipher suite table index */ int mka_algindex; /* MKA alg table index */ u32 dist_kn; @@ -171,7 +168,7 @@ void ieee802_1x_kay_mka_participate(struct ieee802_1x_kay *kay, Boolean status); int ieee802_1x_kay_new_sak(struct ieee802_1x_kay *kay); int ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay *kay, - int cs_index); + unsigned int cs_index); int ieee802_1x_kay_set_latest_sa_attr(struct ieee802_1x_kay *kay, struct ieee802_1x_mka_ki *lki, u8 lan, @@ -188,7 +185,5 @@ int ieee802_1x_kay_enable_tx_sas(struct ieee802_1x_kay *kay, int ieee802_1x_kay_enable_rx_sas(struct ieee802_1x_kay *kay, struct ieee802_1x_mka_ki *lki); int ieee802_1x_kay_enable_new_info(struct ieee802_1x_kay *kay); -int ieee802_1x_kay_cp_conf(struct ieee802_1x_kay *kay, - struct ieee802_1x_cp_conf *pconf); #endif /* IEEE802_1X_KAY_H */ diff --git a/contrib/wpa/src/pae/ieee802_1x_kay_i.h b/contrib/wpa/src/pae/ieee802_1x_kay_i.h index bdad3a5beb13..622282e97c51 100644 --- a/contrib/wpa/src/pae/ieee802_1x_kay_i.h +++ b/contrib/wpa/src/pae/ieee802_1x_kay_i.h @@ -38,7 +38,7 @@ struct ieee802_1x_kay; struct ieee802_1x_mka_peer_id { u8 mi[MI_LEN]; - u32 mn; + be32 mn; }; struct ieee802_1x_kay_peer { @@ -49,21 +49,11 @@ struct ieee802_1x_kay_peer { Boolean is_key_server; u8 key_server_priority; Boolean macsec_desired; - enum macsec_cap macsec_capbility; + enum macsec_cap macsec_capability; Boolean sak_used; struct dl_list list; }; -struct key_conf { - u8 *key; - struct ieee802_1x_mka_ki ki; - enum confidentiality_offset offset; - u8 an; - Boolean tx; - Boolean rx; - int key_len; /* unit: byte */ -}; - struct data_key { u8 *key; int key_len; @@ -147,7 +137,7 @@ struct receive_sa { }; struct macsec_ciphersuite { - u8 id[CS_ID_LEN]; + u64 id; char name[32]; enum macsec_cap capable; int sak_len; /* unit: byte */ @@ -241,48 +231,48 @@ struct ieee802_1x_mka_participant { struct ieee802_1x_mka_hdr { /* octet 1 */ - u32 type:8; + u8 type; /* octet 2 */ - u32 reserve:8; + u8 reserve; /* octet 3 */ #if __BYTE_ORDER == __LITTLE_ENDIAN - u32 length:4; - u32 reserve1:4; + u8 length:4; + u8 reserve1:4; #elif __BYTE_ORDER == __BIG_ENDIAN - u32 reserve1:4; - u32 length:4; + u8 reserve1:4; + u8 length:4; #else #error "Please fix " #endif /* octet 4 */ - u32 length1:8; + u8 length1; }; #define MKA_HDR_LEN sizeof(struct ieee802_1x_mka_hdr) struct ieee802_1x_mka_basic_body { /* octet 1 */ - u32 version:8; + u8 version; /* octet 2 */ - u32 priority:8; + u8 priority; /* octet 3 */ #if __BYTE_ORDER == __LITTLE_ENDIAN - u32 length:4; - u32 macsec_capbility:2; - u32 macsec_desired:1; - u32 key_server:1; + u8 length:4; + u8 macsec_capability:2; + u8 macsec_desired:1; + u8 key_server:1; #elif __BYTE_ORDER == __BIG_ENDIAN - u32 key_server:1; - u32 macsec_desired:1; - u32 macsec_capbility:2; - u32 length:4; + u8 key_server:1; + u8 macsec_desired:1; + u8 macsec_capability:2; + u8 length:4; #endif /* octet 4 */ - u32 length1:8; + u8 length1; struct ieee802_1x_mka_sci actor_sci; u8 actor_mi[MI_LEN]; - u32 actor_mn; + be32 actor_mn; u8 algo_agility[4]; /* followed by CAK Name*/ @@ -291,19 +281,19 @@ struct ieee802_1x_mka_basic_body { struct ieee802_1x_mka_peer_body { /* octet 1 */ - u32 type:8; + u8 type; /* octet 2 */ - u32 reserve:8; + u8 reserve; /* octet 3 */ #if __BYTE_ORDER == __LITTLE_ENDIAN - u32 length:4; - u32 reserve1:4; + u8 length:4; + u8 reserve1:4; #elif __BYTE_ORDER == __BIG_ENDIAN - u32 reserve1:4; - u32 length:4; + u8 reserve1:4; + u8 length:4; #endif /* octet 4 */ - u32 length1:8; + u8 length1; u8 peer[0]; /* followed by Peers */ @@ -311,83 +301,83 @@ struct ieee802_1x_mka_peer_body { struct ieee802_1x_mka_sak_use_body { /* octet 1 */ - u32 type:8; + u8 type; /* octet 2 */ #if __BYTE_ORDER == __LITTLE_ENDIAN - u32 orx:1; - u32 otx:1; - u32 oan:2; - u32 lrx:1; - u32 ltx:1; - u32 lan:2; + u8 orx:1; + u8 otx:1; + u8 oan:2; + u8 lrx:1; + u8 ltx:1; + u8 lan:2; #elif __BYTE_ORDER == __BIG_ENDIAN - u32 lan:2; - u32 ltx:1; - u32 lrx:1; - u32 oan:2; - u32 otx:1; - u32 orx:1; + u8 lan:2; + u8 ltx:1; + u8 lrx:1; + u8 oan:2; + u8 otx:1; + u8 orx:1; #endif /* octet 3 */ #if __BYTE_ORDER == __LITTLE_ENDIAN - u32 length:4; - u32 delay_protect:1; - u32 reserve:1; - u32 prx:1; - u32 ptx:1; + u8 length:4; + u8 delay_protect:1; + u8 reserve:1; + u8 prx:1; + u8 ptx:1; #elif __BYTE_ORDER == __BIG_ENDIAN - u32 ptx:1; - u32 prx:1; - u32 reserve:1; - u32 delay_protect:1; - u32 length:4; + u8 ptx:1; + u8 prx:1; + u8 reserve:1; + u8 delay_protect:1; + u8 length:4; #endif /* octet 4 */ - u32 length1:8; + u8 length1; /* octet 5 - 16 */ u8 lsrv_mi[MI_LEN]; /* octet 17 - 20 */ - u32 lkn; + be32 lkn; /* octet 21 - 24 */ - u32 llpn; + be32 llpn; /* octet 25 - 36 */ u8 osrv_mi[MI_LEN]; /* octet 37 - 40 */ - u32 okn; + be32 okn; /* octet 41 - 44 */ - u32 olpn; + be32 olpn; }; struct ieee802_1x_mka_dist_sak_body { /* octet 1 */ - u32 type:8; + u8 type; /* octet 2 */ #if __BYTE_ORDER == __LITTLE_ENDIAN - u32 reserve:4; - u32 confid_offset:2; - u32 dan:2; + u8 reserve:4; + u8 confid_offset:2; + u8 dan:2; #elif __BYTE_ORDER == __BIG_ENDIAN - u32 dan:2; - u32 confid_offset:2; - u32 reserve:4; + u8 dan:2; + u8 confid_offset:2; + u8 reserve:4; #endif /* octet 3 */ #if __BYTE_ORDER == __LITTLE_ENDIAN - u32 length:4; - u32 reserve1:4; + u8 length:4; + u8 reserve1:4; #elif __BYTE_ORDER == __BIG_ENDIAN - u32 reserve1:4; - u32 length:4; + u8 reserve1:4; + u8 length:4; #endif /* octet 4 */ - u32 length1:8; + u8 length1; /* octet 5 - 8 */ - u32 kn; + be32 kn; /* for GCM-AES-128: octet 9-32: SAK * for other cipher suite: octet 9-16: cipher suite id, octet 17-: SAK @@ -398,19 +388,19 @@ struct ieee802_1x_mka_dist_sak_body { struct ieee802_1x_mka_icv_body { /* octet 1 */ - u32 type:8; + u8 type; /* octet 2 */ - u32 reserve:8; + u8 reserve; /* octet 3 */ #if __BYTE_ORDER == __LITTLE_ENDIAN - u32 length:4; - u32 reserve1:4; + u8 length:4; + u8 reserve1:4; #elif __BYTE_ORDER == __BIG_ENDIAN - u32 reserve1:4; - u32 length:4; + u8 reserve1:4; + u8 length:4; #endif /* octet 4 */ - u32 length1:8; + u8 length1; /* octet 5 - */ u8 icv[0]; diff --git a/contrib/wpa/src/pae/ieee802_1x_secy_ops.c b/contrib/wpa/src/pae/ieee802_1x_secy_ops.c index fbe05dc35d7c..2d12911dbfcf 100644 --- a/contrib/wpa/src/pae/ieee802_1x_secy_ops.c +++ b/contrib/wpa/src/pae/ieee802_1x_secy_ops.c @@ -65,8 +65,7 @@ int secy_cp_control_replay(struct ieee802_1x_kay *kay, Boolean enabled, u32 win) } -int secy_cp_control_current_cipher_suite(struct ieee802_1x_kay *kay, - const u8 *cs, size_t cs_len) +int secy_cp_control_current_cipher_suite(struct ieee802_1x_kay *kay, u64 cs) { struct ieee802_1x_kay_ctx *ops; @@ -82,7 +81,7 @@ int secy_cp_control_current_cipher_suite(struct ieee802_1x_kay *kay, return -1; } - return ops->set_current_cipher_suite(ops->ctx, cs, cs_len); + return ops->set_current_cipher_suite(ops->ctx, cs); } diff --git a/contrib/wpa/src/pae/ieee802_1x_secy_ops.h b/contrib/wpa/src/pae/ieee802_1x_secy_ops.h index 295b823a9d7f..f5057ee11958 100644 --- a/contrib/wpa/src/pae/ieee802_1x_secy_ops.h +++ b/contrib/wpa/src/pae/ieee802_1x_secy_ops.h @@ -26,8 +26,7 @@ int secy_cp_control_validate_frames(struct ieee802_1x_kay *kay, enum validate_frames vf); int secy_cp_control_protect_frames(struct ieee802_1x_kay *kay, Boolean flag); int secy_cp_control_replay(struct ieee802_1x_kay *kay, Boolean flag, u32 win); -int secy_cp_control_current_cipher_suite(struct ieee802_1x_kay *kay, - const u8 *cs, size_t cs_len); +int secy_cp_control_current_cipher_suite(struct ieee802_1x_kay *kay, u64 cs); int secy_cp_control_confidentiality_offset(struct ieee802_1x_kay *kay, enum confidentiality_offset co); int secy_cp_control_enable_port(struct ieee802_1x_kay *kay, Boolean flag); diff --git a/contrib/wpa/src/radius/radius.c b/contrib/wpa/src/radius/radius.c index 1ebfd11f3b9a..407e4f8b9614 100644 --- a/contrib/wpa/src/radius/radius.c +++ b/contrib/wpa/src/radius/radius.c @@ -1,6 +1,6 @@ /* * RADIUS message processing - * Copyright (c) 2002-2009, 2011-2014, Jouni Malinen + * Copyright (c) 2002-2009, 2011-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -173,6 +173,8 @@ static const struct radius_attr_type radius_attrs[] = { RADIUS_ATTR_USER_PASSWORD, "User-Password", RADIUS_ATTR_UNDIST }, { RADIUS_ATTR_NAS_IP_ADDRESS, "NAS-IP-Address", RADIUS_ATTR_IP }, { RADIUS_ATTR_NAS_PORT, "NAS-Port", RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_SERVICE_TYPE, "Service-Type", RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_FRAMED_IP_ADDRESS, "Framed-IP-Address", RADIUS_ATTR_IP }, { RADIUS_ATTR_FRAMED_MTU, "Framed-MTU", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_REPLY_MESSAGE, "Reply-Message", RADIUS_ATTR_TEXT }, { RADIUS_ATTR_STATE, "State", RADIUS_ATTR_UNDIST }, @@ -214,6 +216,7 @@ static const struct radius_attr_type radius_attrs[] = RADIUS_ATTR_INT32 }, { RADIUS_ATTR_EVENT_TIMESTAMP, "Event-Timestamp", RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_EGRESS_VLANID, "EGRESS-VLANID", RADIUS_ATTR_HEXDUMP }, { RADIUS_ATTR_NAS_PORT_TYPE, "NAS-Port-Type", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_TUNNEL_TYPE, "Tunnel-Type", RADIUS_ATTR_HEXDUMP }, { RADIUS_ATTR_TUNNEL_MEDIUM_TYPE, "Tunnel-Medium-Type", @@ -535,7 +538,8 @@ int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret, int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret, - size_t secret_len) + size_t secret_len, + int require_message_authenticator) { const u8 *addr[4]; size_t len[4]; @@ -574,7 +578,11 @@ int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret, } if (attr == NULL) { - /* Message-Authenticator is MAY; not required */ + if (require_message_authenticator) { + wpa_printf(MSG_WARNING, + "Missing Message-Authenticator attribute in RADIUS message"); + return 1; + } return 0; } @@ -703,7 +711,7 @@ struct radius_msg * radius_msg_parse(const u8 *data, size_t len) attr = (struct radius_attr_hdr *) pos; - if (pos + attr->length > end || attr->length < sizeof(*attr)) + if (attr->length > end - pos || attr->length < sizeof(*attr)) goto fail; /* TODO: check that attr->length is suitable for attr->type */ @@ -815,8 +823,9 @@ int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret, os_memcpy(msg->hdr->authenticator, req_auth, sizeof(msg->hdr->authenticator)); } - hmac_md5(secret, secret_len, wpabuf_head(msg->buf), - wpabuf_len(msg->buf), auth); + if (hmac_md5(secret, secret_len, wpabuf_head(msg->buf), + wpabuf_len(msg->buf), auth) < 0) + return 1; os_memcpy(attr + 1, orig, MD5_MAC_LEN); if (req_auth) { os_memcpy(msg->hdr->authenticator, orig_authenticator, @@ -859,8 +868,8 @@ int radius_msg_verify(struct radius_msg *msg, const u8 *secret, len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr); addr[3] = secret; len[3] = secret_len; - md5_vector(4, addr, len, hash); - if (os_memcmp_const(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) { + if (md5_vector(4, addr, len, hash) < 0 || + os_memcmp_const(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) { wpa_printf(MSG_INFO, "Response Authenticator invalid!"); return 1; } @@ -892,25 +901,11 @@ int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src, /* Create Request Authenticator. The value should be unique over the lifetime * of the shared secret between authenticator and authentication server. - * Use one-way MD5 hash calculated from current timestamp and some data given - * by the caller. */ -void radius_msg_make_authenticator(struct radius_msg *msg, - const u8 *data, size_t len) + */ +int radius_msg_make_authenticator(struct radius_msg *msg) { - struct os_time tv; - long int l; - const u8 *addr[3]; - size_t elen[3]; - - os_get_time(&tv); - l = os_random(); - addr[0] = (u8 *) &tv; - elen[0] = sizeof(tv); - addr[1] = data; - elen[1] = len; - addr[2] = (u8 *) &l; - elen[2] = sizeof(l); - md5_vector(3, addr, elen, msg->hdr->authenticator); + return os_get_random((u8 *) &msg->hdr->authenticator, + sizeof(msg->hdr->authenticator)); } @@ -1028,7 +1023,10 @@ static u8 * decrypt_ms_key(const u8 *key, size_t len, addr[1] = pos - MD5_MAC_LEN; elen[1] = MD5_MAC_LEN; } - md5_vector(first ? 3 : 2, addr, elen, hash); + if (md5_vector(first ? 3 : 2, addr, elen, hash) < 0) { + os_free(plain); + return NULL; + } first = 0; for (i = 0; i < MD5_MAC_LEN; i++) @@ -1210,7 +1208,11 @@ int radius_msg_add_mppe_keys(struct radius_msg *msg, vhdr = (struct radius_attr_vendor *) pos; vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY; pos = (u8 *) (vhdr + 1); - salt = os_random() | 0x8000; + if (os_get_random((u8 *) &salt, sizeof(salt)) < 0) { + os_free(buf); + return 0; + } + salt |= 0x8000; WPA_PUT_BE16(pos, salt); pos += 2; encrypt_ms_key(send_key, send_key_len, salt, req_authenticator, secret, @@ -1422,12 +1424,30 @@ struct radius_tunnel_attrs { }; +static int cmp_int(const void *a, const void *b) +{ + int x, y; + + x = *((int *) a); + y = *((int *) b); + return (x - y); +} + + /** * radius_msg_get_vlanid - Parse RADIUS attributes for VLAN tunnel information + * The k tagged vlans found are sorted by vlan_id and stored in the first k + * items of tagged. + * * @msg: RADIUS message - * Returns: VLAN ID for the first tunnel configuration or 0 if none is found + * @untagged: Pointer to store untagged vid + * @numtagged: Size of tagged + * @tagged: Pointer to store tagged list + * + * Returns: 0 if neither tagged nor untagged configuration is found, 1 otherwise */ -int radius_msg_get_vlanid(struct radius_msg *msg) +int radius_msg_get_vlanid(struct radius_msg *msg, int *untagged, int numtagged, + int *tagged) { struct radius_tunnel_attrs tunnel[RADIUS_TUNNEL_TAGS], *tun; size_t i; @@ -1435,8 +1455,12 @@ int radius_msg_get_vlanid(struct radius_msg *msg) const u8 *data; char buf[10]; size_t dlen; + int j, taggedidx = 0, vlan_id; os_memset(&tunnel, 0, sizeof(tunnel)); + for (j = 0; j < numtagged; j++) + tagged[j] = 0; + *untagged = 0; for (i = 0; i < msg->attr_used; i++) { attr = radius_get_attr_hdr(msg, i); @@ -1473,21 +1497,44 @@ int radius_msg_get_vlanid(struct radius_msg *msg) break; os_memcpy(buf, data, dlen); buf[dlen] = '\0'; + vlan_id = atoi(buf); + if (vlan_id <= 0) + break; tun->tag_used++; - tun->vlanid = atoi(buf); + tun->vlanid = vlan_id; + break; + case RADIUS_ATTR_EGRESS_VLANID: /* RFC 4675 */ + if (attr->length != 6) + break; + vlan_id = WPA_GET_BE24(data + 1); + if (vlan_id <= 0) + break; + if (data[0] == 0x32) + *untagged = vlan_id; + else if (data[0] == 0x31 && tagged && + taggedidx < numtagged) + tagged[taggedidx++] = vlan_id; break; } } + /* Use tunnel with the lowest tag for untagged VLAN id */ for (i = 0; i < RADIUS_TUNNEL_TAGS; i++) { tun = &tunnel[i]; if (tun->tag_used && tun->type == RADIUS_TUNNEL_TYPE_VLAN && tun->medium_type == RADIUS_TUNNEL_MEDIUM_TYPE_802 && - tun->vlanid > 0) - return tun->vlanid; + tun->vlanid > 0) { + *untagged = tun->vlanid; + break; + } } + if (taggedidx) + qsort(tagged, taggedidx, sizeof(int), cmp_int); + + if (*untagged > 0 || taggedidx) + return 1; return 0; } @@ -1669,3 +1716,14 @@ u8 radius_msg_find_unlisted_attr(struct radius_msg *msg, u8 *attrs) return 0; } + + +int radius_gen_session_id(u8 *id, size_t len) +{ + /* + * Acct-Session-Id and Acct-Multi-Session-Id should be globally and + * temporarily unique. A high quality random number is required + * therefore. This could be be improved by switching to a GUID. + */ + return os_get_random(id, len); +} diff --git a/contrib/wpa/src/radius/radius.h b/contrib/wpa/src/radius/radius.h index 5977339e08d2..cd510d2c88e2 100644 --- a/contrib/wpa/src/radius/radius.h +++ b/contrib/wpa/src/radius/radius.h @@ -1,6 +1,6 @@ /* * RADIUS message processing - * Copyright (c) 2002-2009, 2012, 2014, Jouni Malinen + * Copyright (c) 2002-2009, 2012, 2014-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -52,6 +52,8 @@ enum { RADIUS_ATTR_USER_NAME = 1, RADIUS_ATTR_USER_PASSWORD = 2, RADIUS_ATTR_NAS_IP_ADDRESS = 4, RADIUS_ATTR_NAS_PORT = 5, + RADIUS_ATTR_SERVICE_TYPE = 6, + RADIUS_ATTR_FRAMED_IP_ADDRESS = 8, RADIUS_ATTR_FRAMED_MTU = 12, RADIUS_ATTR_REPLY_MESSAGE = 18, RADIUS_ATTR_STATE = 24, @@ -79,6 +81,7 @@ enum { RADIUS_ATTR_USER_NAME = 1, RADIUS_ATTR_ACCT_INPUT_GIGAWORDS = 52, RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS = 53, RADIUS_ATTR_EVENT_TIMESTAMP = 55, + RADIUS_ATTR_EGRESS_VLANID = 56, RADIUS_ATTR_NAS_PORT_TYPE = 61, RADIUS_ATTR_TUNNEL_TYPE = 64, RADIUS_ATTR_TUNNEL_MEDIUM_TYPE = 65, @@ -108,6 +111,9 @@ enum { RADIUS_ATTR_USER_NAME = 1, }; +/* Service-Type values (RFC 2865, 5.6) */ +#define RADIUS_SERVICE_TYPE_FRAMED 2 + /* Termination-Action */ #define RADIUS_TERMINATION_ACTION_DEFAULT 0 #define RADIUS_TERMINATION_ACTION_RADIUS_REQUEST 1 @@ -236,7 +242,8 @@ void radius_msg_finish_acct_resp(struct radius_msg *msg, const u8 *secret, int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret, size_t secret_len); int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret, - size_t secret_len); + size_t secret_len, + int require_message_authenticator); struct radius_attr_hdr * radius_msg_add_attr(struct radius_msg *msg, u8 type, const u8 *data, size_t data_len); struct radius_msg * radius_msg_parse(const u8 *data, size_t len); @@ -250,8 +257,7 @@ int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret, size_t secret_len, const u8 *req_auth); int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src, u8 type); -void radius_msg_make_authenticator(struct radius_msg *msg, - const u8 *data, size_t len); +int radius_msg_make_authenticator(struct radius_msg *msg); struct radius_ms_mppe_keys * radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg, const u8 *secret, size_t secret_len); @@ -274,7 +280,8 @@ radius_msg_add_attr_user_password(struct radius_msg *msg, const u8 *data, size_t data_len, const u8 *secret, size_t secret_len); int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len); -int radius_msg_get_vlanid(struct radius_msg *msg); +int radius_msg_get_vlanid(struct radius_msg *msg, int *untagged, int numtagged, + int *tagged); char * radius_msg_get_tunnel_password(struct radius_msg *msg, int *keylen, const u8 *secret, size_t secret_len, struct radius_msg *sent_msg, size_t n); @@ -319,4 +326,6 @@ int radius_copy_class(struct radius_class_data *dst, u8 radius_msg_find_unlisted_attr(struct radius_msg *msg, u8 *attrs); +int radius_gen_session_id(u8 *id, size_t len); + #endif /* RADIUS_H */ diff --git a/contrib/wpa/src/radius/radius_client.c b/contrib/wpa/src/radius/radius_client.c index 693f61ea0455..06c804d132fd 100644 --- a/contrib/wpa/src/radius/radius_client.c +++ b/contrib/wpa/src/radius/radius_client.c @@ -226,6 +226,16 @@ struct radius_client_data { * next_radius_identifier - Next RADIUS message identifier to use */ u8 next_radius_identifier; + + /** + * interim_error_cb - Interim accounting error callback + */ + void (*interim_error_cb)(const u8 *addr, void *ctx); + + /** + * interim_error_cb_ctx - interim_error_cb() context data + */ + void *interim_error_cb_ctx; }; @@ -297,6 +307,25 @@ int radius_client_register(struct radius_client_data *radius, } +/** + * radius_client_set_interim_erro_cb - Register an interim acct error callback + * @radius: RADIUS client context from radius_client_init() + * @addr: Station address from the failed message + * @cb: Handler for interim accounting errors + * @ctx: Context pointer for handler callbacks + * + * This function is used to register a handler for processing failed + * transmission attempts of interim accounting update messages. + */ +void radius_client_set_interim_error_cb(struct radius_client_data *radius, + void (*cb)(const u8 *addr, void *ctx), + void *ctx) +{ + radius->interim_error_cb = cb; + radius->interim_error_cb_ctx = ctx; +} + + /* * Returns >0 if message queue was flushed (i.e., the message that triggered * the error is not available anymore) @@ -308,7 +337,7 @@ static int radius_client_handle_send_error(struct radius_client_data *radius, int _errno = errno; wpa_printf(MSG_INFO, "send[RADIUS,s=%d]: %s", s, strerror(errno)); if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL || - _errno == EBADF || _errno == ENETUNREACH) { + _errno == EBADF || _errno == ENETUNREACH || _errno == EACCES) { hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, "Send failed - maybe interface status changed -" @@ -336,6 +365,8 @@ static int radius_client_retransmit(struct radius_client_data *radius, int s; struct wpabuf *buf; size_t prev_num_msgs; + u8 *acct_delay_time; + size_t acct_delay_time_len; if (entry->msg_type == RADIUS_ACCT || entry->msg_type == RADIUS_ACCT_INTERIM) { @@ -371,12 +402,52 @@ static int radius_client_retransmit(struct radius_client_data *radius, conf->auth_server->retransmissions++; } } + + if (entry->msg_type == RADIUS_ACCT_INTERIM) { + wpa_printf(MSG_DEBUG, + "RADIUS: Failed to transmit interim accounting update to " + MACSTR " - drop message and request a new update", + MAC2STR(entry->addr)); + if (radius->interim_error_cb) + radius->interim_error_cb(entry->addr, + radius->interim_error_cb_ctx); + return 1; + } + if (s < 0) { wpa_printf(MSG_INFO, "RADIUS: No valid socket for retransmission"); return 1; } + if (entry->msg_type == RADIUS_ACCT && + radius_msg_get_attr_ptr(entry->msg, RADIUS_ATTR_ACCT_DELAY_TIME, + &acct_delay_time, &acct_delay_time_len, + NULL) == 0 && + acct_delay_time_len == 4) { + struct radius_hdr *hdr; + u32 delay_time; + + /* + * Need to assign a new identifier since attribute contents + * changes. + */ + hdr = radius_msg_get_hdr(entry->msg); + hdr->identifier = radius_client_get_id(radius); + + /* Update Acct-Delay-Time to show wait time in queue */ + delay_time = now - entry->first_try; + WPA_PUT_BE32(acct_delay_time, delay_time); + + wpa_printf(MSG_DEBUG, + "RADIUS: Updated Acct-Delay-Time to %u for retransmission", + delay_time); + radius_msg_finish_acct(entry->msg, entry->shared_secret, + entry->shared_secret_len); + if (radius->conf->msg_dumps) + radius_msg_dump(entry->msg); + } + /* retransmit; remove entry if too many attempts */ entry->attempts++; hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS, @@ -407,7 +478,6 @@ static int radius_client_retransmit(struct radius_client_data *radius, static void radius_client_timer(void *eloop_ctx, void *timeout_ctx) { struct radius_client_data *radius = eloop_ctx; - struct hostapd_radius_servers *conf = radius->conf; struct os_reltime now; os_time_t first; struct radius_msg_list *entry, *prev, *tmp; @@ -476,10 +546,10 @@ static void radius_client_timer(void *eloop_ctx, void *timeout_ctx) (long int) (first - now.sec)); } - if (auth_failover && conf->num_auth_servers > 1) + if (auth_failover) radius_client_auth_failover(radius); - if (acct_failover && conf->num_acct_servers > 1) + if (acct_failover) radius_client_acct_failover(radius); } @@ -625,39 +695,6 @@ static void radius_client_list_add(struct radius_client_data *radius, } -static void radius_client_list_del(struct radius_client_data *radius, - RadiusType msg_type, const u8 *addr) -{ - struct radius_msg_list *entry, *prev, *tmp; - - if (addr == NULL) - return; - - entry = radius->msgs; - prev = NULL; - while (entry) { - if (entry->msg_type == msg_type && - os_memcmp(entry->addr, addr, ETH_ALEN) == 0) { - if (prev) - prev->next = entry->next; - else - radius->msgs = entry->next; - tmp = entry; - entry = entry->next; - hostapd_logger(radius->ctx, addr, - HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_DEBUG, - "Removing matching RADIUS message"); - radius_client_msg_free(tmp); - radius->num_msgs--; - continue; - } - prev = entry; - entry = entry->next; - } -} - - /** * radius_client_send - Send a RADIUS request * @radius: RADIUS client context from radius_client_init() @@ -669,16 +706,19 @@ static void radius_client_list_del(struct radius_client_data *radius, * This function is used to transmit a RADIUS authentication (RADIUS_AUTH) or * accounting request (RADIUS_ACCT or RADIUS_ACCT_INTERIM). The only difference * between accounting and interim accounting messages is that the interim - * message will override any pending interim accounting updates while a new - * accounting message does not remove any pending messages. + * message will not be retransmitted. Instead, a callback is used to indicate + * that the transmission failed for the specific station @addr so that a new + * interim accounting update message can be generated with up-to-date session + * data instead of trying to resend old information. * * The message is added on the retransmission queue and will be retransmitted * automatically until a response is received or maximum number of retries - * (RADIUS_CLIENT_MAX_RETRIES) is reached. + * (RADIUS_CLIENT_MAX_RETRIES) is reached. No such retries are used with + * RADIUS_ACCT_INTERIM, i.e., such a pending message is removed from the queue + * automatically on transmission failure. * * The related device MAC address can be used to identify pending messages that - * can be removed with radius_client_flush_auth() or with interim accounting - * updates. + * can be removed with radius_client_flush_auth(). */ int radius_client_send(struct radius_client_data *radius, struct radius_msg *msg, RadiusType msg_type, @@ -691,11 +731,6 @@ int radius_client_send(struct radius_client_data *radius, int s, res; struct wpabuf *buf; - if (msg_type == RADIUS_ACCT_INTERIM) { - /* Remove any pending interim acct update for the same STA. */ - radius_client_list_del(radius, msg_type, addr); - } - if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) { if (conf->acct_server && radius->acct_sock < 0) radius_client_init_acct(radius); @@ -1015,6 +1050,9 @@ radius_change_server(struct radius_client_data *radius, int sel_sock; struct radius_msg_list *entry; struct hostapd_radius_servers *conf = radius->conf; + struct sockaddr_in disconnect_addr = { + .sin_family = AF_UNSPEC, + }; hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, @@ -1023,6 +1061,12 @@ radius_change_server(struct radius_client_data *radius, hostapd_ip_txt(&nserv->addr, abuf, sizeof(abuf)), nserv->port); + if (oserv && oserv == nserv) { + /* Reconnect to same server, flush */ + if (auth) + radius_client_flush(radius, 1); + } + if (oserv && oserv != nserv && (nserv->shared_secret_len != oserv->shared_secret_len || os_memcmp(nserv->shared_secret, oserv->shared_secret, @@ -1125,6 +1169,11 @@ radius_change_server(struct radius_client_data *radius, } } + /* Force a reconnect by disconnecting the socket first */ + if (connect(sel_sock, (struct sockaddr *) &disconnect_addr, + sizeof(disconnect_addr)) < 0) + wpa_printf(MSG_INFO, "disconnect[radius]: %s", strerror(errno)); + if (connect(sel_sock, addr, addrlen) < 0) { wpa_printf(MSG_INFO, "connect[radius]: %s", strerror(errno)); return -1; @@ -1587,11 +1636,16 @@ static int radius_client_dump_acct_server(char *buf, size_t buflen, int radius_client_get_mib(struct radius_client_data *radius, char *buf, size_t buflen) { - struct hostapd_radius_servers *conf = radius->conf; + struct hostapd_radius_servers *conf; int i; struct hostapd_radius_server *serv; int count = 0; + if (!radius) + return 0; + + conf = radius->conf; + if (conf->auth_servers) { for (i = 0; i < conf->num_auth_servers; i++) { serv = &conf->auth_servers[i]; diff --git a/contrib/wpa/src/radius/radius_client.h b/contrib/wpa/src/radius/radius_client.h index 3db16aa282ba..8ca0874db498 100644 --- a/contrib/wpa/src/radius/radius_client.h +++ b/contrib/wpa/src/radius/radius_client.h @@ -241,6 +241,9 @@ int radius_client_register(struct radius_client_data *radius, const u8 *shared_secret, size_t shared_secret_len, void *data), void *data); +void radius_client_set_interim_error_cb(struct radius_client_data *radius, + void (*cb)(const u8 *addr, void *ctx), + void *ctx); int radius_client_send(struct radius_client_data *radius, struct radius_msg *msg, RadiusType msg_type, const u8 *addr); diff --git a/contrib/wpa/src/radius/radius_das.c b/contrib/wpa/src/radius/radius_das.c index b7d991bbd097..8a3d7e0324bc 100644 --- a/contrib/wpa/src/radius/radius_das.c +++ b/contrib/wpa/src/radius/radius_das.c @@ -23,6 +23,7 @@ struct radius_das_data { struct hostapd_ip_addr client_addr; unsigned int time_window; int require_event_timestamp; + int require_message_authenticator; void *ctx; enum radius_das_res (*disconnect)(void *ctx, struct radius_das_attrs *attr); @@ -234,9 +235,11 @@ static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx) radius_msg_dump(msg); if (radius_msg_verify_das_req(msg, das->shared_secret, - das->shared_secret_len)) { - wpa_printf(MSG_DEBUG, "DAS: Invalid authenticator in packet " - "from %s:%d - drop", abuf, from_port); + das->shared_secret_len, + das->require_message_authenticator)) { + wpa_printf(MSG_DEBUG, + "DAS: Invalid authenticator or Message-Authenticator in packet from %s:%d - drop", + abuf, from_port); goto fail; } @@ -362,6 +365,8 @@ radius_das_init(struct radius_das_conf *conf) das->time_window = conf->time_window; das->require_event_timestamp = conf->require_event_timestamp; + das->require_message_authenticator = + conf->require_message_authenticator; das->ctx = conf->ctx; das->disconnect = conf->disconnect; diff --git a/contrib/wpa/src/radius/radius_das.h b/contrib/wpa/src/radius/radius_das.h index ce731d46a9ac..9863fdc1eaca 100644 --- a/contrib/wpa/src/radius/radius_das.h +++ b/contrib/wpa/src/radius/radius_das.h @@ -44,6 +44,7 @@ struct radius_das_conf { const struct hostapd_ip_addr *client_addr; unsigned int time_window; int require_event_timestamp; + int require_message_authenticator; void *ctx; enum radius_das_res (*disconnect)(void *ctx, struct radius_das_attrs *attr); diff --git a/contrib/wpa/src/rsn_supp/pmksa_cache.c b/contrib/wpa/src/rsn_supp/pmksa_cache.c index ef7b68386476..3d8d12223c26 100644 --- a/contrib/wpa/src/rsn_supp/pmksa_cache.c +++ b/contrib/wpa/src/rsn_supp/pmksa_cache.c @@ -15,7 +15,7 @@ #include "wpa_i.h" #include "pmksa_cache.h" -#ifdef IEEE8021X_EAPOL +#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA) static const int pmksa_cache_max_entries = 32; @@ -109,6 +109,7 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() * @pmk: The new pairwise master key * @pmk_len: PMK length in bytes, usually PMK_LEN (32) + * @pmkid: Calculated PMKID * @kck: Key confirmation key or %NULL if not yet derived * @kck_len: KCK length in bytes * @aa: Authenticator address @@ -124,13 +125,13 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) */ struct rsn_pmksa_cache_entry * pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, - const u8 *kck, size_t kck_len, + const u8 *pmkid, const u8 *kck, size_t kck_len, const u8 *aa, const u8 *spa, void *network_ctx, int akmp) { struct rsn_pmksa_cache_entry *entry, *pos, *prev; struct os_reltime now; - if (pmk_len > PMK_LEN) + if (pmk_len > PMK_LEN_MAX) return NULL; if (wpa_key_mgmt_suite_b(akmp) && !kck) @@ -141,7 +142,9 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, return NULL; os_memcpy(entry->pmk, pmk, pmk_len); entry->pmk_len = pmk_len; - if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) + if (pmkid) + os_memcpy(entry->pmkid, pmkid, PMKID_LEN); + else if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) rsn_pmkid_suite_b_192(kck, kck_len, aa, spa, entry->pmkid); else if (wpa_key_mgmt_suite_b(akmp)) rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid); @@ -344,7 +347,7 @@ pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa, struct rsn_pmksa_cache_entry *new_entry; new_entry = pmksa_cache_add(pmksa, old_entry->pmk, old_entry->pmk_len, - NULL, 0, + NULL, NULL, 0, aa, pmksa->sm->own_addr, old_entry->network_ctx, old_entry->akmp); if (new_entry == NULL) diff --git a/contrib/wpa/src/rsn_supp/pmksa_cache.h b/contrib/wpa/src/rsn_supp/pmksa_cache.h index f8e040e067d9..daede6dac7fe 100644 --- a/contrib/wpa/src/rsn_supp/pmksa_cache.h +++ b/contrib/wpa/src/rsn_supp/pmksa_cache.h @@ -15,7 +15,7 @@ struct rsn_pmksa_cache_entry { struct rsn_pmksa_cache_entry *next; u8 pmkid[PMKID_LEN]; - u8 pmk[PMK_LEN]; + u8 pmk[PMK_LEN_MAX]; size_t pmk_len; os_time_t expiration; int akmp; /* WPA_KEY_MGMT_* */ @@ -44,7 +44,7 @@ enum pmksa_free_reason { PMKSA_EXPIRE, }; -#ifdef IEEE8021X_EAPOL +#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA) struct rsn_pmksa_cache * pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, @@ -57,7 +57,7 @@ struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len); struct rsn_pmksa_cache_entry * pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, - const u8 *kck, size_t kck_len, + const u8 *pmkid, const u8 *kck, size_t kck_len, const u8 *aa, const u8 *spa, void *network_ctx, int akmp); struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm); void pmksa_cache_clear_current(struct wpa_sm *sm); @@ -105,7 +105,7 @@ static inline int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, static inline struct rsn_pmksa_cache_entry * pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, - const u8 *kck, size_t kck_len, + const u8 *pmkid, const u8 *kck, size_t kck_len, const u8 *aa, const u8 *spa, void *network_ctx, int akmp) { return NULL; diff --git a/contrib/wpa/src/rsn_supp/preauth.c b/contrib/wpa/src/rsn_supp/preauth.c index c6534af2c96e..4c9a4fb8b14c 100644 --- a/contrib/wpa/src/rsn_supp/preauth.c +++ b/contrib/wpa/src/rsn_supp/preauth.c @@ -18,7 +18,7 @@ #include "wpa_i.h" -#ifdef IEEE8021X_EAPOL +#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA) #define PMKID_CANDIDATE_PRIO_SCAN 1000 @@ -93,7 +93,7 @@ static void rsn_preauth_eapol_cb(struct eapol_sm *eapol, wpa_hexdump_key(MSG_DEBUG, "RSN: PMK from pre-auth", pmk, pmk_len); sm->pmk_len = pmk_len; - pmksa_cache_add(sm->pmksa, pmk, pmk_len, + pmksa_cache_add(sm->pmksa, pmk, pmk_len, NULL, NULL, 0, sm->preauth_bssid, sm->own_addr, sm->network_ctx, @@ -538,4 +538,4 @@ int rsn_preauth_in_progress(struct wpa_sm *sm) return sm->preauth_eapol != NULL; } -#endif /* IEEE8021X_EAPOL */ +#endif /* IEEE8021X_EAPOL && !CONFIG_NO_WPA */ diff --git a/contrib/wpa/src/rsn_supp/preauth.h b/contrib/wpa/src/rsn_supp/preauth.h index 277f0663b0f0..8caf3ee56b5f 100644 --- a/contrib/wpa/src/rsn_supp/preauth.h +++ b/contrib/wpa/src/rsn_supp/preauth.h @@ -11,7 +11,7 @@ struct wpa_scan_results; -#ifdef IEEE8021X_EAPOL +#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA) void pmksa_candidate_free(struct wpa_sm *sm); int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst, @@ -27,7 +27,7 @@ int rsn_preauth_get_status(struct wpa_sm *sm, char *buf, size_t buflen, int verbose); int rsn_preauth_in_progress(struct wpa_sm *sm); -#else /* IEEE8021X_EAPOL */ +#else /* IEEE8021X_EAPOL && !CONFIG_NO_WPA */ static inline void pmksa_candidate_free(struct wpa_sm *sm) { @@ -74,6 +74,6 @@ static inline int rsn_preauth_in_progress(struct wpa_sm *sm) return 0; } -#endif /* IEEE8021X_EAPOL */ +#endif /* IEEE8021X_EAPOL && !CONFIG_NO_WPA */ #endif /* PREAUTH_H */ diff --git a/contrib/wpa/src/rsn_supp/tdls.c b/contrib/wpa/src/rsn_supp/tdls.c index cce59d7df5b9..9eb973860049 100644 --- a/contrib/wpa/src/rsn_supp/tdls.c +++ b/contrib/wpa/src/rsn_supp/tdls.c @@ -645,9 +645,15 @@ static void wpa_tdls_tpk_timeout(void *eloop_ctx, void *timeout_ctx) */ if (peer->initiator) { + u8 addr[ETH_ALEN]; + wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR " - try to renew", MAC2STR(peer->addr)); - wpa_tdls_start(sm, peer->addr); + /* cache the peer address before do_teardown */ + os_memcpy(addr, peer->addr, ETH_ALEN); + wpa_tdls_do_teardown(sm, peer, + WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED); + wpa_tdls_start(sm, addr); } else { wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR " - tear down", MAC2STR(peer->addr)); @@ -2367,7 +2373,7 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr, kde.ftie, sizeof(*ftie)); ftie = (struct wpa_tdls_ftie *) kde.ftie; - if (!os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) == 0) { + if (os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) != 0) { wpa_printf(MSG_INFO, "TDLS: FTIE SNonce in TPK M2 does " "not match with FTIE SNonce used in TPK M1"); /* Silently discard the frame */ @@ -2428,7 +2434,7 @@ skip_rsn: wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Confirm / " "TPK Handshake Message 3"); if (wpa_tdls_send_tpk_m3(sm, src_addr, dtoken, lnkid, peer) < 0) - goto error; + goto error_no_msg; if (!peer->tpk_success) { /* @@ -2449,6 +2455,7 @@ skip_rsn: error: wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken, 1, status); +error_no_msg: wpa_tdls_disable_peer_link(sm, peer); return -1; } @@ -2545,13 +2552,13 @@ static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr, goto error; } - if (!os_memcmp(peer->rnonce, ftie->Anonce, WPA_NONCE_LEN) == 0) { + if (os_memcmp(peer->rnonce, ftie->Anonce, WPA_NONCE_LEN) != 0) { wpa_printf(MSG_INFO, "TDLS: FTIE ANonce in TPK M3 does " "not match with FTIE ANonce used in TPK M2"); goto error; } - if (!os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) == 0) { + if (os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) != 0) { wpa_printf(MSG_INFO, "TDLS: FTIE SNonce in TPK M3 does not " "match with FTIE SNonce used in TPK M1"); goto error; diff --git a/contrib/wpa/src/rsn_supp/wpa.c b/contrib/wpa/src/rsn_supp/wpa.c index bc50c97141b6..58203783a218 100644 --- a/contrib/wpa/src/rsn_supp/wpa.c +++ b/contrib/wpa/src/rsn_supp/wpa.c @@ -1,6 +1,7 @@ /* * WPA Supplicant - WPA state machine and EAPOL-Key processing * Copyright (c) 2003-2015, Jouni Malinen + * Copyright(c) 2015 Intel Deutschland GmbH * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -23,6 +24,9 @@ #include "peerkey.h" +static const u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + + /** * wpa_eapol_key_send - Send WPA/RSN EAPOL-Key message * @sm: Pointer to WPA state machine data from wpa_sm_init() @@ -34,11 +38,13 @@ * @msg: EAPOL-Key message * @msg_len: Length of message * @key_mic: Pointer to the buffer to which the EAPOL-Key MIC is written + * Returns: >= 0 on success, < 0 on failure */ -void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len, - int ver, const u8 *dest, u16 proto, - u8 *msg, size_t msg_len, u8 *key_mic) +int wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len, + int ver, const u8 *dest, u16 proto, + u8 *msg, size_t msg_len, u8 *key_mic) { + int ret = -1; size_t mic_len = wpa_mic_len(sm->key_mgmt); if (is_zero_ether_addr(dest) && is_zero_ether_addr(sm->bssid)) { @@ -69,10 +75,11 @@ void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len, wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", kck, kck_len); wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC", key_mic, mic_len); wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key", msg, msg_len); - wpa_sm_ether_send(sm, dest, proto, msg, msg_len); + ret = wpa_sm_ether_send(sm, dest, proto, msg, msg_len); eapol_sm_notify_tx_eapol_key(sm->eapol); out: os_free(msg); + return ret; } @@ -124,7 +131,7 @@ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; key_info = WPA_KEY_INFO_REQUEST | ver; if (sm->ptk_set) - key_info |= WPA_KEY_INFO_MIC; + key_info |= WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE; if (error) key_info |= WPA_KEY_INFO_ERROR; if (pairwise) @@ -206,15 +213,21 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, #endif /* CONFIG_IEEE80211R */ } else if (wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && sm->eapol) { int res, pmk_len; - pmk_len = PMK_LEN; - res = eapol_sm_get_key(sm->eapol, sm->pmk, PMK_LEN); + + if (sm->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) + pmk_len = PMK_LEN_SUITE_B_192; + else + pmk_len = PMK_LEN; + res = eapol_sm_get_key(sm->eapol, sm->pmk, pmk_len); if (res) { - /* - * EAP-LEAP is an exception from other EAP methods: it - * uses only 16-byte PMK. - */ - res = eapol_sm_get_key(sm->eapol, sm->pmk, 16); - pmk_len = 16; + if (pmk_len == PMK_LEN) { + /* + * EAP-LEAP is an exception from other EAP + * methods: it uses only 16-byte PMK. + */ + res = eapol_sm_get_key(sm->eapol, sm->pmk, 16); + pmk_len = 16; + } } else { #ifdef CONFIG_IEEE80211R u8 buf[2 * PMK_LEN]; @@ -236,7 +249,7 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, !wpa_key_mgmt_suite_b(sm->key_mgmt) && !wpa_key_mgmt_ft(sm->key_mgmt)) { sa = pmksa_cache_add(sm->pmksa, - sm->pmk, pmk_len, + sm->pmk, pmk_len, NULL, NULL, 0, src_addr, sm->own_addr, sm->network_ctx, @@ -257,7 +270,7 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, * much we can do here without knowing what * exactly caused the server to misbehave. */ - wpa_dbg(sm->ctx->msg_ctx, MSG_INFO, + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: PMKID mismatch - authentication server may have derived different MSK?!"); return -1; } @@ -318,7 +331,7 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, * @wpa_ie: WPA/RSN IE * @wpa_ie_len: Length of the WPA/RSN IE * @ptk: PTK to use for keyed hash and encryption - * Returns: 0 on success, -1 on failure + * Returns: >= 0 on success, < 0 on failure */ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, const struct wpa_eapol_key *key, @@ -351,13 +364,12 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, if (rsn_ie_buf == NULL) return -1; os_memcpy(rsn_ie_buf, wpa_ie, wpa_ie_len); - res = wpa_insert_pmkid(rsn_ie_buf, wpa_ie_len, + res = wpa_insert_pmkid(rsn_ie_buf, &wpa_ie_len, sm->pmk_r1_name); if (res < 0) { os_free(rsn_ie_buf); return -1; } - wpa_ie_len += res; if (sm->assoc_resp_ies) { os_memcpy(rsn_ie_buf + wpa_ie_len, sm->assoc_resp_ies, @@ -409,10 +421,8 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, os_memcpy(reply->key_nonce, nonce, WPA_NONCE_LEN); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4"); - wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, ETH_P_EAPOL, - rbuf, rlen, key_mic); - - return 0; + return wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, + ETH_P_EAPOL, rbuf, rlen, key_mic); } @@ -500,6 +510,7 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, os_memset(buf, 0, sizeof(buf)); } sm->tptk_set = 1; + sm->tk_to_set = 1; kde = sm->assoc_wpa_ie; kde_len = sm->assoc_wpa_ie_len; @@ -525,7 +536,7 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, #endif /* CONFIG_P2P */ if (wpa_supplicant_send_2_of_4(sm, sm->bssid, key, ver, sm->snonce, - kde, kde_len, ptk)) + kde, kde_len, ptk) < 0) goto failed; os_free(kde_buf); @@ -603,7 +614,6 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm, int keylen, rsclen; enum wpa_alg alg; const u8 *key_rsc; - u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; if (sm->ptk.installed) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, @@ -781,12 +791,43 @@ static int wpa_supplicant_gtk_tx_bit_workaround(const struct wpa_sm *sm, } +static int wpa_supplicant_rsc_relaxation(const struct wpa_sm *sm, + const u8 *rsc) +{ + int rsclen; + + if (!sm->wpa_rsc_relaxation) + return 0; + + rsclen = wpa_cipher_rsc_len(sm->group_cipher); + + /* + * Try to detect RSC (endian) corruption issue where the AP sends + * the RSC bytes in EAPOL-Key message in the wrong order, both if + * it's actually a 6-byte field (as it should be) and if it treats + * it as an 8-byte field. + * An AP model known to have this bug is the Sapido RB-1632. + */ + if (rsclen == 6 && ((rsc[5] && !rsc[0]) || rsc[6] || rsc[7])) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "RSC %02x%02x%02x%02x%02x%02x%02x%02x is likely bogus, using 0", + rsc[0], rsc[1], rsc[2], rsc[3], + rsc[4], rsc[5], rsc[6], rsc[7]); + + return 1; + } + + return 0; +} + + static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm, const struct wpa_eapol_key *key, const u8 *gtk, size_t gtk_len, int key_info) { struct wpa_gtk_data gd; + const u8 *key_rsc; /* * IEEE Std 802.11i-2004 - 8.5.2 EAPOL-Key frames - Figure 43x @@ -812,6 +853,10 @@ static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm, os_memcpy(gd.gtk, gtk, gtk_len); gd.gtk_len = gtk_len; + key_rsc = key->key_rsc; + if (wpa_supplicant_rsc_relaxation(sm, key->key_rsc)) + key_rsc = null_rsc; + if (sm->group_cipher != WPA_CIPHER_GTK_NOT_USED && (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, gtk_len, gtk_len, @@ -1053,8 +1098,8 @@ static int wpa_supplicant_validate_ie_ft(struct wpa_sm *sm, if (sm->assoc_resp_ies) { pos = sm->assoc_resp_ies; end = pos + sm->assoc_resp_ies_len; - while (pos + 2 < end) { - if (pos + 2 + pos[1] > end) + while (end - pos > 2) { + if (2 + pos[1] > end - pos) break; switch (*pos) { case WLAN_EID_MOBILITY_DOMAIN: @@ -1150,7 +1195,7 @@ static int wpa_supplicant_validate_ie(struct wpa_sm *sm, * @ver: Version bits from EAPOL-Key Key Info * @key_info: Key Info * @ptk: PTK to use for keyed hash and encryption - * Returns: 0 on success, -1 on failure + * Returns: >= 0 on success, < 0 on failure */ int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst, const struct wpa_eapol_key *key, @@ -1190,10 +1235,8 @@ int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst, WPA_PUT_BE16(reply->key_data_length, 0); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4"); - wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, ETH_P_EAPOL, - rbuf, rlen, key_mic); - - return 0; + return wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, + ETH_P_EAPOL, rbuf, rlen, key_mic); } @@ -1266,7 +1309,7 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, #endif /* CONFIG_P2P */ if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info, - &sm->ptk)) { + &sm->ptk) < 0) { goto failed; } @@ -1311,7 +1354,7 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, if (sm->proto == WPA_PROTO_RSN && wpa_key_mgmt_suite_b(sm->key_mgmt)) { struct rsn_pmksa_cache_entry *sa; - sa = pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len, + sa = pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len, NULL, sm->ptk.kck, sm->ptk.kck_len, sm->bssid, sm->own_addr, sm->network_ctx, sm->key_mgmt); @@ -1501,10 +1544,8 @@ static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm, WPA_PUT_BE16(reply->key_data_length, 0); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2"); - wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, sm->bssid, - ETH_P_EAPOL, rbuf, rlen, key_mic); - - return 0; + return wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, + sm->bssid, ETH_P_EAPOL, rbuf, rlen, key_mic); } @@ -1517,6 +1558,7 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, u16 key_info; int rekey, ret; struct wpa_gtk_data gd; + const u8 *key_rsc; if (!sm->msg_3_of_4_ok) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, @@ -1547,6 +1589,10 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, if (ret) goto failed; + key_rsc = key->key_rsc; + if (wpa_supplicant_rsc_relaxation(sm, key->key_rsc)) + key_rsc = null_rsc; + if (wpa_supplicant_install_gtk(sm, &gd, key->key_rsc, 0) || wpa_supplicant_send_2_of_2(sm, key, ver, key_info)) goto failed; @@ -1681,14 +1727,14 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, } if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, *key_data_len / 8, key_data, buf)) { - os_free(buf); + 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; } os_memcpy(key_data, buf, *key_data_len); - os_free(buf); + bin_clear_free(buf, *key_data_len); WPA_PUT_BE16(key->key_data_length, *key_data_len); } else { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, @@ -2301,6 +2347,9 @@ void wpa_sm_deinit(struct wpa_sm *sm) #ifdef CONFIG_IEEE80211R os_free(sm->assoc_resp_ies); #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_TESTING_OPTIONS + wpabuf_free(sm->test_assoc_ie); +#endif /* CONFIG_TESTING_OPTIONS */ os_free(sm); } @@ -2408,12 +2457,13 @@ void wpa_sm_notify_disassoc(struct wpa_sm *sm) * @sm: Pointer to WPA state machine data from wpa_sm_init() * @pmk: The new PMK * @pmk_len: The length of the new PMK in bytes + * @pmkid: Calculated PMKID * @bssid: AA to add into PMKSA cache or %NULL to not cache the PMK * * Configure the PMK for WPA state machine. */ void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len, - const u8 *bssid) + const u8 *pmkid, const u8 *bssid) { if (sm == NULL) return; @@ -2428,7 +2478,7 @@ void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len, #endif /* CONFIG_IEEE80211R */ if (bssid) { - pmksa_cache_add(sm->pmksa, pmk, pmk_len, NULL, 0, + pmksa_cache_add(sm->pmksa, pmk, pmk_len, pmkid, NULL, 0, bssid, sm->own_addr, sm->network_ctx, sm->key_mgmt); } @@ -2512,6 +2562,7 @@ void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config) sm->ssid_len = 0; sm->wpa_ptk_rekey = config->wpa_ptk_rekey; sm->p2p = config->p2p; + sm->wpa_rsc_relaxation = config->wpa_rsc_relaxation; } else { sm->network_ctx = NULL; sm->peerkey_enabled = 0; @@ -2522,6 +2573,7 @@ void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config) sm->ssid_len = 0; sm->wpa_ptk_rekey = 0; sm->p2p = 0; + sm->wpa_rsc_relaxation = 0; } } @@ -2709,6 +2761,17 @@ int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie, if (sm == NULL) return -1; +#ifdef CONFIG_TESTING_OPTIONS + if (sm->test_assoc_ie) { + wpa_printf(MSG_DEBUG, + "TESTING: Replace association WPA/RSN IE"); + if (*wpa_ie_len < wpabuf_len(sm->test_assoc_ie)) + return -1; + os_memcpy(wpa_ie, wpabuf_head(sm->test_assoc_ie), + wpabuf_len(sm->test_assoc_ie)); + res = wpabuf_len(sm->test_assoc_ie); + } else +#endif /* CONFIG_TESTING_OPTIONS */ res = wpa_gen_wpa_ie(sm, wpa_ie, *wpa_ie_len); if (res < 0) return -1; @@ -3036,3 +3099,12 @@ void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, } sm->ptk_set = 1; } + + +#ifdef CONFIG_TESTING_OPTIONS +void wpa_sm_set_test_assoc_ie(struct wpa_sm *sm, struct wpabuf *buf) +{ + wpabuf_free(sm->test_assoc_ie); + sm->test_assoc_ie = buf; +} +#endif /* CONFIG_TESTING_OPTIONS */ diff --git a/contrib/wpa/src/rsn_supp/wpa.h b/contrib/wpa/src/rsn_supp/wpa.h index e163b7010b70..0b7477f31bc7 100644 --- a/contrib/wpa/src/rsn_supp/wpa.h +++ b/contrib/wpa/src/rsn_supp/wpa.h @@ -104,6 +104,7 @@ struct rsn_supp_config { size_t ssid_len; int wpa_ptk_rekey; int p2p; + int wpa_rsc_relaxation; }; #ifndef CONFIG_NO_WPA @@ -113,7 +114,7 @@ void wpa_sm_deinit(struct wpa_sm *sm); void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid); void wpa_sm_notify_disassoc(struct wpa_sm *sm); void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len, - const u8 *bssid); + const u8 *pmkid, const u8 *bssid); void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm); void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth); void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx); @@ -180,7 +181,8 @@ static inline void wpa_sm_notify_disassoc(struct wpa_sm *sm) } static inline void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, - size_t pmk_len) + size_t pmk_len, const u8 *pmkid, + const u8 *bssid) { } @@ -320,7 +322,8 @@ static inline void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm, } static inline void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, const u8 *ptk_kck, - const u8 *ptk_kek) + size_t ptk_kck_len, + const u8 *ptk_kek, size_t ptk_kek_len) { } @@ -415,7 +418,12 @@ int wpa_tdls_enable_chan_switch(struct wpa_sm *sm, const u8 *addr, u8 oper_class, struct hostapd_freq_params *freq_params); int wpa_tdls_disable_chan_switch(struct wpa_sm *sm, const u8 *addr); +#ifdef CONFIG_TDLS_TESTING +extern unsigned int tdls_testing; +#endif /* CONFIG_TDLS_TESTING */ + int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf); +void wpa_sm_set_test_assoc_ie(struct wpa_sm *sm, struct wpabuf *buf); #endif /* WPA_H */ diff --git a/contrib/wpa/src/rsn_supp/wpa_i.h b/contrib/wpa/src/rsn_supp/wpa_i.h index 85cc86282640..884309a9f78a 100644 --- a/contrib/wpa/src/rsn_supp/wpa_i.h +++ b/contrib/wpa/src/rsn_supp/wpa_i.h @@ -19,11 +19,12 @@ struct wpa_eapol_key; * struct wpa_sm - Internal WPA state machine data */ struct wpa_sm { - u8 pmk[PMK_LEN]; + u8 pmk[PMK_LEN_MAX]; size_t pmk_len; struct wpa_ptk ptk, tptk; int ptk_set, tptk_set; unsigned int msg_3_of_4_ok:1; + unsigned int tk_to_set:1; u8 snonce[WPA_NONCE_LEN]; u8 anonce[WPA_NONCE_LEN]; /* ANonce from the last 1/4 msg */ int renew_snonce; @@ -66,6 +67,7 @@ struct wpa_sm { size_t ssid_len; int wpa_ptk_rekey; int p2p; + int wpa_rsc_relaxation; u8 own_addr[ETH_ALEN]; const char *ifname; @@ -139,6 +141,10 @@ struct wpa_sm { #ifdef CONFIG_P2P u8 p2p_ip_addr[3 * 4]; #endif /* CONFIG_P2P */ + +#ifdef CONFIG_TESTING_OPTIONS + struct wpabuf *test_assoc_ie; +#endif /* CONFIG_TESTING_OPTIONS */ }; @@ -349,16 +355,14 @@ wpa_sm_tdls_disable_channel_switch(struct wpa_sm *sm, const u8 *addr) static inline int wpa_sm_key_mgmt_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len) { - if (!sm->proactive_key_caching) - return 0; if (!sm->ctx->key_mgmt_set_pmk) return -1; return sm->ctx->key_mgmt_set_pmk(sm->ctx->ctx, pmk, pmk_len); } -void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len, - int ver, const u8 *dest, u16 proto, - u8 *msg, size_t msg_len, u8 *key_mic); +int wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len, + int ver, const u8 *dest, u16 proto, + u8 *msg, size_t msg_len, u8 *key_mic); int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, const struct wpa_eapol_key *key, int ver, const u8 *nonce, diff --git a/contrib/wpa/src/rsn_supp/wpa_ie.c b/contrib/wpa/src/rsn_supp/wpa_ie.c index 0c37b35c1ee1..c44844ec583b 100644 --- a/contrib/wpa/src/rsn_supp/wpa_ie.c +++ b/contrib/wpa/src/rsn_supp/wpa_ie.c @@ -378,7 +378,7 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end, return 0; } - if (pos + 1 + RSN_SELECTOR_LEN < end && + if (1 + RSN_SELECTOR_LEN < end - pos && pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN && RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) { ie->pmkid = pos + 2 + RSN_SELECTOR_LEN; @@ -491,13 +491,13 @@ int wpa_supplicant_parse_ies(const u8 *buf, size_t len, int ret = 0; os_memset(ie, 0, sizeof(*ie)); - for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) { + for (pos = buf, end = pos + len; end - pos > 1; pos += 2 + pos[1]) { if (pos[0] == 0xdd && ((pos == buf + len - 1) || pos[1] == 0)) { /* Ignore padding */ break; } - if (pos + 2 + pos[1] > end) { + if (2 + pos[1] > end - pos) { wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data " "underflow (ie=%d len=%d pos=%d)", pos[0], pos[1], (int) (pos - buf)); diff --git a/contrib/wpa/src/tls/asn1.h b/contrib/wpa/src/tls/asn1.h index 74750076731d..6bd7df565dba 100644 --- a/contrib/wpa/src/tls/asn1.h +++ b/contrib/wpa/src/tls/asn1.h @@ -20,6 +20,7 @@ #define ASN1_TAG_EXTERNAL 0x08 /* not yet parsed */ #define ASN1_TAG_REAL 0x09 /* not yet parsed */ #define ASN1_TAG_ENUMERATED 0x0A /* not yet parsed */ +#define ASN1_TAG_EMBEDDED_PDV 0x0B /* not yet parsed */ #define ASN1_TAG_UTF8STRING 0x0C /* not yet parsed */ #define ANS1_TAG_RELATIVE_OID 0x0D #define ASN1_TAG_SEQUENCE 0x10 /* shall be constructed */ @@ -35,7 +36,8 @@ #define ASN1_TAG_VISIBLESTRING 0x1A #define ASN1_TAG_GENERALSTRING 0x1B /* not yet parsed */ #define ASN1_TAG_UNIVERSALSTRING 0x1C /* not yet parsed */ -#define ASN1_TAG_BMPSTRING 0x1D /* not yet parsed */ +#define ASN1_TAG_CHARACTERSTRING 0x1D /* not yet parsed */ +#define ASN1_TAG_BMPSTRING 0x1E /* not yet parsed */ #define ASN1_CLASS_UNIVERSAL 0 #define ASN1_CLASS_APPLICATION 1 diff --git a/contrib/wpa/src/tls/pkcs5.c b/contrib/wpa/src/tls/pkcs5.c index 8a93483781d8..a2ad83b8a898 100644 --- a/contrib/wpa/src/tls/pkcs5.c +++ b/contrib/wpa/src/tls/pkcs5.c @@ -1,6 +1,6 @@ /* * PKCS #5 (Password-based Encryption) - * Copyright (c) 2009, Jouni Malinen + * Copyright (c) 2009-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -11,6 +11,7 @@ #include "common.h" #include "crypto/crypto.h" #include "crypto/md5.h" +#include "crypto/sha1.h" #include "asn1.h" #include "pkcs5.h" @@ -18,30 +19,261 @@ struct pkcs5_params { enum pkcs5_alg { PKCS5_ALG_UNKNOWN, - PKCS5_ALG_MD5_DES_CBC + PKCS5_ALG_MD5_DES_CBC, + PKCS5_ALG_PBES2, + PKCS5_ALG_SHA1_3DES_CBC, } alg; - u8 salt[8]; + u8 salt[64]; size_t salt_len; unsigned int iter_count; + enum pbes2_enc_alg { + PBES2_ENC_ALG_UNKNOWN, + PBES2_ENC_ALG_DES_EDE3_CBC, + } enc_alg; + u8 iv[8]; + size_t iv_len; }; +static int oid_is_rsadsi(struct asn1_oid *oid) +{ + return oid->len >= 4 && + oid->oid[0] == 1 /* iso */ && + oid->oid[1] == 2 /* member-body */ && + oid->oid[2] == 840 /* us */ && + oid->oid[3] == 113549 /* rsadsi */; +} + + +static int pkcs5_is_oid(struct asn1_oid *oid, unsigned long alg) +{ + return oid->len == 7 && + oid_is_rsadsi(oid) && + oid->oid[4] == 1 /* pkcs */ && + oid->oid[5] == 5 /* pkcs-5 */ && + oid->oid[6] == alg; +} + + +static int enc_alg_is_oid(struct asn1_oid *oid, unsigned long alg) +{ + return oid->len == 6 && + oid_is_rsadsi(oid) && + oid->oid[4] == 3 /* encryptionAlgorithm */ && + oid->oid[5] == alg; +} + + +static int pkcs12_is_pbe_oid(struct asn1_oid *oid, unsigned long alg) +{ + return oid->len == 8 && + oid_is_rsadsi(oid) && + oid->oid[4] == 1 /* pkcs */ && + oid->oid[5] == 12 /* pkcs-12 */ && + oid->oid[6] == 1 /* pkcs-12PbeIds */ && + oid->oid[7] == alg; +} + + static enum pkcs5_alg pkcs5_get_alg(struct asn1_oid *oid) { - if (oid->len == 7 && - oid->oid[0] == 1 /* iso */ && - oid->oid[1] == 2 /* member-body */ && - oid->oid[2] == 840 /* us */ && - oid->oid[3] == 113549 /* rsadsi */ && - oid->oid[4] == 1 /* pkcs */ && - oid->oid[5] == 5 /* pkcs-5 */ && - oid->oid[6] == 3 /* pbeWithMD5AndDES-CBC */) + if (pkcs5_is_oid(oid, 3)) /* pbeWithMD5AndDES-CBC (PBES1) */ return PKCS5_ALG_MD5_DES_CBC; - + if (pkcs12_is_pbe_oid(oid, 3)) /* pbeWithSHAAnd3-KeyTripleDES-CBC */ + return PKCS5_ALG_SHA1_3DES_CBC; + if (pkcs5_is_oid(oid, 13)) /* id-PBES2 (PBES2) */ + return PKCS5_ALG_PBES2; return PKCS5_ALG_UNKNOWN; } +static int pkcs5_get_params_pbes2(struct pkcs5_params *params, const u8 *pos, + const u8 *enc_alg_end) +{ + struct asn1_hdr hdr; + const u8 *end, *kdf_end; + struct asn1_oid oid; + char obuf[80]; + + /* + * RFC 2898, Ch. A.4 + * + * PBES2-params ::= SEQUENCE { + * keyDerivationFunc AlgorithmIdentifier {{PBES2-KDFs}}, + * encryptionScheme AlgorithmIdentifier {{PBES2-Encs}} } + * + * PBES2-KDFs ALGORITHM-IDENTIFIER ::= + * { {PBKDF2-params IDENTIFIED BY id-PBKDF2}, ... } + */ + + if (asn1_get_next(pos, enc_alg_end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #5: Expected SEQUENCE (PBES2-params) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + pos = hdr.payload; + end = hdr.payload + hdr.length; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #5: Expected SEQUENCE (keyDerivationFunc) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + kdf_end = end = hdr.payload + hdr.length; + + if (asn1_get_oid(pos, end - pos, &oid, &pos)) { + wpa_printf(MSG_DEBUG, + "PKCS #5: Failed to parse OID (keyDerivationFunc algorithm)"); + return -1; + } + + asn1_oid_to_str(&oid, obuf, sizeof(obuf)); + wpa_printf(MSG_DEBUG, "PKCS #5: PBES2 keyDerivationFunc algorithm %s", + obuf); + if (!pkcs5_is_oid(&oid, 12)) /* id-PBKDF2 */ { + wpa_printf(MSG_DEBUG, + "PKCS #5: Unsupported PBES2 keyDerivationFunc algorithm %s", + obuf); + return -1; + } + + /* + * RFC 2898, C. + * + * PBKDF2-params ::= SEQUENCE { + * salt CHOICE { + * specified OCTET STRING, + * otherSource AlgorithmIdentifier {{PBKDF2-SaltSources}} + * }, + * iterationCount INTEGER (1..MAX), + * keyLength INTEGER (1..MAX) OPTIONAL, + * prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT + * algid-hmacWithSHA1 + * } + */ + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #5: Expected SEQUENCE (PBKDF2-params) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + end = hdr.payload + hdr.length; + + /* For now, only support the salt CHOICE specified (OCTET STRING) */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING || + hdr.length > sizeof(params->salt)) { + wpa_printf(MSG_DEBUG, + "PKCS #5: Expected OCTET STRING (salt.specified) - found class %d tag 0x%x size %d", + hdr.class, hdr.tag, hdr.length); + return -1; + } + pos = hdr.payload + hdr.length; + os_memcpy(params->salt, hdr.payload, hdr.length); + params->salt_len = hdr.length; + wpa_hexdump(MSG_DEBUG, "PKCS #5: salt", params->salt, params->salt_len); + + /* iterationCount INTEGER */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) { + wpa_printf(MSG_DEBUG, + "PKCS #5: Expected INTEGER - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + if (hdr.length == 1) { + params->iter_count = *hdr.payload; + } else if (hdr.length == 2) { + params->iter_count = WPA_GET_BE16(hdr.payload); + } else if (hdr.length == 4) { + params->iter_count = WPA_GET_BE32(hdr.payload); + } else { + wpa_hexdump(MSG_DEBUG, + "PKCS #5: Unsupported INTEGER value (iterationCount)", + hdr.payload, hdr.length); + return -1; + } + wpa_printf(MSG_DEBUG, "PKCS #5: iterationCount=0x%x", + params->iter_count); + if (params->iter_count == 0 || params->iter_count > 0xffff) { + wpa_printf(MSG_INFO, "PKCS #5: Unsupported iterationCount=0x%x", + params->iter_count); + return -1; + } + + /* For now, ignore optional keyLength and prf */ + + pos = kdf_end; + + /* encryptionScheme AlgorithmIdentifier {{PBES2-Encs}} */ + + if (asn1_get_next(pos, enc_alg_end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #5: Expected SEQUENCE (encryptionScheme) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + end = hdr.payload + hdr.length; + + if (asn1_get_oid(pos, end - pos, &oid, &pos)) { + wpa_printf(MSG_DEBUG, + "PKCS #5: Failed to parse OID (encryptionScheme algorithm)"); + return -1; + } + + asn1_oid_to_str(&oid, obuf, sizeof(obuf)); + wpa_printf(MSG_DEBUG, "PKCS #5: PBES2 encryptionScheme algorithm %s", + obuf); + if (enc_alg_is_oid(&oid, 7)) { + params->enc_alg = PBES2_ENC_ALG_DES_EDE3_CBC; + } else { + wpa_printf(MSG_DEBUG, + "PKCS #5: Unsupported PBES2 encryptionScheme algorithm %s", + obuf); + return -1; + } + + /* + * RFC 2898, B.2.2: + * The parameters field associated with this OID in an + * AlgorithmIdentifier shall have type OCTET STRING (SIZE(8)), + * specifying the initialization vector for CBC mode. + */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING || + hdr.length != 8) { + wpa_printf(MSG_DEBUG, + "PKCS #5: Expected OCTET STRING (SIZE(8)) (IV) - found class %d tag 0x%x size %d", + hdr.class, hdr.tag, hdr.length); + return -1; + } + os_memcpy(params->iv, hdr.payload, hdr.length); + params->iv_len = hdr.length; + wpa_hexdump(MSG_DEBUG, "PKCS #5: IV", params->iv, params->iv_len); + + return 0; +} + + static int pkcs5_get_params(const u8 *enc_alg, size_t enc_alg_len, struct pkcs5_params *params) { @@ -71,11 +303,23 @@ static int pkcs5_get_params(const u8 *enc_alg, size_t enc_alg_len, return -1; } + if (params->alg == PKCS5_ALG_PBES2) + return pkcs5_get_params_pbes2(params, pos, enc_alg_end); + + /* PBES1 */ + /* * PKCS#5, Section 8 * PBEParameter ::= SEQUENCE { * salt OCTET STRING SIZE(8), * iterationCount INTEGER } + * + * Note: The same implementation can be used to parse the PKCS #12 + * version described in RFC 7292, C: + * pkcs-12PbeParams ::= SEQUENCE { + * salt OCTET STRING, + * iterations INTEGER + * } */ if (asn1_get_next(pos, enc_alg_end - pos, &hdr) < 0 || @@ -89,11 +333,11 @@ static int pkcs5_get_params(const u8 *enc_alg, size_t enc_alg_len, pos = hdr.payload; end = hdr.payload + hdr.length; - /* salt OCTET STRING SIZE(8) */ + /* salt OCTET STRING SIZE(8) (PKCS #5) or OCTET STRING (PKCS #12) */ if (asn1_get_next(pos, end - pos, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_OCTETSTRING || - hdr.length != 8) { + hdr.length > sizeof(params->salt)) { wpa_printf(MSG_DEBUG, "PKCS #5: Expected OCTETSTRING SIZE(8) " "(salt) - found class %d tag 0x%x size %d", hdr.class, hdr.tag, hdr.length); @@ -136,6 +380,174 @@ static int pkcs5_get_params(const u8 *enc_alg, size_t enc_alg_len, } +static struct crypto_cipher * +pkcs5_crypto_init_pbes2(struct pkcs5_params *params, const char *passwd) +{ + u8 key[24]; + + if (params->enc_alg != PBES2_ENC_ALG_DES_EDE3_CBC || + params->iv_len != 8) + return NULL; + + wpa_hexdump_ascii_key(MSG_DEBUG, "PKCS #5: PBES2 password for PBKDF2", + passwd, os_strlen(passwd)); + wpa_hexdump(MSG_DEBUG, "PKCS #5: PBES2 salt for PBKDF2", + params->salt, params->salt_len); + wpa_printf(MSG_DEBUG, "PKCS #5: PBES2 PBKDF2 iterations: %u", + params->iter_count); + if (pbkdf2_sha1(passwd, params->salt, params->salt_len, + params->iter_count, key, sizeof(key)) < 0) + return NULL; + wpa_hexdump_key(MSG_DEBUG, "PKCS #5: DES EDE3 key", key, sizeof(key)); + wpa_hexdump(MSG_DEBUG, "PKCS #5: DES IV", params->iv, params->iv_len); + + return crypto_cipher_init(CRYPTO_CIPHER_ALG_3DES, params->iv, + key, sizeof(key)); +} + + +static void add_byte_array_mod(u8 *a, const u8 *b, size_t len) +{ + size_t i; + unsigned int carry = 0; + + for (i = len - 1; i < len; i--) { + carry = carry + a[i] + b[i]; + a[i] = carry & 0xff; + carry >>= 8; + } +} + + +static int pkcs12_key_gen(const u8 *pw, size_t pw_len, const u8 *salt, + size_t salt_len, u8 id, unsigned int iter, + size_t out_len, u8 *out) +{ + unsigned int u, v, S_len, P_len, i; + u8 *D = NULL, *I = NULL, *B = NULL, *pos; + int res = -1; + + /* RFC 7292, B.2 */ + u = SHA1_MAC_LEN; + v = 64; + + /* D = copies of ID */ + D = os_malloc(v); + if (!D) + goto done; + os_memset(D, id, v); + + /* S = copies of salt; P = copies of password, I = S || P */ + S_len = v * ((salt_len + v - 1) / v); + P_len = v * ((pw_len + v - 1) / v); + I = os_malloc(S_len + P_len); + if (!I) + goto done; + pos = I; + if (salt_len) { + for (i = 0; i < S_len; i++) + *pos++ = salt[i % salt_len]; + } + if (pw_len) { + for (i = 0; i < P_len; i++) + *pos++ = pw[i % pw_len]; + } + + B = os_malloc(v); + if (!B) + goto done; + + for (;;) { + u8 hash[SHA1_MAC_LEN]; + const u8 *addr[2]; + size_t len[2]; + + addr[0] = D; + len[0] = v; + addr[1] = I; + len[1] = S_len + P_len; + if (sha1_vector(2, addr, len, hash) < 0) + goto done; + + addr[0] = hash; + len[0] = SHA1_MAC_LEN; + for (i = 1; i < iter; i++) { + if (sha1_vector(1, addr, len, hash) < 0) + goto done; + } + + if (out_len <= u) { + os_memcpy(out, hash, out_len); + res = 0; + goto done; + } + + os_memcpy(out, hash, u); + out += u; + out_len -= u; + + /* I_j = (I_j + B + 1) mod 2^(v*8) */ + /* B = copies of Ai (final hash value) */ + for (i = 0; i < v; i++) + B[i] = hash[i % u]; + inc_byte_array(B, v); + for (i = 0; i < S_len + P_len; i += v) + add_byte_array_mod(&I[i], B, v); + } + +done: + os_free(B); + os_free(I); + os_free(D); + return res; +} + + +#define PKCS12_ID_ENC 1 +#define PKCS12_ID_IV 2 +#define PKCS12_ID_MAC 3 + +static struct crypto_cipher * +pkcs12_crypto_init_sha1(struct pkcs5_params *params, const char *passwd) +{ + unsigned int i; + u8 *pw; + size_t pw_len; + u8 key[24]; + u8 iv[8]; + + if (params->alg != PKCS5_ALG_SHA1_3DES_CBC) + return NULL; + + pw_len = passwd ? os_strlen(passwd) : 0; + pw = os_malloc(2 * (pw_len + 1)); + if (!pw) + return NULL; + if (pw_len) { + for (i = 0; i <= pw_len; i++) + WPA_PUT_BE16(&pw[2 * i], passwd[i]); + pw_len = 2 * (pw_len + 1); + } + + if (pkcs12_key_gen(pw, pw_len, params->salt, params->salt_len, + PKCS12_ID_ENC, params->iter_count, + sizeof(key), key) < 0 || + pkcs12_key_gen(pw, pw_len, params->salt, params->salt_len, + PKCS12_ID_IV, params->iter_count, + sizeof(iv), iv) < 0) { + os_free(pw); + return NULL; + } + + os_free(pw); + + wpa_hexdump_key(MSG_DEBUG, "PKCS #12: DES key", key, sizeof(key)); + wpa_hexdump_key(MSG_DEBUG, "PKCS #12: DES IV", iv, sizeof(iv)); + + return crypto_cipher_init(CRYPTO_CIPHER_ALG_3DES, iv, key, sizeof(key)); +} + + static struct crypto_cipher * pkcs5_crypto_init(struct pkcs5_params *params, const char *passwd) { @@ -144,6 +556,12 @@ static struct crypto_cipher * pkcs5_crypto_init(struct pkcs5_params *params, const u8 *addr[2]; size_t len[2]; + if (params->alg == PKCS5_ALG_PBES2) + return pkcs5_crypto_init_pbes2(params, passwd); + + if (params->alg == PKCS5_ALG_SHA1_3DES_CBC) + return pkcs12_crypto_init_sha1(params, passwd); + if (params->alg != PKCS5_ALG_MD5_DES_CBC) return NULL; diff --git a/contrib/wpa/src/tls/tlsv1_client.c b/contrib/wpa/src/tls/tlsv1_client.c index a6f0587e34c5..9bc0d211f48d 100644 --- a/contrib/wpa/src/tls/tlsv1_client.c +++ b/contrib/wpa/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-2014, Jouni Malinen + * Copyright (c) 2006-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -11,6 +11,7 @@ #include "common.h" #include "crypto/sha1.h" #include "crypto/tls.h" +#include "x509v3.h" #include "tlsv1_common.h" #include "tlsv1_record.h" #include "tlsv1_client.h" @@ -110,7 +111,6 @@ int tls_derive_keys(struct tlsv1_client *conn, pos += conn->rl.iv_size; /* server_write_IV */ os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size); - pos += conn->rl.iv_size; } else { /* * Use IV field to set the mask value for TLS v1.1. A fixed @@ -494,6 +494,7 @@ void tlsv1_client_deinit(struct tlsv1_client *conn) tlsv1_client_free_dh(conn); tlsv1_cred_free(conn->cred); wpabuf_free(conn->partial_input); + x509_certificate_chain_free(conn->server_cert); os_free(conn); } @@ -691,18 +692,16 @@ int tlsv1_client_hello_ext(struct tlsv1_client *conn, int ext_type, if (data == NULL || data_len == 0) return 0; - pos = conn->client_hello_ext = os_malloc(6 + data_len); + pos = conn->client_hello_ext = os_malloc(4 + data_len); if (pos == NULL) return -1; - WPA_PUT_BE16(pos, 4 + data_len); - pos += 2; WPA_PUT_BE16(pos, ext_type); pos += 2; WPA_PUT_BE16(pos, data_len); pos += 2; os_memcpy(pos, data, data_len); - conn->client_hello_ext_len = 6 + data_len; + conn->client_hello_ext_len = 4 + data_len; if (ext_type == TLS_EXT_PAC_OPAQUE) { conn->session_ticket_included = 1; @@ -813,9 +812,14 @@ int tlsv1_client_set_cred(struct tlsv1_client *conn, } -void tlsv1_client_set_time_checks(struct tlsv1_client *conn, int enabled) +/** + * tlsv1_client_set_flags - Set connection flags + * @conn: TLSv1 client connection data from tlsv1_client_init() + * @flags: TLS_CONN_* bitfield + */ +void tlsv1_client_set_flags(struct tlsv1_client *conn, unsigned int flags) { - conn->disable_time_checks = !enabled; + conn->flags = flags; } @@ -828,3 +832,38 @@ void tlsv1_client_set_session_ticket_cb(struct tlsv1_client *conn, conn->session_ticket_cb = cb; conn->session_ticket_cb_ctx = ctx; } + + +void tlsv1_client_set_cb(struct tlsv1_client *conn, + void (*event_cb)(void *ctx, enum tls_event ev, + union tls_event_data *data), + void *cb_ctx, + int cert_in_cb) +{ + conn->event_cb = event_cb; + conn->cb_ctx = cb_ctx; + conn->cert_in_cb = !!cert_in_cb; +} + + +int tlsv1_client_get_version(struct tlsv1_client *conn, char *buf, + size_t buflen) +{ + if (!conn) + return -1; + switch (conn->rl.tls_version) { + case TLS_VERSION_1: + os_strlcpy(buf, "TLSv1", buflen); + break; + case TLS_VERSION_1_1: + os_strlcpy(buf, "TLSv1.1", buflen); + break; + case TLS_VERSION_1_2: + os_strlcpy(buf, "TLSv1.2", buflen); + break; + default: + return -1; + } + + return 0; +} diff --git a/contrib/wpa/src/tls/tlsv1_client.h b/contrib/wpa/src/tls/tlsv1_client.h index a4e25e969937..40fa6c7fbdee 100644 --- a/contrib/wpa/src/tls/tlsv1_client.h +++ b/contrib/wpa/src/tls/tlsv1_client.h @@ -41,7 +41,7 @@ int tlsv1_client_get_keyblock_size(struct tlsv1_client *conn); int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers); int tlsv1_client_set_cred(struct tlsv1_client *conn, struct tlsv1_credentials *cred); -void tlsv1_client_set_time_checks(struct tlsv1_client *conn, int enabled); +void tlsv1_client_set_flags(struct tlsv1_client *conn, unsigned int flags); typedef int (*tlsv1_client_session_ticket_cb) (void *ctx, const u8 *ticket, size_t len, const u8 *client_random, @@ -51,4 +51,12 @@ void tlsv1_client_set_session_ticket_cb(struct tlsv1_client *conn, tlsv1_client_session_ticket_cb cb, void *ctx); +void tlsv1_client_set_cb(struct tlsv1_client *conn, + void (*event_cb)(void *ctx, enum tls_event ev, + union tls_event_data *data), + void *cb_ctx, + int cert_in_cb); +int tlsv1_client_get_version(struct tlsv1_client *conn, char *buf, + size_t buflen); + #endif /* TLSV1_CLIENT_H */ diff --git a/contrib/wpa/src/tls/tlsv1_client_i.h b/contrib/wpa/src/tls/tlsv1_client_i.h index 55fdcf8d0435..12ec8df6c3ac 100644 --- a/contrib/wpa/src/tls/tlsv1_client_i.h +++ b/contrib/wpa/src/tls/tlsv1_client_i.h @@ -29,11 +29,14 @@ struct tlsv1_client { u8 alert_level; u8 alert_description; + unsigned int flags; /* TLS_CONN_* bitfield */ + unsigned int certificate_requested:1; unsigned int session_resumed:1; unsigned int session_ticket_included:1; unsigned int use_session_ticket:1; - unsigned int disable_time_checks:1; + unsigned int cert_in_cb:1; + unsigned int ocsp_resp_received:1; struct crypto_public_key *server_rsa_key; @@ -64,6 +67,12 @@ struct tlsv1_client { void *session_ticket_cb_ctx; struct wpabuf *partial_input; + + void (*event_cb)(void *ctx, enum tls_event ev, + union tls_event_data *data); + void *cb_ctx; + + struct x509_certificate *server_cert; }; @@ -81,4 +90,11 @@ int tlsv1_client_process_handshake(struct tlsv1_client *conn, u8 ct, const u8 *buf, size_t *len, u8 **out_data, size_t *out_len); +enum tls_ocsp_result { + TLS_OCSP_NO_RESPONSE, TLS_OCSP_INVALID, TLS_OCSP_GOOD, TLS_OCSP_REVOKED +}; + +enum tls_ocsp_result tls_process_ocsp_response(struct tlsv1_client *conn, + const u8 *resp, size_t len); + #endif /* TLSV1_CLIENT_I_H */ diff --git a/contrib/wpa/src/tls/tlsv1_client_ocsp.c b/contrib/wpa/src/tls/tlsv1_client_ocsp.c new file mode 100644 index 000000000000..1d7b68ca286e --- /dev/null +++ b/contrib/wpa/src/tls/tlsv1_client_ocsp.c @@ -0,0 +1,803 @@ +/* + * TLSv1 client - OCSP + * Copyright (c) 2015, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/tls.h" +#include "crypto/sha1.h" +#include "asn1.h" +#include "x509v3.h" +#include "tlsv1_common.h" +#include "tlsv1_record.h" +#include "tlsv1_client.h" +#include "tlsv1_client_i.h" + + +/* RFC 6960, 4.2.1: OCSPResponseStatus ::= ENUMERATED */ +enum ocsp_response_status { + OCSP_RESP_STATUS_SUCCESSFUL = 0, + OCSP_RESP_STATUS_MALFORMED_REQ = 1, + OCSP_RESP_STATUS_INT_ERROR = 2, + OCSP_RESP_STATUS_TRY_LATER = 3, + /* 4 not used */ + OCSP_RESP_STATUS_SIG_REQUIRED = 5, + OCSP_RESP_STATUS_UNAUTHORIZED = 6, +}; + + +static int is_oid_basic_ocsp_resp(struct asn1_oid *oid) +{ + return oid->len == 10 && + oid->oid[0] == 1 /* iso */ && + oid->oid[1] == 3 /* identified-organization */ && + oid->oid[2] == 6 /* dod */ && + oid->oid[3] == 1 /* internet */ && + oid->oid[4] == 5 /* security */ && + oid->oid[5] == 5 /* mechanisms */ && + oid->oid[6] == 7 /* id-pkix */ && + oid->oid[7] == 48 /* id-ad */ && + oid->oid[8] == 1 /* id-pkix-ocsp */ && + oid->oid[9] == 1 /* id-pkix-ocsp-basic */; +} + + +static int ocsp_responder_id_match(struct x509_certificate *signer, + struct x509_name *name, const u8 *key_hash) +{ + if (key_hash) { + u8 hash[SHA1_MAC_LEN]; + const u8 *addr[1] = { signer->public_key }; + size_t len[1] = { signer->public_key_len }; + + if (sha1_vector(1, addr, len, hash) < 0) + return 0; + return os_memcmp(hash, key_hash, SHA1_MAC_LEN) == 0; + } + + return x509_name_compare(&signer->subject, name) == 0; +} + + +static unsigned int ocsp_hash_data(struct asn1_oid *alg, const u8 *data, + size_t data_len, u8 *hash) +{ + const u8 *addr[1] = { data }; + size_t len[1] = { data_len }; + char buf[100]; + + if (x509_sha1_oid(alg)) { + if (sha1_vector(1, addr, len, hash) < 0) + return 0; + wpa_hexdump(MSG_MSGDUMP, "OCSP: Hash (SHA1)", hash, 20); + return 20; + } + + if (x509_sha256_oid(alg)) { + if (sha256_vector(1, addr, len, hash) < 0) + return 0; + wpa_hexdump(MSG_MSGDUMP, "OCSP: Hash (SHA256)", hash, 32); + return 32; + } + + if (x509_sha384_oid(alg)) { + if (sha384_vector(1, addr, len, hash) < 0) + return 0; + wpa_hexdump(MSG_MSGDUMP, "OCSP: Hash (SHA384)", hash, 48); + return 48; + } + + if (x509_sha512_oid(alg)) { + if (sha512_vector(1, addr, len, hash) < 0) + return 0; + wpa_hexdump(MSG_MSGDUMP, "OCSP: Hash (SHA512)", hash, 64); + return 64; + } + + + asn1_oid_to_str(alg, buf, sizeof(buf)); + wpa_printf(MSG_DEBUG, "OCSP: Could not calculate hash with alg %s", + buf); + return 0; +} + + +static int tls_process_ocsp_single_response(struct tlsv1_client *conn, + struct x509_certificate *cert, + struct x509_certificate *issuer, + const u8 *resp, size_t len, + enum tls_ocsp_result *res) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + struct x509_algorithm_identifier alg; + const u8 *name_hash, *key_hash; + size_t name_hash_len, key_hash_len; + const u8 *serial_number; + size_t serial_number_len; + u8 hash[64]; + unsigned int hash_len; + unsigned int cert_status; + os_time_t update; + struct os_time now; + + wpa_hexdump(MSG_MSGDUMP, "OCSP: SingleResponse", resp, len); + + /* + * SingleResponse ::= SEQUENCE { + * certID CertID, + * certStatus CertStatus, + * thisUpdate GeneralizedTime, + * nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL, + * singleExtensions [1] EXPLICIT Extensions OPTIONAL } + */ + + /* CertID ::= SEQUENCE */ + if (asn1_get_next(resp, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected SEQUENCE (CertID) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + pos = hdr.payload; + end = hdr.payload + hdr.length; + + /* + * CertID ::= SEQUENCE { + * hashAlgorithm AlgorithmIdentifier, + * issuerNameHash OCTET STRING, + * issuerKeyHash OCTET STRING, + * serialNumber CertificateSerialNumber } + */ + + /* hashAlgorithm AlgorithmIdentifier */ + if (x509_parse_algorithm_identifier(pos, end - pos, &alg, &pos)) + return -1; + + /* issuerNameHash OCTET STRING */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected OCTET STRING (issuerNameHash) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + name_hash = hdr.payload; + name_hash_len = hdr.length; + wpa_hexdump(MSG_DEBUG, "OCSP: issuerNameHash", + name_hash, name_hash_len); + pos = hdr.payload + hdr.length; + + wpa_hexdump(MSG_DEBUG, "OCSP: Issuer subject DN", + issuer->subject_dn, issuer->subject_dn_len); + hash_len = ocsp_hash_data(&alg.oid, issuer->subject_dn, + issuer->subject_dn_len, hash); + if (hash_len == 0 || name_hash_len != hash_len || + os_memcmp(name_hash, hash, hash_len) != 0) { + wpa_printf(MSG_DEBUG, "OCSP: issuerNameHash mismatch"); + wpa_hexdump(MSG_DEBUG, "OCSP: Calculated issuerNameHash", + hash, hash_len); + return -1; + } + + /* issuerKeyHash OCTET STRING */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected OCTET STRING (issuerKeyHash) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + key_hash = hdr.payload; + key_hash_len = hdr.length; + wpa_hexdump(MSG_DEBUG, "OCSP: issuerKeyHash", key_hash, key_hash_len); + pos = hdr.payload + hdr.length; + + hash_len = ocsp_hash_data(&alg.oid, issuer->public_key, + issuer->public_key_len, hash); + if (hash_len == 0 || key_hash_len != hash_len || + os_memcmp(key_hash, hash, hash_len) != 0) { + wpa_printf(MSG_DEBUG, "OCSP: issuerKeyHash mismatch"); + wpa_hexdump(MSG_DEBUG, "OCSP: Calculated issuerKeyHash", + hash, hash_len); + return -1; + } + + /* serialNumber CertificateSerialNumber ::= INTEGER */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_INTEGER || + hdr.length < 1 || hdr.length > X509_MAX_SERIAL_NUM_LEN) { + wpa_printf(MSG_DEBUG, "OCSP: No INTEGER tag found for serialNumber; class=%d tag=0x%x length=%u", + hdr.class, hdr.tag, hdr.length); + return -1; + } + serial_number = hdr.payload; + serial_number_len = hdr.length; + while (serial_number_len > 0 && serial_number[0] == 0) { + serial_number++; + serial_number_len--; + } + wpa_hexdump(MSG_MSGDUMP, "OCSP: serialNumber", serial_number, + serial_number_len); + + if (serial_number_len != cert->serial_number_len || + os_memcmp(serial_number, cert->serial_number, + serial_number_len) != 0) { + wpa_printf(MSG_DEBUG, "OCSP: serialNumber mismatch"); + return -1; + } + + pos = end; + end = resp + len; + + /* certStatus CertStatus ::= CHOICE */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected CHOICE (CertStatus) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + cert_status = hdr.tag; + wpa_printf(MSG_DEBUG, "OCSP: certStatus=%u", cert_status); + wpa_hexdump(MSG_DEBUG, "OCSP: CertStatus additional data", + hdr.payload, hdr.length); + pos = hdr.payload + hdr.length; + + os_get_time(&now); + /* thisUpdate GeneralizedTime */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_GENERALIZEDTIME || + x509_parse_time(hdr.payload, hdr.length, hdr.tag, &update) < 0) { + wpa_printf(MSG_DEBUG, "OCSP: Failed to parse thisUpdate"); + return -1; + } + wpa_printf(MSG_DEBUG, "OCSP: thisUpdate %lu", (unsigned long) update); + pos = hdr.payload + hdr.length; + if ((unsigned long) now.sec < (unsigned long) update) { + wpa_printf(MSG_DEBUG, + "OCSP: thisUpdate time in the future (response not yet valid)"); + return -1; + } + + /* nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL */ + if (pos < end) { + if (asn1_get_next(pos, end - pos, &hdr) < 0) + return -1; + if (hdr.class == ASN1_CLASS_CONTEXT_SPECIFIC && hdr.tag == 0) { + const u8 *next = hdr.payload + hdr.length; + + if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_GENERALIZEDTIME || + x509_parse_time(hdr.payload, hdr.length, hdr.tag, + &update) < 0) { + wpa_printf(MSG_DEBUG, + "OCSP: Failed to parse nextUpdate"); + return -1; + } + wpa_printf(MSG_DEBUG, "OCSP: nextUpdate %lu", + (unsigned long) update); + pos = next; + if ((unsigned long) now.sec > (unsigned long) update) { + wpa_printf(MSG_DEBUG, "OCSP: nextUpdate time in the past (response has expired)"); + return -1; + } + } + } + + /* singleExtensions [1] EXPLICIT Extensions OPTIONAL */ + if (pos < end) { + wpa_hexdump(MSG_MSGDUMP, "OCSP: singleExtensions", + pos, end - pos); + /* Ignore for now */ + } + + if (cert_status == 0 /* good */) + *res = TLS_OCSP_GOOD; + else if (cert_status == 1 /* revoked */) + *res = TLS_OCSP_REVOKED; + else + return -1; + return 0; +} + + +static enum tls_ocsp_result +tls_process_ocsp_responses(struct tlsv1_client *conn, + struct x509_certificate *cert, + struct x509_certificate *issuer, const u8 *resp, + size_t len) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + enum tls_ocsp_result res; + + pos = resp; + end = resp + len; + while (pos < end) { + /* SingleResponse ::= SEQUENCE */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected SEQUENCE (SingleResponse) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return TLS_OCSP_INVALID; + } + if (tls_process_ocsp_single_response(conn, cert, issuer, + hdr.payload, hdr.length, + &res) == 0) + return res; + pos = hdr.payload + hdr.length; + } + + wpa_printf(MSG_DEBUG, + "OCSP: Did not find a response matching the server certificate"); + return TLS_OCSP_NO_RESPONSE; +} + + +static enum tls_ocsp_result +tls_process_basic_ocsp_response(struct tlsv1_client *conn, + struct x509_certificate *srv_cert, + const u8 *resp, size_t len) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + const u8 *resp_data, *sign_value, *key_hash = NULL, *responses; + const u8 *resp_data_signed; + size_t resp_data_len, sign_value_len, responses_len; + size_t resp_data_signed_len; + struct x509_algorithm_identifier alg; + struct x509_certificate *certs = NULL, *last_cert = NULL; + struct x509_certificate *issuer, *signer; + struct x509_name name; /* used if key_hash == NULL */ + char buf[100]; + os_time_t produced_at; + enum tls_ocsp_result res; + + wpa_hexdump(MSG_MSGDUMP, "OCSP: BasicOCSPResponse", resp, len); + + os_memset(&name, 0, sizeof(name)); + + /* + * RFC 6960, 4.2.1: + * BasicOCSPResponse ::= SEQUENCE { + * tbsResponseData ResponseData, + * signatureAlgorithm AlgorithmIdentifier, + * signature BIT STRING, + * certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } + */ + + if (asn1_get_next(resp, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected SEQUENCE (BasicOCSPResponse) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return TLS_OCSP_INVALID; + } + pos = hdr.payload; + end = hdr.payload + hdr.length; + + /* ResponseData ::= SEQUENCE */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected SEQUENCE (ResponseData) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return TLS_OCSP_INVALID; + } + resp_data = hdr.payload; + resp_data_len = hdr.length; + resp_data_signed = pos; + pos = hdr.payload + hdr.length; + resp_data_signed_len = pos - resp_data_signed; + + /* signatureAlgorithm AlgorithmIdentifier */ + if (x509_parse_algorithm_identifier(pos, end - pos, &alg, &pos)) + return TLS_OCSP_INVALID; + + /* signature BIT STRING */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_BITSTRING) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected BITSTRING (signature) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return TLS_OCSP_INVALID; + } + if (hdr.length < 1) + return TLS_OCSP_INVALID; + pos = hdr.payload; + if (*pos) { + wpa_printf(MSG_DEBUG, "OCSP: BITSTRING - %d unused bits", *pos); + /* PKCS #1 v1.5 10.2.1: + * It is an error if the length in bits of the signature S is + * not a multiple of eight. + */ + return TLS_OCSP_INVALID; + } + sign_value = pos + 1; + sign_value_len = hdr.length - 1; + pos += hdr.length; + wpa_hexdump(MSG_MSGDUMP, "OCSP: signature", sign_value, sign_value_len); + + /* certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL */ + if (pos < end) { + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || + hdr.tag != 0) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected [0] EXPLICIT (certs) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return TLS_OCSP_INVALID; + } + wpa_hexdump(MSG_MSGDUMP, "OCSP: certs", + hdr.payload, hdr.length); + pos = hdr.payload; + end = hdr.payload + hdr.length; + while (pos < end) { + struct x509_certificate *cert; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected SEQUENCE (Certificate) - found class %d tag 0x%x", + hdr.class, hdr.tag); + goto fail; + } + + cert = x509_certificate_parse(hdr.payload, hdr.length); + if (!cert) + goto fail; + if (last_cert) { + last_cert->next = cert; + last_cert = cert; + } else { + last_cert = certs = cert; + } + pos = hdr.payload + hdr.length; + } + } + + /* + * ResponseData ::= SEQUENCE { + * version [0] EXPLICIT Version DEFAULT v1, + * responderID ResponderID, + * producedAt GeneralizedTime, + * responses SEQUENCE OF SingleResponse, + * responseExtensions [1] EXPLICIT Extensions OPTIONAL } + */ + pos = resp_data; + end = resp_data + resp_data_len; + wpa_hexdump(MSG_MSGDUMP, "OCSP: ResponseData", pos, end - pos); + + /* + * version [0] EXPLICIT Version DEFAULT v1 + * Version ::= INTEGER { v1(0) } + */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 && + hdr.class == ASN1_CLASS_CONTEXT_SPECIFIC && + hdr.tag == 0) { + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_INTEGER || + hdr.length != 1) { + wpa_printf(MSG_DEBUG, + "OCSP: No INTEGER (len=1) tag found for version field - found class %d tag 0x%x length %d", + hdr.class, hdr.tag, hdr.length); + goto fail; + } + wpa_printf(MSG_DEBUG, "OCSP: ResponseData version %u", + hdr.payload[0]); + if (hdr.payload[0] != 0) { + wpa_printf(MSG_DEBUG, + "OCSP: Unsupported ResponseData version %u", + hdr.payload[0]); + goto no_resp; + } + pos = hdr.payload + hdr.length; + } else { + wpa_printf(MSG_DEBUG, + "OCSP: Default ResponseData version (v1)"); + } + + /* + * ResponderID ::= CHOICE { + * byName [1] Name, + * byKey [2] KeyHash } + */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected CHOICE (ResponderID) - found class %d tag 0x%x", + hdr.class, hdr.tag); + goto fail; + } + + if (hdr.tag == 1) { + /* Name */ + if (x509_parse_name(hdr.payload, hdr.length, &name, &pos) < 0) + goto fail; + x509_name_string(&name, buf, sizeof(buf)); + wpa_printf(MSG_DEBUG, "OCSP: ResponderID byName Name: %s", buf); + } else if (hdr.tag == 2) { + /* KeyHash ::= OCTET STRING */ + if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected OCTET STRING (KeyHash) - found class %d tag 0x%x", + hdr.class, hdr.tag); + goto fail; + } + key_hash = hdr.payload; + wpa_hexdump(MSG_DEBUG, "OCSP: ResponderID byKey KeyHash", + key_hash, hdr.length); + if (hdr.length != SHA1_MAC_LEN) { + wpa_printf(MSG_DEBUG, + "OCSP: Unexpected byKey KeyHash length %u - expected %u for SHA-1", + hdr.length, SHA1_MAC_LEN); + goto fail; + } + pos = hdr.payload + hdr.length; + } else { + wpa_printf(MSG_DEBUG, "OCSP: Unexpected ResponderID CHOICE %u", + hdr.tag); + goto fail; + } + + /* producedAt GeneralizedTime */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_GENERALIZEDTIME || + x509_parse_time(hdr.payload, hdr.length, hdr.tag, + &produced_at) < 0) { + wpa_printf(MSG_DEBUG, "OCSP: Failed to parse producedAt"); + goto fail; + } + wpa_printf(MSG_DEBUG, "OCSP: producedAt %lu", + (unsigned long) produced_at); + pos = hdr.payload + hdr.length; + + /* responses SEQUENCE OF SingleResponse */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected SEQUENCE (responses) - found class %d tag 0x%x", + hdr.class, hdr.tag); + goto fail; + } + responses = hdr.payload; + responses_len = hdr.length; + wpa_hexdump(MSG_MSGDUMP, "OCSP: responses", responses, responses_len); + pos = hdr.payload + hdr.length; + + if (pos < end) { + /* responseExtensions [1] EXPLICIT Extensions OPTIONAL */ + wpa_hexdump(MSG_MSGDUMP, "OCSP: responseExtensions", + pos, end - pos); + /* Ignore for now. */ + } + + if (!srv_cert) { + wpa_printf(MSG_DEBUG, + "OCSP: Server certificate not known - cannot check OCSP response"); + goto no_resp; + } + + if (srv_cert->next) { + /* Issuer has already been verified in the chain */ + issuer = srv_cert->next; + } else { + /* Find issuer from the set of trusted certificates */ + for (issuer = conn->cred ? conn->cred->trusted_certs : NULL; + issuer; issuer = issuer->next) { + if (x509_name_compare(&srv_cert->issuer, + &issuer->subject) == 0) + break; + } + } + if (!issuer) { + wpa_printf(MSG_DEBUG, + "OCSP: Server certificate issuer not known - cannot check OCSP response"); + goto no_resp; + } + + if (ocsp_responder_id_match(issuer, &name, key_hash)) { + wpa_printf(MSG_DEBUG, + "OCSP: Server certificate issuer certificate matches ResponderID"); + signer = issuer; + } else { + for (signer = certs; signer; signer = signer->next) { + if (!ocsp_responder_id_match(signer, &name, key_hash) || + x509_name_compare(&srv_cert->issuer, + &issuer->subject) != 0 || + !(signer->ext_key_usage & + X509_EXT_KEY_USAGE_OCSP) || + x509_certificate_check_signature(issuer, signer) < + 0) + continue; + wpa_printf(MSG_DEBUG, + "OCSP: An extra certificate from the response matches ResponderID and is trusted as an OCSP signer"); + break; + } + if (!signer) { + wpa_printf(MSG_DEBUG, + "OCSP: Could not find OCSP signer certificate"); + goto no_resp; + } + } + + x509_free_name(&name); + os_memset(&name, 0, sizeof(name)); + x509_certificate_chain_free(certs); + certs = NULL; + + if (x509_check_signature(signer, &alg, sign_value, sign_value_len, + resp_data_signed, resp_data_signed_len) < 0) { + wpa_printf(MSG_DEBUG, "OCSP: Invalid signature"); + return TLS_OCSP_INVALID; + } + + res = tls_process_ocsp_responses(conn, srv_cert, issuer, + responses, responses_len); + if (res == TLS_OCSP_REVOKED) + srv_cert->ocsp_revoked = 1; + else if (res == TLS_OCSP_GOOD) + srv_cert->ocsp_good = 1; + return res; + +no_resp: + x509_free_name(&name); + x509_certificate_chain_free(certs); + return TLS_OCSP_NO_RESPONSE; + +fail: + x509_free_name(&name); + x509_certificate_chain_free(certs); + return TLS_OCSP_INVALID; +} + + +enum tls_ocsp_result tls_process_ocsp_response(struct tlsv1_client *conn, + const u8 *resp, size_t len) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + u8 resp_status; + struct asn1_oid oid; + char obuf[80]; + struct x509_certificate *cert; + enum tls_ocsp_result res = TLS_OCSP_NO_RESPONSE; + enum tls_ocsp_result res_first = res; + + wpa_hexdump(MSG_MSGDUMP, "TLSv1: OCSPResponse", resp, len); + + /* + * RFC 6960, 4.2.1: + * OCSPResponse ::= SEQUENCE { + * responseStatus OCSPResponseStatus, + * responseBytes [0] EXPLICIT ResponseBytes OPTIONAL } + */ + + if (asn1_get_next(resp, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected SEQUENCE (OCSPResponse) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return TLS_OCSP_INVALID; + } + pos = hdr.payload; + end = hdr.payload + hdr.length; + + /* OCSPResponseStatus ::= ENUMERATED */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_ENUMERATED || + hdr.length != 1) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected ENUMERATED (responseStatus) - found class %d tag 0x%x length %u", + hdr.class, hdr.tag, hdr.length); + return TLS_OCSP_INVALID; + } + resp_status = hdr.payload[0]; + wpa_printf(MSG_DEBUG, "OCSP: responseStatus %u", resp_status); + pos = hdr.payload + hdr.length; + if (resp_status != OCSP_RESP_STATUS_SUCCESSFUL) { + wpa_printf(MSG_DEBUG, "OCSP: No stapling result"); + return TLS_OCSP_NO_RESPONSE; + } + + /* responseBytes [0] EXPLICIT ResponseBytes OPTIONAL */ + if (pos == end) + return TLS_OCSP_NO_RESPONSE; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || + hdr.tag != 0) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected [0] EXPLICIT (responseBytes) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return TLS_OCSP_INVALID; + } + + /* + * ResponseBytes ::= SEQUENCE { + * responseType OBJECT IDENTIFIER, + * response OCTET STRING } + */ + + if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected SEQUENCE (ResponseBytes) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return TLS_OCSP_INVALID; + } + pos = hdr.payload; + end = hdr.payload + hdr.length; + + /* responseType OBJECT IDENTIFIER */ + if (asn1_get_oid(pos, end - pos, &oid, &pos)) { + wpa_printf(MSG_DEBUG, + "OCSP: Failed to parse OID (responseType)"); + return TLS_OCSP_INVALID; + } + asn1_oid_to_str(&oid, obuf, sizeof(obuf)); + wpa_printf(MSG_DEBUG, "OCSP: responseType %s", obuf); + if (!is_oid_basic_ocsp_resp(&oid)) { + wpa_printf(MSG_DEBUG, "OCSP: Ignore unsupported response type"); + return TLS_OCSP_NO_RESPONSE; + } + + /* response OCTET STRING */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected OCTET STRING (response) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return TLS_OCSP_INVALID; + } + + cert = conn->server_cert; + while (cert) { + if (!cert->ocsp_good && !cert->ocsp_revoked) { + char sbuf[128]; + + x509_name_string(&cert->subject, sbuf, sizeof(sbuf)); + wpa_printf(MSG_DEBUG, + "OCSP: Trying to find certificate status for %s", + sbuf); + + res = tls_process_basic_ocsp_response(conn, cert, + hdr.payload, + hdr.length); + if (cert == conn->server_cert) + res_first = res; + } + if (res == TLS_OCSP_REVOKED || cert->issuer_trusted) + break; + cert = cert->next; + } + return res == TLS_OCSP_REVOKED ? res : res_first; +} diff --git a/contrib/wpa/src/tls/tlsv1_client_read.c b/contrib/wpa/src/tls/tlsv1_client_read.c index 9ce96803753a..244c3cb06082 100644 --- a/contrib/wpa/src/tls/tlsv1_client_read.c +++ b/contrib/wpa/src/tls/tlsv1_client_read.c @@ -1,6 +1,6 @@ /* * TLSv1 client - read handshake message - * Copyright (c) 2006-2014, Jouni Malinen + * Copyright (c) 2006-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -27,6 +27,54 @@ static int tls_process_server_hello_done(struct tlsv1_client *conn, u8 ct, const u8 *in_data, size_t *in_len); +static int tls_version_disabled(struct tlsv1_client *conn, u16 ver) +{ + return (((conn->flags & TLS_CONN_DISABLE_TLSv1_0) && + ver == TLS_VERSION_1) || + ((conn->flags & TLS_CONN_DISABLE_TLSv1_1) && + ver == TLS_VERSION_1_1) || + ((conn->flags & TLS_CONN_DISABLE_TLSv1_2) && + ver == TLS_VERSION_1_2)); +} + + +static int tls_process_server_hello_extensions(struct tlsv1_client *conn, + const u8 *pos, size_t len) +{ + const u8 *end = pos + len; + + wpa_hexdump(MSG_MSGDUMP, "TLSv1: ServerHello extensions", + pos, len); + while (pos < end) { + u16 ext, elen; + + if (end - pos < 4) { + wpa_printf(MSG_INFO, "TLSv1: Truncated ServerHello extension header"); + return -1; + } + + ext = WPA_GET_BE16(pos); + pos += 2; + elen = WPA_GET_BE16(pos); + pos += 2; + + if (elen > end - pos) { + wpa_printf(MSG_INFO, "TLSv1: Truncated ServerHello extension"); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: ServerHello ExtensionType %u", + ext); + wpa_hexdump(MSG_DEBUG, "TLSv1: ServerHello extension data", + pos, elen); + + pos += elen; + } + + return 0; +} + + static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct, const u8 *in_data, size_t *in_len) { @@ -76,7 +124,8 @@ static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct, if (end - pos < 2) goto decode_error; tls_version = WPA_GET_BE16(pos); - if (!tls_version_ok(tls_version)) { + if (!tls_version_ok(tls_version) || + tls_version_disabled(conn, tls_version)) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in " "ServerHello %u.%u", pos[0], pos[1]); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, @@ -165,8 +214,24 @@ static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct, } pos++; + if (end - pos >= 2) { + u16 ext_len; + + ext_len = WPA_GET_BE16(pos); + pos += 2; + if (end - pos < ext_len) { + wpa_printf(MSG_INFO, + "TLSv1: Invalid ServerHello extension length: %u (left: %u)", + ext_len, (unsigned int) (end - pos)); + goto decode_error; + } + + if (tls_process_server_hello_extensions(conn, pos, ext_len)) + goto decode_error; + pos += ext_len; + } + if (end != pos) { - /* TODO: ServerHello extensions */ wpa_hexdump(MSG_DEBUG, "TLSv1: Unexpected extra data in the " "end of ServerHello", pos, end - pos); goto decode_error; @@ -211,6 +276,73 @@ decode_error: } +static void tls_peer_cert_event(struct tlsv1_client *conn, int depth, + struct x509_certificate *cert) +{ + union tls_event_data ev; + struct wpabuf *cert_buf = NULL; +#ifdef CONFIG_SHA256 + u8 hash[32]; +#endif /* CONFIG_SHA256 */ + char subject[128]; + + if (!conn->event_cb) + return; + + os_memset(&ev, 0, sizeof(ev)); + if (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; + } +#ifdef CONFIG_SHA256 + if (cert_buf) { + const u8 *addr[1]; + size_t len[1]; + addr[0] = wpabuf_head(cert_buf); + len[0] = wpabuf_len(cert_buf); + if (sha256_vector(1, addr, len, hash) == 0) { + ev.peer_cert.hash = hash; + ev.peer_cert.hash_len = sizeof(hash); + } + } +#endif /* CONFIG_SHA256 */ + + ev.peer_cert.depth = depth; + x509_name_string(&cert->subject, subject, sizeof(subject)); + ev.peer_cert.subject = subject; + + conn->event_cb(conn->cb_ctx, TLS_PEER_CERTIFICATE, &ev); + wpabuf_free(cert_buf); +} + + +static void tls_cert_chain_failure_event(struct tlsv1_client *conn, int depth, + struct x509_certificate *cert, + enum tls_fail_reason reason, + const char *reason_txt) +{ + struct wpabuf *cert_buf = NULL; + union tls_event_data ev; + char subject[128]; + + if (!conn->event_cb || !cert) + return; + + os_memset(&ev, 0, sizeof(ev)); + ev.cert_fail.depth = depth; + x509_name_string(&cert->subject, subject, sizeof(subject)); + ev.peer_cert.subject = subject; + ev.cert_fail.reason = reason; + ev.cert_fail.reason_txt = reason_txt; + cert_buf = wpabuf_alloc_copy(cert->cert_start, + cert->cert_len); + ev.cert_fail.cert = cert_buf; + conn->event_cb(conn->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev); + wpabuf_free(cert_buf); +} + + static int tls_process_certificate(struct tlsv1_client *conn, u8 ct, const u8 *in_data, size_t *in_len) { @@ -354,6 +486,8 @@ static int tls_process_certificate(struct tlsv1_client *conn, u8 ct, return -1; } + tls_peer_cert_event(conn, idx, cert); + if (last == NULL) chain = cert; else @@ -364,31 +498,99 @@ static int tls_process_certificate(struct tlsv1_client *conn, u8 ct, pos += cert_len; } - if (conn->cred && - x509_certificate_chain_validate(conn->cred->trusted_certs, chain, - &reason, conn->disable_time_checks) - < 0) { + if (conn->cred && conn->cred->server_cert_only && chain) { + u8 hash[SHA256_MAC_LEN]; + char buf[128]; + + wpa_printf(MSG_DEBUG, + "TLSv1: Validate server certificate hash"); + x509_name_string(&chain->subject, buf, sizeof(buf)); + wpa_printf(MSG_DEBUG, "TLSv1: 0: %s", buf); + if (sha256_vector(1, &chain->cert_start, &chain->cert_len, + hash) < 0 || + os_memcmp(conn->cred->srv_cert_hash, hash, + SHA256_MAC_LEN) != 0) { + wpa_printf(MSG_DEBUG, + "TLSv1: Server certificate hash mismatch"); + wpa_hexdump(MSG_MSGDUMP, "TLSv1: SHA256 hash", + hash, SHA256_MAC_LEN); + if (conn->event_cb) { + union tls_event_data ev; + + os_memset(&ev, 0, sizeof(ev)); + ev.cert_fail.reason = TLS_FAIL_UNSPECIFIED; + ev.cert_fail.reason_txt = + "Server certificate mismatch"; + ev.cert_fail.subject = buf; + conn->event_cb(conn->cb_ctx, + TLS_CERT_CHAIN_FAILURE, &ev); + } + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_BAD_CERTIFICATE); + x509_certificate_chain_free(chain); + return -1; + } + } else if (conn->cred && conn->cred->cert_probe) { + wpa_printf(MSG_DEBUG, + "TLSv1: Reject server certificate on probe-only rune"); + if (conn->event_cb) { + union tls_event_data ev; + char buf[128]; + + os_memset(&ev, 0, sizeof(ev)); + ev.cert_fail.reason = TLS_FAIL_SERVER_CHAIN_PROBE; + ev.cert_fail.reason_txt = + "Server certificate chain probe"; + if (chain) { + x509_name_string(&chain->subject, buf, + sizeof(buf)); + ev.cert_fail.subject = buf; + } + conn->event_cb(conn->cb_ctx, TLS_CERT_CHAIN_FAILURE, + &ev); + } + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_BAD_CERTIFICATE); + x509_certificate_chain_free(chain); + return -1; + } else if (conn->cred && conn->cred->ca_cert_verify && + x509_certificate_chain_validate( + conn->cred->trusted_certs, chain, &reason, + !!(conn->flags & TLS_CONN_DISABLE_TIME_CHECKS)) + < 0) { int tls_reason; wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain " "validation failed (reason=%d)", reason); switch (reason) { case X509_VALIDATE_BAD_CERTIFICATE: tls_reason = TLS_ALERT_BAD_CERTIFICATE; + tls_cert_chain_failure_event( + conn, 0, chain, TLS_FAIL_BAD_CERTIFICATE, + "bad certificate"); break; case X509_VALIDATE_UNSUPPORTED_CERTIFICATE: tls_reason = TLS_ALERT_UNSUPPORTED_CERTIFICATE; break; case X509_VALIDATE_CERTIFICATE_REVOKED: tls_reason = TLS_ALERT_CERTIFICATE_REVOKED; + tls_cert_chain_failure_event( + conn, 0, chain, TLS_FAIL_REVOKED, + "certificate revoked"); break; case X509_VALIDATE_CERTIFICATE_EXPIRED: tls_reason = TLS_ALERT_CERTIFICATE_EXPIRED; + tls_cert_chain_failure_event( + conn, 0, chain, TLS_FAIL_EXPIRED, + "certificate has expired or is not yet valid"); break; case X509_VALIDATE_CERTIFICATE_UNKNOWN: tls_reason = TLS_ALERT_CERTIFICATE_UNKNOWN; break; case X509_VALIDATE_UNKNOWN_CA: tls_reason = TLS_ALERT_UNKNOWN_CA; + tls_cert_chain_failure_event( + conn, 0, chain, TLS_FAIL_UNTRUSTED, + "unknown CA"); break; default: tls_reason = TLS_ALERT_BAD_CERTIFICATE; @@ -399,7 +601,25 @@ static int tls_process_certificate(struct tlsv1_client *conn, u8 ct, return -1; } - x509_certificate_chain_free(chain); + if (conn->cred && !conn->cred->server_cert_only && chain && + (chain->extensions_present & X509_EXT_EXT_KEY_USAGE) && + !(chain->ext_key_usage & + (X509_EXT_KEY_USAGE_ANY | X509_EXT_KEY_USAGE_SERVER_AUTH))) { + tls_cert_chain_failure_event( + conn, 0, chain, TLS_FAIL_BAD_CERTIFICATE, + "certificate not allowed for server authentication"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_BAD_CERTIFICATE); + x509_certificate_chain_free(chain); + return -1; + } + + if (conn->flags & TLS_CONN_REQUEST_OCSP) { + x509_certificate_chain_free(conn->server_cert); + conn->server_cert = chain; + } else { + x509_certificate_chain_free(chain); + } *in_len = end - in_data; @@ -507,7 +727,7 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn, server_params_end = pos; if (key_exchange == TLS_KEY_X_DHE_RSA) { - u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN]; + u8 hash[64]; int hlen; if (conn->rl.tls_version == TLS_VERSION_1_2) { @@ -524,18 +744,21 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn, */ if (end - pos < 2) goto fail; - if (pos[0] != TLS_HASH_ALG_SHA256 || + if ((pos[0] != TLS_HASH_ALG_SHA256 && + pos[0] != TLS_HASH_ALG_SHA384 && + pos[0] != TLS_HASH_ALG_SHA512) || pos[1] != TLS_SIGN_ALG_RSA) { wpa_printf(MSG_DEBUG, "TLSv1.2: Unsupported hash(%u)/signature(%u) algorithm", pos[0], pos[1]); goto fail; } - pos += 2; hlen = tlsv12_key_x_server_params_hash( - conn->rl.tls_version, conn->client_random, + conn->rl.tls_version, pos[0], + conn->client_random, conn->server_random, server_params, server_params_end - server_params, hash); + pos += 2; #else /* CONFIG_TLSV12 */ goto fail; #endif /* CONFIG_TLSV12 */ @@ -567,6 +790,229 @@ fail: } +static enum tls_ocsp_result +tls_process_certificate_status_ocsp_response(struct tlsv1_client *conn, + const u8 *pos, size_t len) +{ + const u8 *end = pos + len; + u32 ocsp_resp_len; + + /* opaque OCSPResponse<1..2^24-1>; */ + if (end - pos < 3) { + wpa_printf(MSG_INFO, "TLSv1: Too short OCSPResponse"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return TLS_OCSP_INVALID; + } + ocsp_resp_len = WPA_GET_BE24(pos); + pos += 3; + if (end - pos < ocsp_resp_len) { + wpa_printf(MSG_INFO, "TLSv1: Truncated OCSPResponse"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return TLS_OCSP_INVALID; + } + + return tls_process_ocsp_response(conn, pos, ocsp_resp_len); +} + + +static int tls_process_certificate_status(struct tlsv1_client *conn, u8 ct, + const u8 *in_data, size_t *in_len) +{ + const u8 *pos, *end; + size_t left, len; + u8 type, status_type; + enum tls_ocsp_result res; + struct x509_certificate *cert; + int depth; + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { + wpa_printf(MSG_DEBUG, + "TLSv1: Expected Handshake; received content type 0x%x", + ct); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 4) { + wpa_printf(MSG_DEBUG, + "TLSv1: Too short CertificateStatus (left=%lu)", + (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + + type = *pos++; + len = WPA_GET_BE24(pos); + pos += 3; + left -= 4; + + if (len > left) { + wpa_printf(MSG_DEBUG, + "TLSv1: Mismatch in CertificateStatus length (len=%lu != left=%lu)", + (unsigned long) len, (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + + end = pos + len; + + if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE_STATUS) { + wpa_printf(MSG_DEBUG, + "TLSv1: Received unexpected handshake message %d (expected CertificateStatus)", + type); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Received CertificateStatus"); + + /* + * struct { + * CertificateStatusType status_type; + * select (status_type) { + * case ocsp: OCSPResponse; + * case ocsp_multi: OCSPResponseList; + * } response; + * } CertificateStatus; + */ + if (end - pos < 1) { + wpa_printf(MSG_INFO, "TLSv1: Too short CertificateStatus"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + status_type = *pos++; + wpa_printf(MSG_DEBUG, "TLSv1: CertificateStatus status_type %u", + status_type); + + if (status_type == 1 /* ocsp */) { + res = tls_process_certificate_status_ocsp_response( + conn, pos, end - pos); + } else if (status_type == 2 /* ocsp_multi */) { + int good = 0, revoked = 0; + u32 resp_len; + + res = TLS_OCSP_NO_RESPONSE; + + /* + * opaque OCSPResponse<0..2^24-1>; + * + * struct { + * OCSPResponse ocsp_response_list<1..2^24-1>; + * } OCSPResponseList; + */ + if (end - pos < 3) { + wpa_printf(MSG_DEBUG, + "TLSv1: Truncated OCSPResponseList"); + res = TLS_OCSP_INVALID; + goto done; + } + resp_len = WPA_GET_BE24(pos); + pos += 3; + if (end - pos < resp_len) { + wpa_printf(MSG_DEBUG, + "TLSv1: Truncated OCSPResponseList(len=%u)", + resp_len); + res = TLS_OCSP_INVALID; + goto done; + } + end = pos + resp_len; + + while (end - pos >= 3) { + resp_len = WPA_GET_BE24(pos); + pos += 3; + if (resp_len > end - pos) { + wpa_printf(MSG_DEBUG, + "TLSv1: Truncated OCSPResponse(len=%u; left=%d) in ocsp_multi", + resp_len, (int) (end - pos)); + res = TLS_OCSP_INVALID; + break; + } + if (!resp_len) + continue; /* Skip an empty response */ + res = tls_process_certificate_status_ocsp_response( + conn, pos - 3, resp_len + 3); + if (res == TLS_OCSP_REVOKED) + revoked++; + else if (res == TLS_OCSP_GOOD) + good++; + pos += resp_len; + } + + if (revoked) + res = TLS_OCSP_REVOKED; + else if (good) + res = TLS_OCSP_GOOD; + } else { + wpa_printf(MSG_DEBUG, + "TLSv1: Ignore unsupported CertificateStatus"); + goto skip; + } + +done: + if (res == TLS_OCSP_REVOKED) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_CERTIFICATE_REVOKED); + for (cert = conn->server_cert, depth = 0; cert; + cert = cert->next, depth++) { + if (cert->ocsp_revoked) { + tls_cert_chain_failure_event( + conn, depth, cert, TLS_FAIL_REVOKED, + "certificate revoked"); + } + } + return -1; + } + + if (conn->flags & TLS_CONN_REQUIRE_OCSP_ALL) { + /* + * Verify that each certificate on the chain that is not part + * of the trusted certificates has a good status. If not, + * terminate handshake. + */ + for (cert = conn->server_cert, depth = 0; cert; + cert = cert->next, depth++) { + if (!cert->ocsp_good) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE); + tls_cert_chain_failure_event( + conn, depth, cert, + TLS_FAIL_UNSPECIFIED, + "bad certificate status response"); + return -1; + } + if (cert->issuer_trusted) + break; + } + } + + if ((conn->flags & TLS_CONN_REQUIRE_OCSP) && res != TLS_OCSP_GOOD) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + res == TLS_OCSP_INVALID ? TLS_ALERT_DECODE_ERROR : + TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE); + if (conn->server_cert) + tls_cert_chain_failure_event( + conn, 0, conn->server_cert, + TLS_FAIL_UNSPECIFIED, + "bad certificate status response"); + return -1; + } + + conn->ocsp_resp_received = 1; + +skip: + *in_len = end - in_data; + + conn->state = SERVER_KEY_EXCHANGE; + + return 0; +} + + static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct, const u8 *in_data, size_t *in_len) { @@ -608,6 +1054,10 @@ static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct, end = pos + len; + if ((conn->flags & TLS_CONN_REQUEST_OCSP) && + type == TLS_HANDSHAKE_TYPE_CERTIFICATE_STATUS) + return tls_process_certificate_status(conn, ct, in_data, + in_len); if (type == TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST) return tls_process_certificate_request(conn, ct, in_data, in_len); @@ -617,7 +1067,9 @@ static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct, if (type != TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE) { wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " "message %d (expected ServerKeyExchange/" - "CertificateRequest/ServerHelloDone)", type); + "CertificateRequest/ServerHelloDone%s)", type, + (conn->flags & TLS_CONN_REQUEST_OCSP) ? + "/CertificateStatus" : ""); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; @@ -771,6 +1223,15 @@ static int tls_process_server_hello_done(struct tlsv1_client *conn, u8 ct, wpa_printf(MSG_DEBUG, "TLSv1: Received ServerHelloDone"); + if ((conn->flags & TLS_CONN_REQUIRE_OCSP) && + !conn->ocsp_resp_received) { + wpa_printf(MSG_INFO, + "TLSv1: No OCSP response received - reject handshake"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE); + return -1; + } + *in_len = end - in_data; conn->state = CLIENT_KEY_EXCHANGE; diff --git a/contrib/wpa/src/tls/tlsv1_client_write.c b/contrib/wpa/src/tls/tlsv1_client_write.c index d192f44f4088..04d895e61926 100644 --- a/contrib/wpa/src/tls/tlsv1_client_write.c +++ b/contrib/wpa/src/tls/tlsv1_client_write.c @@ -1,6 +1,6 @@ /* * TLSv1 client - write handshake message - * Copyright (c) 2006-2014, Jouni Malinen + * Copyright (c) 2006-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -47,8 +47,28 @@ u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len) u8 *hello, *end, *pos, *hs_length, *hs_start, *rhdr; struct os_time now; size_t len, i; + u8 *ext_start; + u16 tls_version = TLS_VERSION; - wpa_printf(MSG_DEBUG, "TLSv1: Send ClientHello"); + /* Pick the highest locally enabled TLS version */ +#ifdef CONFIG_TLSV12 + if ((conn->flags & TLS_CONN_DISABLE_TLSv1_2) && + tls_version == TLS_VERSION_1_2) + tls_version = TLS_VERSION_1_1; +#endif /* CONFIG_TLSV12 */ +#ifdef CONFIG_TLSV11 + if ((conn->flags & TLS_CONN_DISABLE_TLSv1_1) && + tls_version == TLS_VERSION_1_1) + tls_version = TLS_VERSION_1; +#endif /* CONFIG_TLSV11 */ + if ((conn->flags & TLS_CONN_DISABLE_TLSv1_0) && + tls_version == TLS_VERSION_1) { + wpa_printf(MSG_INFO, "TLSv1: No TLS version allowed"); + return NULL; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Send ClientHello (ver %s)", + tls_version_str(tls_version)); *out_len = 0; os_get_time(&now); @@ -61,7 +81,7 @@ u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len) wpa_hexdump(MSG_MSGDUMP, "TLSv1: client_random", conn->client_random, TLS_RANDOM_LEN); - len = 100 + conn->num_cipher_suites * 2 + conn->client_hello_ext_len; + len = 150 + conn->num_cipher_suites * 2 + conn->client_hello_ext_len; hello = os_malloc(len); if (hello == NULL) return NULL; @@ -81,7 +101,7 @@ u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len) pos += 3; /* body - ClientHello */ /* ProtocolVersion client_version */ - WPA_PUT_BE16(pos, TLS_VERSION); + WPA_PUT_BE16(pos, tls_version); pos += 2; /* Random random: uint32 gmt_unix_time, opaque random_bytes */ os_memcpy(pos, conn->client_random, TLS_RANDOM_LEN); @@ -101,12 +121,124 @@ u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len) *pos++ = 1; *pos++ = TLS_COMPRESSION_NULL; + /* Extension */ + ext_start = pos; + pos += 2; + +#ifdef CONFIG_TLSV12 + if (conn->rl.tls_version >= TLS_VERSION_1_2) { + /* + * Add signature_algorithms extension since we support only + * SHA256 (and not the default SHA1) with TLSv1.2. + */ + /* ExtensionsType extension_type = signature_algorithms(13) */ + WPA_PUT_BE16(pos, TLS_EXT_SIGNATURE_ALGORITHMS); + pos += 2; + /* opaque extension_data<0..2^16-1> length */ + WPA_PUT_BE16(pos, 8); + pos += 2; + /* supported_signature_algorithms<2..2^16-2> length */ + WPA_PUT_BE16(pos, 6); + pos += 2; + /* supported_signature_algorithms */ + *pos++ = TLS_HASH_ALG_SHA512; + *pos++ = TLS_SIGN_ALG_RSA; + *pos++ = TLS_HASH_ALG_SHA384; + *pos++ = TLS_SIGN_ALG_RSA; + *pos++ = TLS_HASH_ALG_SHA256; + *pos++ = TLS_SIGN_ALG_RSA; + } +#endif /* CONFIG_TLSV12 */ + if (conn->client_hello_ext) { os_memcpy(pos, conn->client_hello_ext, conn->client_hello_ext_len); pos += conn->client_hello_ext_len; } + if (conn->flags & TLS_CONN_REQUEST_OCSP) { + wpa_printf(MSG_DEBUG, + "TLSv1: Add status_request extension for OCSP stapling"); + /* ExtensionsType extension_type = status_request(5) */ + WPA_PUT_BE16(pos, TLS_EXT_STATUS_REQUEST); + pos += 2; + /* opaque extension_data<0..2^16-1> length */ + WPA_PUT_BE16(pos, 5); + pos += 2; + + /* + * RFC 6066, 8: + * struct { + * CertificateStatusType status_type; + * select (status_type) { + * case ocsp: OCSPStatusRequest; + * } request; + * } CertificateStatusRequest; + * + * enum { ocsp(1), (255) } CertificateStatusType; + */ + *pos++ = 1; /* status_type = ocsp(1) */ + + /* + * struct { + * ResponderID responder_id_list<0..2^16-1>; + * Extensions request_extensions; + * } OCSPStatusRequest; + * + * opaque ResponderID<1..2^16-1>; + * opaque Extensions<0..2^16-1>; + */ + WPA_PUT_BE16(pos, 0); /* responder_id_list(empty) */ + pos += 2; + WPA_PUT_BE16(pos, 0); /* request_extensions(empty) */ + pos += 2; + + wpa_printf(MSG_DEBUG, + "TLSv1: Add status_request_v2 extension for OCSP stapling"); + /* ExtensionsType extension_type = status_request_v2(17) */ + WPA_PUT_BE16(pos, TLS_EXT_STATUS_REQUEST_V2); + pos += 2; + /* opaque extension_data<0..2^16-1> length */ + WPA_PUT_BE16(pos, 7); + pos += 2; + + /* + * RFC 6961, 2.2: + * struct { + * CertificateStatusType status_type; + * uint16 request_length; + * select (status_type) { + * case ocsp: OCSPStatusRequest; + * case ocsp_multi: OCSPStatusRequest; + * } request; + * } CertificateStatusRequestItemV2; + * + * enum { ocsp(1), ocsp_multi(2), (255) } CertificateStatusType; + * + * struct { + * CertificateStatusRequestItemV2 + * certificate_status_req_list<1..2^16-1>; + * } CertificateStatusRequestListV2; + */ + + /* certificate_status_req_list<1..2^16-1> */ + WPA_PUT_BE16(pos, 5); + pos += 2; + + /* CertificateStatusRequestItemV2 */ + *pos++ = 2; /* status_type = ocsp_multi(2) */ + /* OCSPStatusRequest as shown above for v1 */ + WPA_PUT_BE16(pos, 0); /* responder_id_list(empty) */ + pos += 2; + WPA_PUT_BE16(pos, 0); /* request_extensions(empty) */ + pos += 2; + } + + if (pos == ext_start + 2) + pos -= 2; /* no extensions */ + else + WPA_PUT_BE16(ext_start, pos - ext_start - 2); + WPA_PUT_BE24(hs_length, pos - hs_length - 3); tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); @@ -134,6 +266,11 @@ static int tls_write_client_certificate(struct tlsv1_client *conn, struct x509_certificate *cert; pos = *msgpos; + if (TLS_RECORD_HEADER_LEN + 1 + 3 + 3 > end - pos) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } wpa_printf(MSG_DEBUG, "TLSv1: Send Certificate"); rhdr = pos; @@ -154,7 +291,7 @@ static int tls_write_client_certificate(struct tlsv1_client *conn, pos += 3; cert = conn->cred ? conn->cred->cert : NULL; while (cert) { - if (pos + 3 + cert->cert_len > end) { + if (3 + cert->cert_len > (size_t) (end - pos)) { wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space " "for Certificate (cert_len=%lu left=%lu)", (unsigned long) cert->cert_len, @@ -265,9 +402,16 @@ static int tlsv1_key_x_dh(struct tlsv1_client *conn, u8 **pos, u8 *end) wpa_hexdump(MSG_DEBUG, "TLSv1: DH Yc (client's public value)", dh_yc, dh_yc_len); + if (end - *pos < 2) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(csecret); + os_free(dh_yc); + return -1; + } WPA_PUT_BE16(*pos, dh_yc_len); *pos += 2; - if (*pos + dh_yc_len > end) { + if (dh_yc_len > (size_t) (end - *pos)) { wpa_printf(MSG_DEBUG, "TLSv1: Not enough room in the " "message buffer for Yc"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, @@ -789,6 +933,8 @@ static u8 * tls_send_change_cipher_spec(struct tlsv1_client *conn, wpa_printf(MSG_DEBUG, "TLSv1: Session resumption completed " "successfully"); + if (!conn->session_resumed && conn->use_session_ticket) + conn->session_resumed = 1; conn->state = ESTABLISHED; return msg; diff --git a/contrib/wpa/src/tls/tlsv1_common.c b/contrib/wpa/src/tls/tlsv1_common.c index dabc12a12978..6b28417e499c 100644 --- a/contrib/wpa/src/tls/tlsv1_common.c +++ b/contrib/wpa/src/tls/tlsv1_common.c @@ -335,7 +335,7 @@ int tls_prf(u16 ver, const u8 *secret, size_t secret_len, const char *label, #ifdef CONFIG_TLSV12 -int tlsv12_key_x_server_params_hash(u16 tls_version, +int tlsv12_key_x_server_params_hash(u16 tls_version, u8 hash_alg, const u8 *client_random, const u8 *server_random, const u8 *server_params, @@ -343,14 +343,30 @@ int tlsv12_key_x_server_params_hash(u16 tls_version, { size_t hlen; struct crypto_hash *ctx; + enum crypto_hash_alg alg; - ctx = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, 0); + switch (hash_alg) { + case TLS_HASH_ALG_SHA256: + alg = CRYPTO_HASH_ALG_SHA256; + hlen = SHA256_MAC_LEN; + break; + case TLS_HASH_ALG_SHA384: + alg = CRYPTO_HASH_ALG_SHA384; + hlen = 48; + break; + case TLS_HASH_ALG_SHA512: + alg = CRYPTO_HASH_ALG_SHA512; + hlen = 64; + break; + default: + return -1; + } + ctx = crypto_hash_init(alg, NULL, 0); if (ctx == NULL) return -1; crypto_hash_update(ctx, client_random, TLS_RANDOM_LEN); crypto_hash_update(ctx, server_random, TLS_RANDOM_LEN); crypto_hash_update(ctx, server_params, server_params_len); - hlen = SHA256_MAC_LEN; if (crypto_hash_finish(ctx, hash, &hlen) < 0) return -1; @@ -469,6 +485,21 @@ int tls_verify_signature(u16 tls_version, struct crypto_public_key *pk, wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = SHA-256"); decrypted = buf + 19; buflen -= 19; + } else if (buflen >= 19 + 48 && + os_memcmp(buf, "\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01" + "\x65\x03\x04\x02\x02\x05\x00\x04\x30", 19) == 0) + { + wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = SHA-384"); + decrypted = buf + 19; + buflen -= 19; + } else if (buflen >= 19 + 64 && + os_memcmp(buf, "\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01" + "\x65\x03\x04\x02\x03\x05\x00\x04\x40", 19) == 0) + { + wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = SHA-512"); + decrypted = buf + 19; + buflen -= 19; + } else { wpa_printf(MSG_DEBUG, "TLSv1.2: Unrecognized DigestInfo"); os_free(buf); diff --git a/contrib/wpa/src/tls/tlsv1_common.h b/contrib/wpa/src/tls/tlsv1_common.h index 26e68af16606..e30b15a030a8 100644 --- a/contrib/wpa/src/tls/tlsv1_common.h +++ b/contrib/wpa/src/tls/tlsv1_common.h @@ -169,6 +169,8 @@ enum { #define TLS_EXT_TRUSTED_CA_KEYS 3 /* RFC 4366 */ #define TLS_EXT_TRUNCATED_HMAC 4 /* RFC 4366 */ #define TLS_EXT_STATUS_REQUEST 5 /* RFC 4366 */ +#define TLS_EXT_SIGNATURE_ALGORITHMS 13 /* RFC 5246 */ +#define TLS_EXT_STATUS_REQUEST_V2 17 /* RFC 6961 */ #define TLS_EXT_SESSION_TICKET 35 /* RFC 4507 */ #define TLS_EXT_PAC_OPAQUE TLS_EXT_SESSION_TICKET /* EAP-FAST terminology */ @@ -257,7 +259,8 @@ int tls_version_ok(u16 ver); const char * tls_version_str(u16 ver); int tls_prf(u16 ver, const u8 *secret, size_t secret_len, const char *label, const u8 *seed, size_t seed_len, u8 *out, size_t outlen); -int tlsv12_key_x_server_params_hash(u16 tls_version, const u8 *client_random, +int tlsv12_key_x_server_params_hash(u16 tls_version, u8 hash_Alg, + const u8 *client_random, const u8 *server_random, const u8 *server_params, size_t server_params_len, u8 *hash); diff --git a/contrib/wpa/src/tls/tlsv1_cred.c b/contrib/wpa/src/tls/tlsv1_cred.c index 1ea6827b898e..52c1ae0143da 100644 --- a/contrib/wpa/src/tls/tlsv1_cred.c +++ b/contrib/wpa/src/tls/tlsv1_cred.c @@ -1,6 +1,6 @@ /* * TLSv1 credentials - * Copyright (c) 2006-2009, Jouni Malinen + * Copyright (c) 2006-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -11,6 +11,9 @@ #include "common.h" #include "base64.h" #include "crypto/crypto.h" +#include "crypto/sha1.h" +#include "pkcs5.h" +#include "pkcs8.h" #include "x509v3.h" #include "tlsv1_cred.h" @@ -33,6 +36,8 @@ void tlsv1_cred_free(struct tlsv1_credentials *cred) crypto_private_key_free(cred->key); os_free(cred->dh_p); os_free(cred->dh_g); + os_free(cred->ocsp_stapling_response); + os_free(cred->ocsp_stapling_response_multi); os_free(cred); } @@ -190,6 +195,43 @@ int tlsv1_set_ca_cert(struct tlsv1_credentials *cred, const char *cert, const u8 *cert_blob, size_t cert_blob_len, const char *path) { + if (cert && os_strncmp(cert, "hash://", 7) == 0) { + const char *pos = cert + 7; + if (os_strncmp(pos, "server/sha256/", 14) != 0) { + wpa_printf(MSG_DEBUG, + "TLSv1: Unsupported ca_cert hash value '%s'", + cert); + return -1; + } + pos += 14; + if (os_strlen(pos) != 32 * 2) { + wpa_printf(MSG_DEBUG, + "TLSv1: Unexpected SHA256 hash length in ca_cert '%s'", + cert); + return -1; + } + if (hexstr2bin(pos, cred->srv_cert_hash, 32) < 0) { + wpa_printf(MSG_DEBUG, + "TLSv1: Invalid SHA256 hash value in ca_cert '%s'", + cert); + return -1; + } + cred->server_cert_only = 1; + cred->ca_cert_verify = 0; + wpa_printf(MSG_DEBUG, + "TLSv1: Checking only server certificate match"); + return 0; + } + + if (cert && os_strncmp(cert, "probe://", 8) == 0) { + cred->cert_probe = 1; + cred->ca_cert_verify = 0; + wpa_printf(MSG_DEBUG, "TLSv1: Only probe server certificate"); + return 0; + } + + cred->ca_cert_verify = cert || cert_blob || path; + if (tlsv1_set_cert_chain(&cred->trusted_certs, cert, cert_blob, cert_blob_len) < 0) return -1; @@ -288,6 +330,735 @@ static struct crypto_private_key * tlsv1_set_key_enc_pem(const u8 *key, } +#ifdef PKCS12_FUNCS + +static int oid_is_rsadsi(struct asn1_oid *oid) +{ + return oid->len >= 4 && + oid->oid[0] == 1 /* iso */ && + oid->oid[1] == 2 /* member-body */ && + oid->oid[2] == 840 /* us */ && + oid->oid[3] == 113549 /* rsadsi */; +} + + +static int pkcs12_is_bagtype_oid(struct asn1_oid *oid, unsigned long type) +{ + return oid->len == 9 && + oid_is_rsadsi(oid) && + oid->oid[4] == 1 /* pkcs */ && + oid->oid[5] == 12 /* pkcs-12 */ && + oid->oid[6] == 10 && + oid->oid[7] == 1 /* bagtypes */ && + oid->oid[8] == type; +} + + +static int is_oid_pkcs7(struct asn1_oid *oid) +{ + return oid->len == 7 && + oid->oid[0] == 1 /* iso */ && + oid->oid[1] == 2 /* member-body */ && + oid->oid[2] == 840 /* us */ && + oid->oid[3] == 113549 /* rsadsi */ && + oid->oid[4] == 1 /* pkcs */ && + oid->oid[5] == 7 /* pkcs-7 */; +} + + +static int is_oid_pkcs7_data(struct asn1_oid *oid) +{ + return is_oid_pkcs7(oid) && oid->oid[6] == 1 /* data */; +} + + +static int is_oid_pkcs7_enc_data(struct asn1_oid *oid) +{ + return is_oid_pkcs7(oid) && oid->oid[6] == 6 /* encryptedData */; +} + + +static int is_oid_pkcs9(struct asn1_oid *oid) +{ + return oid->len >= 6 && + oid->oid[0] == 1 /* iso */ && + oid->oid[1] == 2 /* member-body */ && + oid->oid[2] == 840 /* us */ && + oid->oid[3] == 113549 /* rsadsi */ && + oid->oid[4] == 1 /* pkcs */ && + oid->oid[5] == 9 /* pkcs-9 */; +} + + +static int is_oid_pkcs9_friendly_name(struct asn1_oid *oid) +{ + return oid->len == 7 && is_oid_pkcs9(oid) && + oid->oid[6] == 20; +} + + +static int is_oid_pkcs9_local_key_id(struct asn1_oid *oid) +{ + return oid->len == 7 && is_oid_pkcs9(oid) && + oid->oid[6] == 21; +} + + +static int is_oid_pkcs9_x509_cert(struct asn1_oid *oid) +{ + return oid->len == 8 && is_oid_pkcs9(oid) && + oid->oid[6] == 22 /* certTypes */ && + oid->oid[7] == 1 /* x509Certificate */; +} + + +static int pkcs12_keybag(struct tlsv1_credentials *cred, + const u8 *buf, size_t len) +{ + /* TODO */ + return 0; +} + + +static int pkcs12_pkcs8_keybag(struct tlsv1_credentials *cred, + const u8 *buf, size_t len, + const char *passwd) +{ + struct crypto_private_key *key; + + /* PKCS8ShroudedKeyBag ::= EncryptedPrivateKeyInfo */ + key = pkcs8_enc_key_import(buf, len, passwd); + if (!key) + return -1; + + wpa_printf(MSG_DEBUG, + "PKCS #12: Successfully decrypted PKCS8ShroudedKeyBag"); + crypto_private_key_free(cred->key); + cred->key = key; + + return 0; +} + + +static int pkcs12_certbag(struct tlsv1_credentials *cred, + const u8 *buf, size_t len) +{ + struct asn1_hdr hdr; + struct asn1_oid oid; + char obuf[80]; + const u8 *pos, *end; + + /* + * CertBag ::= SEQUENCE { + * certId BAG-TYPE.&id ({CertTypes}), + * certValue [0] EXPLICIT BAG-TYPE.&Type ({CertTypes}{@certId}) + * } + */ + + if (asn1_get_next(buf, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SEQUENCE (CertBag) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + end = hdr.payload + hdr.length; + + if (asn1_get_oid(pos, end - pos, &oid, &pos)) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Failed to parse OID (certId)"); + return -1; + } + + asn1_oid_to_str(&oid, obuf, sizeof(obuf)); + wpa_printf(MSG_DEBUG, "PKCS #12: certId %s", obuf); + + if (!is_oid_pkcs9_x509_cert(&oid)) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Ignored unsupported certificate type (certId %s)", + obuf); + } + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || + hdr.tag != 0) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected [0] EXPLICIT (certValue) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected OCTET STRING (x509Certificate) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "PKCS #12: x509Certificate", + hdr.payload, hdr.length); + if (cred->cert) { + struct x509_certificate *cert; + + wpa_printf(MSG_DEBUG, "PKCS #12: Ignore extra certificate"); + cert = x509_certificate_parse(hdr.payload, hdr.length); + if (!cert) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Failed to parse x509Certificate"); + return 0; + } + x509_certificate_chain_free(cert); + + return 0; + } + return tlsv1_set_cert(cred, NULL, hdr.payload, hdr.length); +} + + +static int pkcs12_parse_attr_friendly_name(const u8 *pos, const u8 *end) +{ + struct asn1_hdr hdr; + + /* + * RFC 2985, 5.5.1: + * friendlyName ATTRIBUTE ::= { + * WITH SYNTAX BMPString (SIZE(1..pkcs-9-ub-friendlyName)) + * EQUALITY MATCHING RULE caseIgnoreMatch + * SINGLE VALUE TRUE + * ID pkcs-9-at-friendlyName + * } + */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_BMPSTRING) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected BMPSTRING (friendlyName) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return 0; + } + wpa_hexdump_ascii(MSG_DEBUG, "PKCS #12: friendlyName", + hdr.payload, hdr.length); + return 0; +} + + +static int pkcs12_parse_attr_local_key_id(const u8 *pos, const u8 *end) +{ + struct asn1_hdr hdr; + + /* + * RFC 2985, 5.5.2: + * localKeyId ATTRIBUTE ::= { + * WITH SYNTAX OCTET STRING + * EQUALITY MATCHING RULE octetStringMatch + * SINGLE VALUE TRUE + * ID pkcs-9-at-localKeyId + * } + */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected OCTET STRING (localKeyID) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "PKCS #12: localKeyID", + hdr.payload, hdr.length); + return 0; +} + + +static int pkcs12_parse_attr(const u8 *pos, size_t len) +{ + const u8 *end = pos + len; + struct asn1_hdr hdr; + struct asn1_oid a_oid; + char obuf[80]; + + /* + * PKCS12Attribute ::= SEQUENCE { + * attrId ATTRIBUTE.&id ({PKCS12AttrSet}), + * attrValues SET OF ATTRIBUTE.&Type ({PKCS12AttrSet}{@attrId}) + * } + */ + + if (asn1_get_oid(pos, end - pos, &a_oid, &pos)) { + wpa_printf(MSG_DEBUG, "PKCS #12: Failed to parse OID (attrId)"); + return -1; + } + + asn1_oid_to_str(&a_oid, obuf, sizeof(obuf)); + wpa_printf(MSG_DEBUG, "PKCS #12: attrId %s", obuf); + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SET) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SET (attrValues) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + wpa_hexdump_key(MSG_MSGDUMP, "PKCS #12: attrValues", + hdr.payload, hdr.length); + pos = hdr.payload; + end = hdr.payload + hdr.length; + + if (is_oid_pkcs9_friendly_name(&a_oid)) + return pkcs12_parse_attr_friendly_name(pos, end); + if (is_oid_pkcs9_local_key_id(&a_oid)) + return pkcs12_parse_attr_local_key_id(pos, end); + + wpa_printf(MSG_DEBUG, "PKCS #12: Ignore unknown attribute"); + return 0; +} + + +static int pkcs12_safebag(struct tlsv1_credentials *cred, + const u8 *buf, size_t len, const char *passwd) +{ + struct asn1_hdr hdr; + struct asn1_oid oid; + char obuf[80]; + const u8 *pos = buf, *end = buf + len; + const u8 *value; + size_t value_len; + + wpa_hexdump_key(MSG_MSGDUMP, "PKCS #12: SafeBag", buf, len); + + /* BAG-TYPE ::= TYPE-IDENTIFIER */ + if (asn1_get_oid(pos, end - pos, &oid, &pos)) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Failed to parse OID (BAG-TYPE)"); + return -1; + } + + asn1_oid_to_str(&oid, obuf, sizeof(obuf)); + wpa_printf(MSG_DEBUG, "PKCS #12: BAG-TYPE %s", obuf); + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || + hdr.tag != 0) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected [0] EXPLICIT (bagValue) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return 0; + } + value = hdr.payload; + value_len = hdr.length; + wpa_hexdump_key(MSG_MSGDUMP, "PKCS #12: bagValue", value, value_len); + pos = hdr.payload + hdr.length; + + if (pos < end) { + /* bagAttributes SET OF PKCS12Attribute OPTIONAL */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SET) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SET (bagAttributes) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + wpa_hexdump_key(MSG_MSGDUMP, "PKCS #12: bagAttributes", + hdr.payload, hdr.length); + + pos = hdr.payload; + end = hdr.payload + hdr.length; + while (pos < end) { + /* PKCS12Attribute ::= SEQUENCE */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SEQUENCE (PKCS12Attribute) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + if (pkcs12_parse_attr(hdr.payload, hdr.length) < 0) + return -1; + pos = hdr.payload + hdr.length; + } + } + + if (pkcs12_is_bagtype_oid(&oid, 1)) + return pkcs12_keybag(cred, value, value_len); + if (pkcs12_is_bagtype_oid(&oid, 2)) + return pkcs12_pkcs8_keybag(cred, value, value_len, passwd); + if (pkcs12_is_bagtype_oid(&oid, 3)) + return pkcs12_certbag(cred, value, value_len); + + wpa_printf(MSG_DEBUG, "PKCS #12: Ignore unsupported BAG-TYPE"); + return 0; +} + + +static int pkcs12_safecontents(struct tlsv1_credentials *cred, + const u8 *buf, size_t len, + const char *passwd) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + + /* SafeContents ::= SEQUENCE OF SafeBag */ + if (asn1_get_next(buf, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SEQUENCE (SafeContents) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + pos = hdr.payload; + end = hdr.payload + hdr.length; + + /* + * SafeBag ::= SEQUENCE { + * bagId BAG-TYPE.&id ({PKCS12BagSet}) + * bagValue [0] EXPLICIT BAG-TYPE.&Type({PKCS12BagSet}{@bagId}), + * bagAttributes SET OF PKCS12Attribute OPTIONAL + * } + */ + + while (pos < end) { + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SEQUENCE (SafeBag) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + if (pkcs12_safebag(cred, hdr.payload, hdr.length, passwd) < 0) + return -1; + pos = hdr.payload + hdr.length; + } + + return 0; +} + + +static int pkcs12_parse_content_data(struct tlsv1_credentials *cred, + const u8 *pos, const u8 *end, + const char *passwd) +{ + struct asn1_hdr hdr; + + /* Data ::= OCTET STRING */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected OCTET STRING (Data) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + wpa_hexdump(MSG_MSGDUMP, "PKCS #12: Data", hdr.payload, hdr.length); + + return pkcs12_safecontents(cred, hdr.payload, hdr.length, passwd); +} + + +static int pkcs12_parse_content_enc_data(struct tlsv1_credentials *cred, + const u8 *pos, const u8 *end, + const char *passwd) +{ + struct asn1_hdr hdr; + struct asn1_oid oid; + char buf[80]; + const u8 *enc_alg; + u8 *data; + size_t enc_alg_len, data_len; + int res = -1; + + /* + * EncryptedData ::= SEQUENCE { + * version Version, + * encryptedContentInfo EncryptedContentInfo } + */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SEQUENCE (EncryptedData) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return 0; + } + pos = hdr.payload; + + /* Version ::= INTEGER */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) { + wpa_printf(MSG_DEBUG, + "PKCS #12: No INTEGER tag found for version; class=%d tag=0x%x", + hdr.class, hdr.tag); + return -1; + } + if (hdr.length != 1 || hdr.payload[0] != 0) { + wpa_printf(MSG_DEBUG, "PKCS #12: Unrecognized PKCS #7 version"); + return -1; + } + pos = hdr.payload + hdr.length; + + wpa_hexdump(MSG_MSGDUMP, "PKCS #12: EncryptedContentInfo", + pos, end - pos); + + /* + * EncryptedContentInfo ::= SEQUENCE { + * contentType ContentType, + * contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier, + * encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL } + */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SEQUENCE (EncryptedContentInfo) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + end = pos + hdr.length; + + /* ContentType ::= OBJECT IDENTIFIER */ + if (asn1_get_oid(pos, end - pos, &oid, &pos)) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Could not find OBJECT IDENTIFIER (contentType)"); + return -1; + } + asn1_oid_to_str(&oid, buf, sizeof(buf)); + wpa_printf(MSG_DEBUG, "PKCS #12: EncryptedContentInfo::contentType %s", + buf); + + if (!is_oid_pkcs7_data(&oid)) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Unsupported EncryptedContentInfo::contentType %s", + buf); + return 0; + } + + /* ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "PKCS #12: Expected SEQUENCE (ContentEncryptionAlgorithmIdentifier) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + enc_alg = hdr.payload; + enc_alg_len = hdr.length; + pos = hdr.payload + hdr.length; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || + hdr.tag != 0) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected [0] IMPLICIT (encryptedContent) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + /* EncryptedContent ::= OCTET STRING */ + data = pkcs5_decrypt(enc_alg, enc_alg_len, hdr.payload, hdr.length, + passwd, &data_len); + if (data) { + wpa_hexdump_key(MSG_MSGDUMP, + "PKCS #12: Decrypted encryptedContent", + data, data_len); + res = pkcs12_safecontents(cred, data, data_len, passwd); + os_free(data); + } + + return res; +} + + +static int pkcs12_parse_content(struct tlsv1_credentials *cred, + const u8 *buf, size_t len, + const char *passwd) +{ + const u8 *pos = buf; + const u8 *end = buf + len; + struct asn1_oid oid; + char txt[80]; + struct asn1_hdr hdr; + + wpa_hexdump(MSG_MSGDUMP, "PKCS #12: ContentInfo", buf, len); + + if (asn1_get_oid(pos, end - pos, &oid, &pos)) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Could not find OBJECT IDENTIFIER (contentType)"); + return 0; + } + + asn1_oid_to_str(&oid, txt, sizeof(txt)); + wpa_printf(MSG_DEBUG, "PKCS #12: contentType %s", txt); + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || + hdr.tag != 0) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected [0] EXPLICIT (content) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return 0; + } + pos = hdr.payload; + + if (is_oid_pkcs7_data(&oid)) + return pkcs12_parse_content_data(cred, pos, end, passwd); + if (is_oid_pkcs7_enc_data(&oid)) + return pkcs12_parse_content_enc_data(cred, pos, end, passwd); + + wpa_printf(MSG_DEBUG, "PKCS #12: Ignored unsupported contentType %s", + txt); + + return 0; +} + + +static int pkcs12_parse(struct tlsv1_credentials *cred, + const u8 *key, size_t len, const char *passwd) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + struct asn1_oid oid; + char buf[80]; + + /* + * PFX ::= SEQUENCE { + * version INTEGER {v3(3)}(v3,...), + * authSafe ContentInfo, + * macData MacData OPTIONAL + * } + */ + + if (asn1_get_next(key, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SEQUENCE (PFX) - found class %d tag 0x%x; assume PKCS #12 not used", + hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + end = pos + hdr.length; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) { + wpa_printf(MSG_DEBUG, + "PKCS #12: No INTEGER tag found for version; class=%d tag=0x%x", + hdr.class, hdr.tag); + return -1; + } + if (hdr.length != 1 || hdr.payload[0] != 3) { + wpa_printf(MSG_DEBUG, "PKCS #12: Unrecognized version"); + return -1; + } + pos = hdr.payload + hdr.length; + + /* + * ContentInfo ::= SEQUENCE { + * contentType ContentType, + * content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL } + */ + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SEQUENCE (authSafe) - found class %d tag 0x%x; assume PKCS #12 not used", + hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + end = pos + hdr.length; + + /* ContentType ::= OBJECT IDENTIFIER */ + if (asn1_get_oid(pos, end - pos, &oid, &pos)) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Could not find OBJECT IDENTIFIER (contentType); assume PKCS #12 not used"); + return -1; + } + asn1_oid_to_str(&oid, buf, sizeof(buf)); + wpa_printf(MSG_DEBUG, "PKCS #12: contentType %s", buf); + if (!is_oid_pkcs7_data(&oid)) { + wpa_printf(MSG_DEBUG, "PKCS #12: Unsupported contentType %s", + buf); + return -1; + } + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || + hdr.tag != 0) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected [0] EXPLICIT (content) - found class %d tag 0x%x; assume PKCS #12 not used", + hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + + /* Data ::= OCTET STRING */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected OCTET STRING (Data) - found class %d tag 0x%x; assume PKCS #12 not used", + hdr.class, hdr.tag); + return -1; + } + + /* + * AuthenticatedSafe ::= SEQUENCE OF ContentInfo + * -- Data if unencrypted + * -- EncryptedData if password-encrypted + * -- EnvelopedData if public key-encrypted + */ + wpa_hexdump(MSG_MSGDUMP, "PKCS #12: Data content", + hdr.payload, hdr.length); + + if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SEQUENCE within Data content - found class %d tag 0x%x; assume PKCS #12 not used", + hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + end = pos + hdr.length; + + while (end > pos) { + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SEQUENCE (ContentInfo) - found class %d tag 0x%x; assume PKCS #12 not used", + hdr.class, hdr.tag); + return -1; + } + if (pkcs12_parse_content(cred, hdr.payload, hdr.length, + passwd) < 0) + return -1; + + pos = hdr.payload + hdr.length; + } + + return 0; +} + +#endif /* PKCS12_FUNCS */ + + static int tlsv1_set_key(struct tlsv1_credentials *cred, const u8 *key, size_t len, const char *passwd) { @@ -296,6 +1067,10 @@ static int tlsv1_set_key(struct tlsv1_credentials *cred, cred->key = tlsv1_set_key_pem(key, len); if (cred->key == NULL) cred->key = tlsv1_set_key_enc_pem(key, len, passwd); +#ifdef PKCS12_FUNCS + if (!cred->key) + pkcs12_parse(cred, key, len, passwd); +#endif /* PKCS12_FUNCS */ if (cred->key == NULL) { wpa_printf(MSG_INFO, "TLSv1: Failed to parse private key"); return -1; diff --git a/contrib/wpa/src/tls/tlsv1_cred.h b/contrib/wpa/src/tls/tlsv1_cred.h index 68fbdc923008..716e93c39006 100644 --- a/contrib/wpa/src/tls/tlsv1_cred.h +++ b/contrib/wpa/src/tls/tlsv1_cred.h @@ -14,11 +14,19 @@ struct tlsv1_credentials { struct x509_certificate *cert; struct crypto_private_key *key; + unsigned int cert_probe:1; + unsigned int ca_cert_verify:1; + unsigned int server_cert_only:1; + u8 srv_cert_hash[32]; + /* Diffie-Hellman parameters */ u8 *dh_p; /* prime */ size_t dh_p_len; u8 *dh_g; /* generator */ size_t dh_g_len; + + char *ocsp_stapling_response; + char *ocsp_stapling_response_multi; }; diff --git a/contrib/wpa/src/tls/tlsv1_server_i.h b/contrib/wpa/src/tls/tlsv1_server_i.h index 96d79b3a8ba2..29c667877215 100644 --- a/contrib/wpa/src/tls/tlsv1_server_i.h +++ b/contrib/wpa/src/tls/tlsv1_server_i.h @@ -55,6 +55,9 @@ struct tlsv1_server { void *log_cb_ctx; int use_session_ticket; + unsigned int status_request:1; + unsigned int status_request_v2:1; + unsigned int status_request_multi:1; u8 *dh_secret; size_t dh_secret_len; diff --git a/contrib/wpa/src/tls/tlsv1_server_read.c b/contrib/wpa/src/tls/tlsv1_server_read.c index 0f237baff9db..4aa8a019f3e6 100644 --- a/contrib/wpa/src/tls/tlsv1_server_read.c +++ b/contrib/wpa/src/tls/tlsv1_server_read.c @@ -46,6 +46,78 @@ static int testing_cipher_suite_filter(struct tlsv1_server *conn, u16 suite) } +static void tls_process_status_request_item(struct tlsv1_server *conn, + const u8 *req, size_t req_len) +{ + const u8 *pos, *end; + u8 status_type; + + pos = req; + end = req + req_len; + + /* + * RFC 6961, 2.2: + * struct { + * CertificateStatusType status_type; + * uint16 request_length; + * select (status_type) { + * case ocsp: OCSPStatusRequest; + * case ocsp_multi: OCSPStatusRequest; + * } request; + * } CertificateStatusRequestItemV2; + * + * enum { ocsp(1), ocsp_multi(2), (255) } CertificateStatusType; + */ + + if (end - pos < 1) + return; /* Truncated data */ + + status_type = *pos++; + wpa_printf(MSG_DEBUG, "TLSv1: CertificateStatusType %u", status_type); + if (status_type != 1 && status_type != 2) + return; /* Unsupported status type */ + /* + * For now, only OCSP stapling is supported, so ignore the specific + * request, if any. + */ + wpa_hexdump(MSG_DEBUG, "TLSv1: OCSPStatusRequest", pos, end - pos); + + if (status_type == 2) + conn->status_request_multi = 1; +} + + +static void tls_process_status_request_v2(struct tlsv1_server *conn, + const u8 *ext, size_t ext_len) +{ + const u8 *pos, *end; + + conn->status_request_v2 = 1; + + pos = ext; + end = ext + ext_len; + + /* + * RFC 6961, 2.2: + * struct { + * CertificateStatusRequestItemV2 + * certificate_status_req_list<1..2^16-1>; + * } CertificateStatusRequestListV2; + */ + + while (end - pos >= 2) { + u16 len; + + len = WPA_GET_BE16(pos); + pos += 2; + if (len > end - pos) + break; /* Truncated data */ + tls_process_status_request_item(conn, pos, len); + pos += len; + } +} + + static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, const u8 *in_data, size_t *in_len) { @@ -267,6 +339,11 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, ext_len); conn->session_ticket_len = ext_len; } + } else if (ext_type == TLS_EXT_STATUS_REQUEST) { + conn->status_request = 1; + } else if (ext_type == TLS_EXT_STATUS_REQUEST_V2) { + tls_process_status_request_v2(conn, pos, + ext_len); } pos += ext_len; @@ -471,6 +548,15 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, return -1; } + if (chain && (chain->extensions_present & X509_EXT_EXT_KEY_USAGE) && + !(chain->ext_key_usage & + (X509_EXT_KEY_USAGE_ANY | X509_EXT_KEY_USAGE_CLIENT_AUTH))) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_BAD_CERTIFICATE); + x509_certificate_chain_free(chain); + return -1; + } + x509_certificate_chain_free(chain); *in_len = end - in_data; diff --git a/contrib/wpa/src/tls/tlsv1_server_write.c b/contrib/wpa/src/tls/tlsv1_server_write.c index 15e6692178ff..bdc6c1199238 100644 --- a/contrib/wpa/src/tls/tlsv1_server_write.c +++ b/contrib/wpa/src/tls/tlsv1_server_write.c @@ -42,7 +42,7 @@ static size_t tls_server_cert_chain_der_len(struct tlsv1_server *conn) static int tls_write_server_hello(struct tlsv1_server *conn, u8 **msgpos, u8 *end) { - u8 *pos, *rhdr, *hs_start, *hs_length; + u8 *pos, *rhdr, *hs_start, *hs_length, *ext_start; struct os_time now; size_t rlen; @@ -97,6 +97,32 @@ static int tls_write_server_hello(struct tlsv1_server *conn, /* CompressionMethod compression_method */ *pos++ = TLS_COMPRESSION_NULL; + /* Extension */ + ext_start = pos; + pos += 2; + + if (conn->status_request) { + /* Add a status_request extension with empty extension_data */ + /* ExtensionsType extension_type = status_request(5) */ + WPA_PUT_BE16(pos, TLS_EXT_STATUS_REQUEST); + pos += 2; + /* opaque extension_data<0..2^16-1> length */ + WPA_PUT_BE16(pos, 0); + pos += 2; + } + + if (conn->status_request_v2) { + /* + Add a status_request_v2 extension with empty extension_data + */ + /* ExtensionsType extension_type = status_request_v2(17) */ + WPA_PUT_BE16(pos, TLS_EXT_STATUS_REQUEST_V2); + pos += 2; + /* opaque extension_data<0..2^16-1> length */ + WPA_PUT_BE16(pos, 0); + pos += 2; + } + if (conn->session_ticket && conn->session_ticket_cb) { int res = conn->session_ticket_cb( conn->session_ticket_cb_ctx, @@ -133,6 +159,11 @@ static int tls_write_server_hello(struct tlsv1_server *conn, */ } + if (pos == ext_start + 2) + pos -= 2; /* no extensions */ + else + WPA_PUT_BE16(ext_start, pos - ext_start - 2); + WPA_PUT_BE24(hs_length, pos - hs_length - 3); tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); @@ -168,6 +199,11 @@ static int tls_write_server_certificate(struct tlsv1_server *conn, } pos = *msgpos; + if (TLS_RECORD_HEADER_LEN + 1 + 3 + 3 > end - pos) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } tlsv1_server_log(conn, "Send Certificate"); rhdr = pos; @@ -188,7 +224,7 @@ static int tls_write_server_certificate(struct tlsv1_server *conn, pos += 3; cert = conn->cred->cert; while (cert) { - if (pos + 3 + cert->cert_len > end) { + if (3 + cert->cert_len > (size_t) (end - pos)) { wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space " "for Certificate (cert_len=%lu left=%lu)", (unsigned long) cert->cert_len, @@ -239,6 +275,93 @@ static int tls_write_server_certificate(struct tlsv1_server *conn, } +static int tls_write_server_certificate_status(struct tlsv1_server *conn, + u8 **msgpos, u8 *end, + int ocsp_multi, + char *ocsp_resp, + size_t ocsp_resp_len) +{ + u8 *pos, *rhdr, *hs_start, *hs_length; + size_t rlen; + + if (!ocsp_resp) { + /* + * Client did not request certificate status or there is no + * matching response cached. + */ + return 0; + } + + pos = *msgpos; + if (TLS_RECORD_HEADER_LEN + 1 + 3 + 1 + 3 + ocsp_resp_len > + (unsigned int) (end - pos)) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + tlsv1_server_log(conn, "Send CertificateStatus (multi=%d)", ocsp_multi); + rhdr = pos; + pos += TLS_RECORD_HEADER_LEN; + + /* opaque fragment[TLSPlaintext.length] */ + + /* Handshake */ + hs_start = pos; + /* HandshakeType msg_type */ + *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE_STATUS; + /* uint24 length (to be filled) */ + hs_length = pos; + pos += 3; + + /* body - CertificateStatus + * + * struct { + * CertificateStatusType status_type; + * select (status_type) { + * case ocsp: OCSPResponse; + * case ocsp_multi: OCSPResponseList; + * } response; + * } CertificateStatus; + * + * opaque OCSPResponse<1..2^24-1>; + * + * struct { + * OCSPResponse ocsp_response_list<1..2^24-1>; + * } OCSPResponseList; + */ + + /* CertificateStatusType status_type */ + if (ocsp_multi) + *pos++ = 2; /* ocsp_multi(2) */ + else + *pos++ = 1; /* ocsp(1) */ + /* uint24 length of OCSPResponse */ + WPA_PUT_BE24(pos, ocsp_resp_len); + pos += 3; + os_memcpy(pos, ocsp_resp, ocsp_resp_len); + pos += ocsp_resp_len; + + WPA_PUT_BE24(hs_length, pos - hs_length - 3); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, + rhdr, end - rhdr, hs_start, pos - hs_start, + &rlen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + pos = rhdr + rlen; + + tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); + + *msgpos = pos; + + return 0; +} + + static int tls_write_server_key_exchange(struct tlsv1_server *conn, u8 **msgpos, u8 *end) { @@ -371,7 +494,7 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, /* body - ServerDHParams */ server_params = pos; /* dh_p */ - if (pos + 2 + dh_p_len > end) { + if (2 + dh_p_len > (size_t) (end - pos)) { wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for " "dh_p"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, @@ -385,7 +508,7 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, pos += dh_p_len; /* dh_g */ - if (pos + 2 + conn->cred->dh_g_len > end) { + if (2 + conn->cred->dh_g_len > (size_t) (end - pos)) { wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for " "dh_g"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, @@ -399,7 +522,7 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, pos += conn->cred->dh_g_len; /* dh_Ys */ - if (pos + 2 + dh_ys_len > end) { + if (2 + dh_ys_len > (size_t) (end - pos)) { wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for " "dh_Ys"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, @@ -443,7 +566,8 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, if (conn->rl.tls_version >= TLS_VERSION_1_2) { #ifdef CONFIG_TLSV12 hlen = tlsv12_key_x_server_params_hash( - conn->rl.tls_version, conn->client_random, + conn->rl.tls_version, TLS_HASH_ALG_SHA256, + conn->client_random, conn->server_random, server_params, pos - server_params, hash + 19); @@ -457,7 +581,7 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, * SignatureAlgorithm signature; * } SignatureAndHashAlgorithm; */ - if (hlen < 0 || pos + 2 > end) { + if (hlen < 0 || end - pos < 2) { tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; @@ -804,24 +928,46 @@ static u8 * tls_send_server_hello(struct tlsv1_server *conn, size_t *out_len) { u8 *msg, *end, *pos; size_t msglen; + int ocsp_multi = 0; + char *ocsp_resp = NULL; + size_t ocsp_resp_len = 0; *out_len = 0; - msglen = 1000 + tls_server_cert_chain_der_len(conn); + if (conn->status_request_multi && + conn->cred->ocsp_stapling_response_multi) { + ocsp_resp = os_readfile( + conn->cred->ocsp_stapling_response_multi, + &ocsp_resp_len); + ocsp_multi = 1; + } else if ((conn->status_request || conn->status_request_v2) && + conn->cred->ocsp_stapling_response) { + ocsp_resp = os_readfile(conn->cred->ocsp_stapling_response, + &ocsp_resp_len); + } + if (!ocsp_resp) + ocsp_resp_len = 0; + + msglen = 1000 + tls_server_cert_chain_der_len(conn) + ocsp_resp_len; msg = os_malloc(msglen); - if (msg == NULL) + if (msg == NULL) { + os_free(ocsp_resp); return NULL; + } pos = msg; end = msg + msglen; if (tls_write_server_hello(conn, &pos, end) < 0) { os_free(msg); + os_free(ocsp_resp); return NULL; } if (conn->use_session_ticket) { + os_free(ocsp_resp); + /* Abbreviated handshake using session ticket; RFC 4507 */ if (tls_write_server_change_cipher_spec(conn, &pos, end) < 0 || tls_write_server_finished(conn, &pos, end) < 0) { @@ -838,12 +984,16 @@ static u8 * tls_send_server_hello(struct tlsv1_server *conn, size_t *out_len) /* Full handshake */ if (tls_write_server_certificate(conn, &pos, end) < 0 || + tls_write_server_certificate_status(conn, &pos, end, ocsp_multi, + ocsp_resp, ocsp_resp_len) < 0 || tls_write_server_key_exchange(conn, &pos, end) < 0 || tls_write_server_certificate_request(conn, &pos, end) < 0 || tls_write_server_hello_done(conn, &pos, end) < 0) { os_free(msg); + os_free(ocsp_resp); return NULL; } + os_free(ocsp_resp); *out_len = pos - msg; diff --git a/contrib/wpa/src/tls/x509v3.c b/contrib/wpa/src/tls/x509v3.c index b51dfcd44732..75f222c4f249 100644 --- a/contrib/wpa/src/tls/x509v3.c +++ b/contrib/wpa/src/tls/x509v3.c @@ -1,6 +1,6 @@ /* * X.509v3 certificate parsing and processing (RFC 3280 profile) - * Copyright (c) 2006-2011, Jouni Malinen + * Copyright (c) 2006-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -14,7 +14,7 @@ #include "x509v3.h" -static void x509_free_name(struct x509_name *name) +void x509_free_name(struct x509_name *name) { size_t i; @@ -55,6 +55,7 @@ void x509_certificate_free(struct x509_certificate *cert) x509_free_name(&cert->subject); os_free(cert->public_key); os_free(cert->sign_value); + os_free(cert->subject_dn); os_free(cert); } @@ -177,9 +178,9 @@ int x509_name_compare(struct x509_name *a, struct x509_name *b) } -static int x509_parse_algorithm_identifier( - const u8 *buf, size_t len, - struct x509_algorithm_identifier *id, const u8 **next) +int x509_parse_algorithm_identifier(const u8 *buf, size_t len, + struct x509_algorithm_identifier *id, + const u8 **next) { struct asn1_hdr hdr; const u8 *pos, *end; @@ -199,12 +200,11 @@ static int x509_parse_algorithm_identifier( hdr.class, hdr.tag); return -1; } + if (hdr.length > buf + len - hdr.payload) + return -1; pos = hdr.payload; end = pos + hdr.length; - if (end > buf + len) - return -1; - *next = end; if (asn1_get_oid(pos, end - pos, &id->oid, &pos)) @@ -243,7 +243,7 @@ static int x509_parse_public_key(const u8 *buf, size_t len, } pos = hdr.payload; - if (pos + hdr.length > end) + if (hdr.length > end - pos) return -1; end = pos + hdr.length; *next = end; @@ -289,8 +289,8 @@ static int x509_parse_public_key(const u8 *buf, size_t len, } -static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, - const u8 **next) +int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, + const u8 **next) { struct asn1_hdr hdr; const u8 *pos, *end, *set_pos, *set_end, *seq_pos, *seq_end; @@ -319,7 +319,7 @@ static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, } pos = hdr.payload; - if (pos + hdr.length > buf + len) + if (hdr.length > buf + len - pos) return -1; end = *next = pos + hdr.length; @@ -537,8 +537,7 @@ done: } -static int x509_parse_time(const u8 *buf, size_t len, u8 asn1_tag, - os_time_t *val) +int x509_parse_time(const u8 *buf, size_t len, u8 asn1_tag, os_time_t *val) { const char *pos; int year, month, day, hour, min, sec; @@ -677,7 +676,7 @@ static int x509_parse_validity(const u8 *buf, size_t len, pos = hdr.payload; plen = hdr.length; - if (pos + plen > buf + len) + if (plen > (size_t) (buf + len - pos)) return -1; *next = pos + plen; @@ -721,6 +720,15 @@ static int x509_id_ce_oid(struct asn1_oid *oid) } +static int x509_any_ext_key_usage_oid(struct asn1_oid *oid) +{ + return oid->len == 6 && + x509_id_ce_oid(oid) && + oid->oid[3] == 37 /* extKeyUsage */ && + oid->oid[4] == 0 /* anyExtendedKeyUsage */; +} + + static int x509_parse_ext_key_usage(struct x509_certificate *cert, const u8 *pos, size_t len) { @@ -801,7 +809,7 @@ static int x509_parse_ext_basic_constraints(struct x509_certificate *cert, } cert->ca = hdr.payload[0]; - if (hdr.payload + hdr.length == pos + len) { + if (hdr.length == pos + len - hdr.payload) { wpa_printf(MSG_DEBUG, "X509: BasicConstraints - cA=%d", cert->ca); return 0; @@ -1074,6 +1082,112 @@ static int x509_parse_ext_issuer_alt_name(struct x509_certificate *cert, } +static int x509_id_pkix_oid(struct asn1_oid *oid) +{ + return oid->len >= 7 && + oid->oid[0] == 1 /* iso */ && + oid->oid[1] == 3 /* identified-organization */ && + oid->oid[2] == 6 /* dod */ && + oid->oid[3] == 1 /* internet */ && + oid->oid[4] == 5 /* security */ && + oid->oid[5] == 5 /* mechanisms */ && + oid->oid[6] == 7 /* id-pkix */; +} + + +static int x509_id_kp_oid(struct asn1_oid *oid) +{ + /* id-kp */ + return oid->len >= 8 && + x509_id_pkix_oid(oid) && + oid->oid[7] == 3 /* id-kp */; +} + + +static int x509_id_kp_server_auth_oid(struct asn1_oid *oid) +{ + /* id-kp */ + return oid->len == 9 && + x509_id_kp_oid(oid) && + oid->oid[8] == 1 /* id-kp-serverAuth */; +} + + +static int x509_id_kp_client_auth_oid(struct asn1_oid *oid) +{ + /* id-kp */ + return oid->len == 9 && + x509_id_kp_oid(oid) && + oid->oid[8] == 2 /* id-kp-clientAuth */; +} + + +static int x509_id_kp_ocsp_oid(struct asn1_oid *oid) +{ + /* id-kp */ + return oid->len == 9 && + x509_id_kp_oid(oid) && + oid->oid[8] == 9 /* id-kp-OCSPSigning */; +} + + +static int x509_parse_ext_ext_key_usage(struct x509_certificate *cert, + const u8 *pos, size_t len) +{ + struct asn1_hdr hdr; + const u8 *end; + struct asn1_oid oid; + + /* + * ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId + * + * KeyPurposeId ::= OBJECT IDENTIFIER + */ + + if (asn1_get_next(pos, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " + "(ExtKeyUsageSyntax) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + if (hdr.length > pos + len - hdr.payload) + return -1; + pos = hdr.payload; + end = pos + hdr.length; + + wpa_hexdump(MSG_MSGDUMP, "X509: ExtKeyUsageSyntax", pos, end - pos); + + while (pos < end) { + char buf[80]; + + if (asn1_get_oid(pos, end - pos, &oid, &pos)) + return -1; + if (x509_any_ext_key_usage_oid(&oid)) { + os_strlcpy(buf, "anyExtendedKeyUsage", sizeof(buf)); + cert->ext_key_usage |= X509_EXT_KEY_USAGE_ANY; + } else if (x509_id_kp_server_auth_oid(&oid)) { + os_strlcpy(buf, "id-kp-serverAuth", sizeof(buf)); + cert->ext_key_usage |= X509_EXT_KEY_USAGE_SERVER_AUTH; + } else if (x509_id_kp_client_auth_oid(&oid)) { + os_strlcpy(buf, "id-kp-clientAuth", sizeof(buf)); + cert->ext_key_usage |= X509_EXT_KEY_USAGE_CLIENT_AUTH; + } else if (x509_id_kp_ocsp_oid(&oid)) { + os_strlcpy(buf, "id-kp-OCSPSigning", sizeof(buf)); + cert->ext_key_usage |= X509_EXT_KEY_USAGE_OCSP; + } else { + asn1_oid_to_str(&oid, buf, sizeof(buf)); + } + wpa_printf(MSG_DEBUG, "ExtKeyUsage KeyPurposeId: %s", buf); + } + + cert->extensions_present |= X509_EXT_EXT_KEY_USAGE; + + return 0; +} + + static int x509_parse_extension_data(struct x509_certificate *cert, struct asn1_oid *oid, const u8 *pos, size_t len) @@ -1085,7 +1199,6 @@ static int x509_parse_extension_data(struct x509_certificate *cert, * certificate policies (section 4.2.1.5) * name constraints (section 4.2.1.11) * policy constraints (section 4.2.1.12) - * extended key usage (section 4.2.1.13) * inhibit any-policy (section 4.2.1.15) */ switch (oid->oid[3]) { @@ -1097,6 +1210,8 @@ static int x509_parse_extension_data(struct x509_certificate *cert, return x509_parse_ext_issuer_alt_name(cert, pos, len); case 19: /* id-ce-basicConstraints */ return x509_parse_ext_basic_constraints(cert, pos, len); + case 37: /* id-ce-extKeyUsage */ + return x509_parse_ext_ext_key_usage(cert, pos, len); default: return 1; } @@ -1224,6 +1339,7 @@ static int x509_parse_tbs_certificate(const u8 *buf, size_t len, size_t left; char sbuf[128]; unsigned long value; + const u8 *subject_dn; /* tbsCertificate TBSCertificate ::= SEQUENCE */ if (asn1_get_next(buf, len, &hdr) < 0 || @@ -1287,21 +1403,23 @@ static int x509_parse_tbs_certificate(const u8 *buf, size_t len, /* serialNumber CertificateSerialNumber ::= INTEGER */ if (hdr.class != ASN1_CLASS_UNIVERSAL || - hdr.tag != ASN1_TAG_INTEGER) { + hdr.tag != ASN1_TAG_INTEGER || + hdr.length < 1 || hdr.length > X509_MAX_SERIAL_NUM_LEN) { wpa_printf(MSG_DEBUG, "X509: No INTEGER tag found for " - "serialNumber; class=%d tag=0x%x", - hdr.class, hdr.tag); + "serialNumber; class=%d tag=0x%x length=%u", + hdr.class, hdr.tag, hdr.length); return -1; } - pos = hdr.payload; - left = hdr.length; - while (left) { - cert->serial_number <<= 8; - cert->serial_number |= *pos++; - left--; + pos = hdr.payload + hdr.length; + while (hdr.length > 0 && hdr.payload[0] == 0) { + hdr.payload++; + hdr.length--; } - wpa_printf(MSG_MSGDUMP, "X509: serialNumber %lu", cert->serial_number); + os_memcpy(cert->serial_number, hdr.payload, hdr.length); + cert->serial_number_len = hdr.length; + wpa_hexdump(MSG_MSGDUMP, "X509: serialNumber", cert->serial_number, + cert->serial_number_len); /* signature AlgorithmIdentifier */ if (x509_parse_algorithm_identifier(pos, end - pos, &cert->signature, @@ -1319,8 +1437,14 @@ static int x509_parse_tbs_certificate(const u8 *buf, size_t len, return -1; /* subject Name */ + subject_dn = pos; if (x509_parse_name(pos, end - pos, &cert->subject, &pos)) return -1; + cert->subject_dn = os_malloc(pos - subject_dn); + if (!cert->subject_dn) + return -1; + cert->subject_dn_len = pos - subject_dn; + os_memcpy(cert->subject_dn, subject_dn, cert->subject_dn_len); x509_name_string(&cert->subject, sbuf, sizeof(sbuf)); wpa_printf(MSG_MSGDUMP, "X509: subject %s", sbuf); @@ -1437,7 +1561,7 @@ static int x509_digest_oid(struct asn1_oid *oid) } -static int x509_sha1_oid(struct asn1_oid *oid) +int x509_sha1_oid(struct asn1_oid *oid) { return oid->len == 6 && oid->oid[0] == 1 /* iso */ && @@ -1449,7 +1573,7 @@ static int x509_sha1_oid(struct asn1_oid *oid) } -static int x509_sha256_oid(struct asn1_oid *oid) +static int x509_sha2_oid(struct asn1_oid *oid) { return oid->len == 9 && oid->oid[0] == 2 /* joint-iso-itu-t */ && @@ -1459,11 +1583,31 @@ static int x509_sha256_oid(struct asn1_oid *oid) oid->oid[4] == 101 /* gov */ && oid->oid[5] == 3 /* csor */ && oid->oid[6] == 4 /* nistAlgorithm */ && - oid->oid[7] == 2 /* hashAlgs */ && + oid->oid[7] == 2 /* hashAlgs */; +} + + +int x509_sha256_oid(struct asn1_oid *oid) +{ + return x509_sha2_oid(oid) && oid->oid[8] == 1 /* sha256 */; } +int x509_sha384_oid(struct asn1_oid *oid) +{ + return x509_sha2_oid(oid) && + oid->oid[8] == 2 /* sha384 */; +} + + +int x509_sha512_oid(struct asn1_oid *oid) +{ + return x509_sha2_oid(oid) && + oid->oid[8] == 3 /* sha512 */; +} + + /** * x509_certificate_parse - Parse a X.509 certificate in DER format * @buf: Pointer to the X.509 certificate in DER format @@ -1503,12 +1647,12 @@ struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len) } pos = hdr.payload; - if (pos + hdr.length > end) { + if (hdr.length > end - pos) { x509_certificate_free(cert); return NULL; } - if (pos + hdr.length < end) { + if (hdr.length < end - pos) { wpa_hexdump(MSG_MSGDUMP, "X509: Ignoring extra data after DER " "encoded certificate", pos + hdr.length, end - (pos + hdr.length)); @@ -1581,6 +1725,17 @@ struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len) */ int x509_certificate_check_signature(struct x509_certificate *issuer, struct x509_certificate *cert) +{ + return x509_check_signature(issuer, &cert->signature, + cert->sign_value, cert->sign_value_len, + cert->tbs_cert_start, cert->tbs_cert_len); +} + + +int x509_check_signature(struct x509_certificate *issuer, + struct x509_algorithm_identifier *signature, + const u8 *sign_value, size_t sign_value_len, + const u8 *signed_data, size_t signed_data_len) { struct crypto_public_key *pk; u8 *data; @@ -1588,12 +1743,14 @@ int x509_certificate_check_signature(struct x509_certificate *issuer, size_t data_len; struct asn1_hdr hdr; struct asn1_oid oid; - u8 hash[32]; + u8 hash[64]; size_t hash_len; + const u8 *addr[1] = { signed_data }; + size_t len[1] = { signed_data_len }; - if (!x509_pkcs_oid(&cert->signature.oid) || - cert->signature.oid.len != 7 || - cert->signature.oid.oid[5] != 1 /* pkcs-1 */) { + if (!x509_pkcs_oid(&signature->oid) || + signature->oid.len != 7 || + signature->oid.oid[5] != 1 /* pkcs-1 */) { wpa_printf(MSG_DEBUG, "X509: Unrecognized signature " "algorithm"); return -1; @@ -1604,15 +1761,15 @@ int x509_certificate_check_signature(struct x509_certificate *issuer, if (pk == NULL) return -1; - data_len = cert->sign_value_len; + data_len = sign_value_len; data = os_malloc(data_len); if (data == NULL) { crypto_public_key_free(pk); return -1; } - if (crypto_public_key_decrypt_pkcs1(pk, cert->sign_value, - cert->sign_value_len, data, + if (crypto_public_key_decrypt_pkcs1(pk, sign_value, + sign_value_len, data, &data_len) < 0) { wpa_printf(MSG_DEBUG, "X509: Failed to decrypt signature"); crypto_public_key_free(pk); @@ -1675,12 +1832,11 @@ int x509_certificate_check_signature(struct x509_certificate *issuer, } if (x509_sha1_oid(&oid)) { - if (cert->signature.oid.oid[6] != - 5 /* sha-1WithRSAEncryption */) { + if (signature->oid.oid[6] != 5 /* sha-1WithRSAEncryption */) { wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA1 " "does not match with certificate " "signatureAlgorithm (%lu)", - cert->signature.oid.oid[6]); + signature->oid.oid[6]); os_free(data); return -1; } @@ -1688,12 +1844,36 @@ int x509_certificate_check_signature(struct x509_certificate *issuer, } if (x509_sha256_oid(&oid)) { - if (cert->signature.oid.oid[6] != + if (signature->oid.oid[6] != 11 /* sha2561WithRSAEncryption */) { wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA256 " "does not match with certificate " "signatureAlgorithm (%lu)", - cert->signature.oid.oid[6]); + signature->oid.oid[6]); + os_free(data); + return -1; + } + goto skip_digest_oid; + } + + if (x509_sha384_oid(&oid)) { + if (signature->oid.oid[6] != 12 /* sha384WithRSAEncryption */) { + wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA384 " + "does not match with certificate " + "signatureAlgorithm (%lu)", + signature->oid.oid[6]); + os_free(data); + return -1; + } + goto skip_digest_oid; + } + + if (x509_sha512_oid(&oid)) { + if (signature->oid.oid[6] != 13 /* sha512WithRSAEncryption */) { + wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA512 " + "does not match with certificate " + "signatureAlgorithm (%lu)", + signature->oid.oid[6]); os_free(data); return -1; } @@ -1707,12 +1887,11 @@ int x509_certificate_check_signature(struct x509_certificate *issuer, } switch (oid.oid[5]) { case 5: /* md5 */ - if (cert->signature.oid.oid[6] != 4 /* md5WithRSAEncryption */) - { + if (signature->oid.oid[6] != 4 /* md5WithRSAEncryption */) { wpa_printf(MSG_DEBUG, "X509: digestAlgorithm MD5 does " "not match with certificate " "signatureAlgorithm (%lu)", - cert->signature.oid.oid[6]); + signature->oid.oid[6]); os_free(data); return -1; } @@ -1743,34 +1922,41 @@ skip_digest_oid: wpa_hexdump(MSG_MSGDUMP, "X509: Decrypted Digest", hdr.payload, hdr.length); - switch (cert->signature.oid.oid[6]) { + switch (signature->oid.oid[6]) { case 4: /* md5WithRSAEncryption */ - md5_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len, - hash); + md5_vector(1, addr, len, hash); hash_len = 16; wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (MD5)", hash, hash_len); break; case 5: /* sha-1WithRSAEncryption */ - sha1_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len, - hash); + sha1_vector(1, addr, len, hash); hash_len = 20; wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA1)", hash, hash_len); break; case 11: /* sha256WithRSAEncryption */ - sha256_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len, - hash); + sha256_vector(1, addr, len, hash); hash_len = 32; wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA256)", hash, hash_len); break; - case 2: /* md2WithRSAEncryption */ case 12: /* sha384WithRSAEncryption */ + sha384_vector(1, addr, len, hash); + hash_len = 48; + wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA384)", + hash, hash_len); + break; case 13: /* sha512WithRSAEncryption */ + sha512_vector(1, addr, len, hash); + hash_len = 64; + wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA512)", + hash, hash_len); + break; + case 2: /* md2WithRSAEncryption */ default: wpa_printf(MSG_INFO, "X509: Unsupported certificate signature " - "algorithm (%lu)", cert->signature.oid.oid[6]); + "algorithm (%lu)", signature->oid.oid[6]); os_free(data); return -1; } @@ -1852,6 +2038,7 @@ int x509_certificate_chain_validate(struct x509_certificate *trusted, os_get_time(&now); for (cert = chain, idx = 0; cert; cert = cert->next, idx++) { + cert->issuer_trusted = 0; x509_name_string(&cert->subject, buf, sizeof(buf)); wpa_printf(MSG_DEBUG, "X509: %lu: %s", idx, buf); @@ -1937,6 +2124,7 @@ int x509_certificate_chain_validate(struct x509_certificate *trusted, wpa_printf(MSG_DEBUG, "X509: Trusted certificate " "found to complete the chain"); + cert->issuer_trusted = 1; chain_trusted = 1; } } diff --git a/contrib/wpa/src/tls/x509v3.h b/contrib/wpa/src/tls/x509v3.h index 91a35baf92b1..7df8e2ab0870 100644 --- a/contrib/wpa/src/tls/x509v3.h +++ b/contrib/wpa/src/tls/x509v3.h @@ -45,13 +45,18 @@ struct x509_name { struct asn1_oid rid; /* registeredID */ }; +#define X509_MAX_SERIAL_NUM_LEN 20 + struct x509_certificate { struct x509_certificate *next; enum { X509_CERT_V1 = 0, X509_CERT_V2 = 1, X509_CERT_V3 = 2 } version; - unsigned long serial_number; + u8 serial_number[X509_MAX_SERIAL_NUM_LEN]; + size_t serial_number_len; struct x509_algorithm_identifier signature; struct x509_name issuer; struct x509_name subject; + u8 *subject_dn; + size_t subject_dn_len; os_time_t not_before; os_time_t not_after; struct x509_algorithm_identifier public_key_alg; @@ -68,6 +73,7 @@ struct x509_certificate { #define X509_EXT_KEY_USAGE (1 << 2) #define X509_EXT_SUBJECT_ALT_NAME (1 << 3) #define X509_EXT_ISSUER_ALT_NAME (1 << 4) +#define X509_EXT_EXT_KEY_USAGE (1 << 5) /* BasicConstraints */ int ca; /* cA */ @@ -85,6 +91,13 @@ struct x509_certificate { #define X509_KEY_USAGE_ENCIPHER_ONLY (1 << 7) #define X509_KEY_USAGE_DECIPHER_ONLY (1 << 8) + /* ExtKeyUsage */ + unsigned long ext_key_usage; +#define X509_EXT_KEY_USAGE_ANY (1 << 0) +#define X509_EXT_KEY_USAGE_SERVER_AUTH (1 << 1) +#define X509_EXT_KEY_USAGE_CLIENT_AUTH (1 << 2) +#define X509_EXT_KEY_USAGE_OCSP (1 << 3) + /* * The DER format certificate follows struct x509_certificate. These * pointers point to that buffer. @@ -93,6 +106,11 @@ struct x509_certificate { size_t cert_len; const u8 *tbs_cert_start; size_t tbs_cert_len; + + /* Meta data used for certificate validation */ + unsigned int ocsp_good:1; + unsigned int ocsp_revoked:1; + unsigned int issuer_trusted:1; }; enum { @@ -106,10 +124,21 @@ enum { }; void x509_certificate_free(struct x509_certificate *cert); +int x509_parse_algorithm_identifier(const u8 *buf, size_t len, + struct x509_algorithm_identifier *id, + const u8 **next); +int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, + const u8 **next); +int x509_parse_time(const u8 *buf, size_t len, u8 asn1_tag, os_time_t *val); struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len); +void x509_free_name(struct x509_name *name); void x509_name_string(struct x509_name *name, char *buf, size_t len); int x509_name_compare(struct x509_name *a, struct x509_name *b); void x509_certificate_chain_free(struct x509_certificate *cert); +int x509_check_signature(struct x509_certificate *issuer, + struct x509_algorithm_identifier *signature, + const u8 *sign_value, size_t sign_value_len, + const u8 *signed_data, size_t signed_data_len); int x509_certificate_check_signature(struct x509_certificate *issuer, struct x509_certificate *cert); int x509_certificate_chain_validate(struct x509_certificate *trusted, @@ -120,4 +149,9 @@ x509_certificate_get_subject(struct x509_certificate *chain, struct x509_name *name); int x509_certificate_self_signed(struct x509_certificate *cert); +int x509_sha1_oid(struct asn1_oid *oid); +int x509_sha256_oid(struct asn1_oid *oid); +int x509_sha384_oid(struct asn1_oid *oid); +int x509_sha512_oid(struct asn1_oid *oid); + #endif /* X509V3_H */ diff --git a/contrib/wpa/src/utils/browser-android.c b/contrib/wpa/src/utils/browser-android.c index 9ce1a5cbeae1..71a165269cf6 100644 --- a/contrib/wpa/src/utils/browser-android.c +++ b/contrib/wpa/src/utils/browser-android.c @@ -95,7 +95,7 @@ int hs20_web_browser(const char *url) if (pid == 0) { /* run the external command in the child process */ - char *argv[9]; + char *argv[7]; argv[0] = "browser-android"; argv[1] = "start"; @@ -103,9 +103,7 @@ int hs20_web_browser(const char *url) argv[3] = "android.intent.action.VIEW"; argv[4] = "-d"; argv[5] = (void *) url; - argv[6] = "-n"; - argv[7] = "com.android.browser/.BrowserActivity"; - argv[8] = NULL; + argv[6] = NULL; execv("/system/bin/am", argv); wpa_printf(MSG_ERROR, "execv: %s", strerror(errno)); diff --git a/contrib/wpa/src/utils/common.c b/contrib/wpa/src/utils/common.c index 660e9fc985d6..04a533a05902 100644 --- a/contrib/wpa/src/utils/common.c +++ b/contrib/wpa/src/utils/common.c @@ -86,7 +86,7 @@ int hwaddr_masked_aton(const char *txt, u8 *addr, u8 *mask, u8 maskable) return -1; /* check for optional mask */ - if (*r == '\0' || isspace(*r)) { + if (*r == '\0' || isspace((unsigned char) *r)) { /* no mask specified, assume default */ os_memset(mask, 0xff, ETH_ALEN); } else if (maskable && *r == '/') { @@ -498,7 +498,7 @@ void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len) *txt++ = 't'; break; default: - if (data[i] >= 32 && data[i] <= 127) { + if (data[i] >= 32 && data[i] <= 126) { *txt++ = data[i]; } else { txt += os_snprintf(txt, end - txt, "\\x%02x", @@ -697,6 +697,29 @@ int is_hex(const u8 *data, size_t len) } +int has_ctrl_char(const u8 *data, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) { + if (data[i] < 32 || data[i] == 127) + return 1; + } + return 0; +} + + +int has_newline(const char *str) +{ + while (*str) { + if (*str == '\n' || *str == '\r') + return 1; + str++; + } + return 0; +} + + size_t merge_byte_arrays(u8 *res, size_t res_len, const u8 *src1, size_t src1_len, const u8 *src2, size_t src2_len) @@ -978,7 +1001,7 @@ int random_mac_addr_keep_oui(u8 *addr) * @delim: a string of delimiters * @last: a pointer to a character following the returned token * It has to be set to NULL for the first call and passed for any - * futher call. + * further call. * Returns: a pointer to token position in str or NULL * * This function is similar to str_token, but it can be used with both @@ -1123,3 +1146,57 @@ int is_ctrl_char(char c) { return c > 0 && c < 32; } + + +/** + * ssid_parse - Parse a string that contains SSID in hex or text format + * @buf: Input NULL terminated string that contains the SSID + * @ssid: Output SSID + * Returns: 0 on success, -1 otherwise + * + * The SSID has to be enclosed in double quotes for the text format or space + * or NULL terminated string of hex digits for the hex format. buf can include + * additional arguments after the SSID. + */ +int ssid_parse(const char *buf, struct wpa_ssid_value *ssid) +{ + char *tmp, *res, *end; + size_t len; + + ssid->ssid_len = 0; + + tmp = os_strdup(buf); + if (!tmp) + return -1; + + if (*tmp != '"') { + end = os_strchr(tmp, ' '); + if (end) + *end = '\0'; + } else { + end = os_strchr(tmp + 1, '"'); + if (!end) { + os_free(tmp); + return -1; + } + + end[1] = '\0'; + } + + res = wpa_config_parse_string(tmp, &len); + if (res && len <= SSID_MAX_LEN) { + ssid->ssid_len = len; + os_memcpy(ssid->ssid, res, len); + } + + os_free(tmp); + os_free(res); + + return ssid->ssid_len ? 0 : -1; +} + + +int str_starts(const char *str, const char *start) +{ + return os_strncmp(str, start, os_strlen(start)) == 0; +} diff --git a/contrib/wpa/src/utils/common.h b/contrib/wpa/src/utils/common.h index 0b9cc3d88209..77856774d215 100644 --- a/contrib/wpa/src/utils/common.h +++ b/contrib/wpa/src/utils/common.h @@ -313,6 +313,9 @@ static inline void WPA_PUT_LE64(u8 *a, u64 val) #ifndef ETH_P_ALL #define ETH_P_ALL 0x0003 #endif +#ifndef ETH_P_IP +#define ETH_P_IP 0x0800 +#endif #ifndef ETH_P_80211_ENCAP #define ETH_P_80211_ENCAP 0x890d /* TDLS comes under this category */ #endif @@ -416,6 +419,7 @@ void perror(const char *s); */ #ifdef __CHECKER__ #define __force __attribute__((force)) +#undef __bitwise #define __bitwise __attribute__((bitwise)) #else #define __force @@ -445,6 +449,13 @@ typedef u64 __bitwise le64; #endif /* __GNUC__ */ #endif /* __must_check */ +#define SSID_MAX_LEN 32 + +struct wpa_ssid_value { + u8 ssid[SSID_MAX_LEN]; + size_t ssid_len; +}; + int hwaddr_aton(const char *txt, u8 *addr); int hwaddr_masked_aton(const char *txt, u8 *addr, u8 *mask, u8 maskable); int hwaddr_compact_aton(const char *txt, u8 *addr); @@ -461,6 +472,7 @@ int wpa_snprintf_hex_uppercase(char *buf, size_t buf_size, const u8 *data, size_t len); int hwaddr_mask_txt(char *buf, size_t len, const u8 *addr, const u8 *mask); +int ssid_parse(const char *buf, struct wpa_ssid_value *ssid); #ifdef CONFIG_NATIVE_WINDOWS void wpa_unicode2ascii_inplace(TCHAR *str); @@ -477,6 +489,8 @@ const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len); char * wpa_config_parse_string(const char *value, size_t *len); int is_hex(const u8 *data, size_t len); +int has_ctrl_char(const u8 *data, size_t len); +int has_newline(const char *str); size_t merge_byte_arrays(u8 *res, size_t res_len, const u8 *src1, size_t src1_len, const u8 *src2, size_t src2_len); @@ -536,6 +550,8 @@ size_t utf8_unescape(const char *inp, size_t in_size, char *outp, size_t out_size); int is_ctrl_char(char c); +int str_starts(const char *str, const char *start); + /* * gcc 4.4 ends up generating strict-aliasing warnings about some very common diff --git a/contrib/wpa/src/utils/edit_simple.c b/contrib/wpa/src/utils/edit_simple.c index 13173cb19361..2ffd1a2a2b7e 100644 --- a/contrib/wpa/src/utils/edit_simple.c +++ b/contrib/wpa/src/utils/edit_simple.c @@ -47,6 +47,12 @@ static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx) return; } + if (c == '\b') { + if (cmdbuf_pos > 0) + cmdbuf_pos--; + return; + } + if (c >= 32 && c <= 255) { if (cmdbuf_pos < (int) sizeof(cmdbuf) - 1) { cmdbuf[cmdbuf_pos++] = c; diff --git a/contrib/wpa/src/utils/eloop.c b/contrib/wpa/src/utils/eloop.c index 8647229b8eb5..436bc8c99338 100644 --- a/contrib/wpa/src/utils/eloop.c +++ b/contrib/wpa/src/utils/eloop.c @@ -18,7 +18,12 @@ #error Do not define both of poll and epoll #endif -#if !defined(CONFIG_ELOOP_POLL) && !defined(CONFIG_ELOOP_EPOLL) +#if defined(CONFIG_ELOOP_POLL) && defined(CONFIG_ELOOP_KQUEUE) +#error Do not define both of poll and kqueue +#endif + +#if !defined(CONFIG_ELOOP_POLL) && !defined(CONFIG_ELOOP_EPOLL) && \ + !defined(CONFIG_ELOOP_KQUEUE) #define CONFIG_ELOOP_SELECT #endif @@ -30,6 +35,10 @@ #include #endif /* CONFIG_ELOOP_EPOLL */ +#ifdef CONFIG_ELOOP_KQUEUE +#include +#endif /* CONFIG_ELOOP_KQUEUE */ + struct eloop_sock { int sock; void *eloop_data; @@ -75,13 +84,20 @@ struct eloop_data { struct pollfd *pollfds; struct pollfd **pollfds_map; #endif /* CONFIG_ELOOP_POLL */ +#if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE) + int max_fd; + struct eloop_sock *fd_table; +#endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */ #ifdef CONFIG_ELOOP_EPOLL int epollfd; int epoll_max_event_num; - int epoll_max_fd; - struct eloop_sock *epoll_table; struct epoll_event *epoll_events; #endif /* CONFIG_ELOOP_EPOLL */ +#ifdef CONFIG_ELOOP_KQUEUE + int kqueuefd; + int kqueue_nevents; + struct kevent *kqueue_events; +#endif /* CONFIG_ELOOP_KQUEUE */ struct eloop_sock_table readers; struct eloop_sock_table writers; struct eloop_sock_table exceptions; @@ -149,14 +165,24 @@ int eloop_init(void) #ifdef CONFIG_ELOOP_EPOLL eloop.epollfd = epoll_create1(0); if (eloop.epollfd < 0) { - wpa_printf(MSG_ERROR, "%s: epoll_create1 failed. %s\n", + wpa_printf(MSG_ERROR, "%s: epoll_create1 failed. %s", __func__, strerror(errno)); return -1; } +#endif /* CONFIG_ELOOP_EPOLL */ +#ifdef CONFIG_ELOOP_KQUEUE + eloop.kqueuefd = kqueue(); + if (eloop.kqueuefd < 0) { + wpa_printf(MSG_ERROR, "%s: kqueue failed: %s", + __func__, strerror(errno)); + return -1; + } +#endif /* CONFIG_ELOOP_KQUEUE */ +#if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE) eloop.readers.type = EVENT_TYPE_READ; eloop.writers.type = EVENT_TYPE_WRITE; eloop.exceptions.type = EVENT_TYPE_EXCEPTION; -#endif /* CONFIG_ELOOP_EPOLL */ +#endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */ #ifdef WPA_TRACE signal(SIGSEGV, eloop_sigsegv_handler); #endif /* WPA_TRACE */ @@ -164,15 +190,80 @@ int eloop_init(void) } +#ifdef CONFIG_ELOOP_EPOLL +static int eloop_sock_queue(int sock, eloop_event_type type) +{ + struct epoll_event ev; + + os_memset(&ev, 0, sizeof(ev)); + switch (type) { + case EVENT_TYPE_READ: + ev.events = EPOLLIN; + break; + case EVENT_TYPE_WRITE: + ev.events = EPOLLOUT; + break; + /* + * Exceptions are always checked when using epoll, but I suppose it's + * possible that someone registered a socket *only* for exception + * handling. + */ + case EVENT_TYPE_EXCEPTION: + ev.events = EPOLLERR | EPOLLHUP; + break; + } + ev.data.fd = sock; + if (epoll_ctl(eloop.epollfd, EPOLL_CTL_ADD, sock, &ev) < 0) { + wpa_printf(MSG_ERROR, "%s: epoll_ctl(ADD) for fd=%d failed: %s", + __func__, sock, strerror(errno)); + return -1; + } + return 0; +} +#endif /* CONFIG_ELOOP_EPOLL */ + + +#ifdef CONFIG_ELOOP_KQUEUE +static int eloop_sock_queue(int sock, eloop_event_type type) +{ + int filter; + struct kevent ke; + + switch (type) { + case EVENT_TYPE_READ: + filter = EVFILT_READ; + break; + case EVENT_TYPE_WRITE: + filter = EVFILT_WRITE; + break; + default: + filter = 0; + } + EV_SET(&ke, sock, filter, EV_ADD, 0, 0, 0); + if (kevent(eloop.kqueuefd, &ke, 1, NULL, 0, NULL) == -1) { + wpa_printf(MSG_ERROR, "%s: kevent(ADD) for fd=%d failed: %s", + __func__, sock, strerror(errno)); + return -1; + } + return 0; +} +#endif /* CONFIG_ELOOP_KQUEUE */ + + static int eloop_sock_table_add_sock(struct eloop_sock_table *table, int sock, eloop_sock_handler handler, void *eloop_data, void *user_data) { #ifdef CONFIG_ELOOP_EPOLL - struct eloop_sock *temp_table; - struct epoll_event ev, *temp_events; - int next; + struct epoll_event *temp_events; #endif /* CONFIG_ELOOP_EPOLL */ +#ifdef CONFIG_ELOOP_KQUEUE + struct kevent *temp_events; +#endif /* CONFIG_ELOOP_EPOLL */ +#if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE) + struct eloop_sock *temp_table; + int next; +#endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */ struct eloop_sock *tmp; int new_max_sock; @@ -208,26 +299,28 @@ static int eloop_sock_table_add_sock(struct eloop_sock_table *table, eloop.pollfds = n; } #endif /* CONFIG_ELOOP_POLL */ -#ifdef CONFIG_ELOOP_EPOLL - if (new_max_sock >= eloop.epoll_max_fd) { - next = eloop.epoll_max_fd == 0 ? 16 : eloop.epoll_max_fd * 2; - temp_table = os_realloc_array(eloop.epoll_table, next, +#if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE) + if (new_max_sock >= eloop.max_fd) { + next = eloop.max_fd == 0 ? 16 : eloop.max_fd * 2; + temp_table = os_realloc_array(eloop.fd_table, next, sizeof(struct eloop_sock)); if (temp_table == NULL) return -1; - eloop.epoll_max_fd = next; - eloop.epoll_table = temp_table; + eloop.max_fd = next; + eloop.fd_table = temp_table; } +#endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */ +#ifdef CONFIG_ELOOP_EPOLL if (eloop.count + 1 > eloop.epoll_max_event_num) { next = eloop.epoll_max_event_num == 0 ? 8 : eloop.epoll_max_event_num * 2; temp_events = os_realloc_array(eloop.epoll_events, next, sizeof(struct epoll_event)); if (temp_events == NULL) { - wpa_printf(MSG_ERROR, "%s: malloc for epoll failed. " - "%s\n", __func__, strerror(errno)); + wpa_printf(MSG_ERROR, "%s: malloc for epoll failed: %s", + __func__, strerror(errno)); return -1; } @@ -235,6 +328,22 @@ static int eloop_sock_table_add_sock(struct eloop_sock_table *table, eloop.epoll_events = temp_events; } #endif /* CONFIG_ELOOP_EPOLL */ +#ifdef CONFIG_ELOOP_KQUEUE + if (eloop.count + 1 > eloop.kqueue_nevents) { + next = eloop.kqueue_nevents == 0 ? 8 : eloop.kqueue_nevents * 2; + temp_events = os_malloc(next * sizeof(*temp_events)); + if (!temp_events) { + wpa_printf(MSG_ERROR, + "%s: malloc for kqueue failed: %s", + __func__, strerror(errno)); + return -1; + } + + os_free(eloop.kqueue_events); + eloop.kqueue_events = temp_events; + eloop.kqueue_nevents = next; + } +#endif /* CONFIG_ELOOP_KQUEUE */ eloop_trace_sock_remove_ref(table); tmp = os_realloc_array(table->table, table->count + 1, @@ -256,33 +365,12 @@ static int eloop_sock_table_add_sock(struct eloop_sock_table *table, table->changed = 1; eloop_trace_sock_add_ref(table); -#ifdef CONFIG_ELOOP_EPOLL - os_memset(&ev, 0, sizeof(ev)); - switch (table->type) { - case EVENT_TYPE_READ: - ev.events = EPOLLIN; - break; - case EVENT_TYPE_WRITE: - ev.events = EPOLLOUT; - break; - /* - * Exceptions are always checked when using epoll, but I suppose it's - * possible that someone registered a socket *only* for exception - * handling. - */ - case EVENT_TYPE_EXCEPTION: - ev.events = EPOLLERR | EPOLLHUP; - break; - } - ev.data.fd = sock; - if (epoll_ctl(eloop.epollfd, EPOLL_CTL_ADD, sock, &ev) < 0) { - wpa_printf(MSG_ERROR, "%s: epoll_ctl(ADD) for fd=%d " - "failed. %s\n", __func__, sock, strerror(errno)); +#if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE) + if (eloop_sock_queue(sock, table->type) < 0) return -1; - } - os_memcpy(&eloop.epoll_table[sock], &table->table[table->count - 1], + os_memcpy(&eloop.fd_table[sock], &table->table[table->count - 1], sizeof(struct eloop_sock)); -#endif /* CONFIG_ELOOP_EPOLL */ +#endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */ return 0; } @@ -290,6 +378,9 @@ static int eloop_sock_table_add_sock(struct eloop_sock_table *table, static void eloop_sock_table_remove_sock(struct eloop_sock_table *table, int sock) { +#ifdef CONFIG_ELOOP_KQUEUE + struct kevent ke; +#endif /* CONFIG_ELOOP_KQUEUE */ int i; if (table == NULL || table->table == NULL || table->count == 0) @@ -313,12 +404,21 @@ static void eloop_sock_table_remove_sock(struct eloop_sock_table *table, eloop_trace_sock_add_ref(table); #ifdef CONFIG_ELOOP_EPOLL if (epoll_ctl(eloop.epollfd, EPOLL_CTL_DEL, sock, NULL) < 0) { - wpa_printf(MSG_ERROR, "%s: epoll_ctl(DEL) for fd=%d " - "failed. %s\n", __func__, sock, strerror(errno)); + wpa_printf(MSG_ERROR, "%s: epoll_ctl(DEL) for fd=%d failed: %s", + __func__, sock, strerror(errno)); return; } - os_memset(&eloop.epoll_table[sock], 0, sizeof(struct eloop_sock)); + os_memset(&eloop.fd_table[sock], 0, sizeof(struct eloop_sock)); #endif /* CONFIG_ELOOP_EPOLL */ +#ifdef CONFIG_ELOOP_KQUEUE + EV_SET(&ke, sock, 0, EV_DELETE, 0, 0, 0); + if (kevent(eloop.kqueuefd, &ke, 1, NULL, 0, NULL) < 0) { + wpa_printf(MSG_ERROR, "%s: kevent(DEL) for fd=%d failed: %s", + __func__, sock, strerror(errno)); + return; + } + os_memset(&eloop.fd_table[sock], 0, sizeof(struct eloop_sock)); +#endif /* CONFIG_ELOOP_KQUEUE */ } @@ -511,7 +611,7 @@ static void eloop_sock_table_dispatch(struct epoll_event *events, int nfds) int i; for (i = 0; i < nfds; i++) { - table = &eloop.epoll_table[events[i].data.fd]; + table = &eloop.fd_table[events[i].data.fd]; if (table->handler == NULL) continue; table->handler(table->sock, table->eloop_data, @@ -525,6 +625,67 @@ static void eloop_sock_table_dispatch(struct epoll_event *events, int nfds) #endif /* CONFIG_ELOOP_EPOLL */ +#ifdef CONFIG_ELOOP_KQUEUE + +static void eloop_sock_table_dispatch(struct kevent *events, int nfds) +{ + struct eloop_sock *table; + int i; + + for (i = 0; i < nfds; i++) { + table = &eloop.fd_table[events[i].ident]; + if (table->handler == NULL) + continue; + table->handler(table->sock, table->eloop_data, + table->user_data); + if (eloop.readers.changed || + eloop.writers.changed || + eloop.exceptions.changed) + break; + } +} + + +static int eloop_sock_table_requeue(struct eloop_sock_table *table) +{ + int i, r; + + r = 0; + for (i = 0; i < table->count && table->table; i++) { + if (eloop_sock_queue(table->table[i].sock, table->type) == -1) + r = -1; + } + return r; +} + +#endif /* CONFIG_ELOOP_KQUEUE */ + + +int eloop_sock_requeue(void) +{ + int r = 0; + +#ifdef CONFIG_ELOOP_KQUEUE + close(eloop.kqueuefd); + eloop.kqueuefd = kqueue(); + if (eloop.kqueuefd < 0) { + wpa_printf(MSG_ERROR, "%s: kqueue failed: %s", + __func__, strerror(errno)); + return -1; + } + + if (eloop_sock_table_requeue(&eloop.readers) < 0) + r = -1; + if (eloop_sock_table_requeue(&eloop.writers) < 0) + r = -1; + if (eloop_sock_table_requeue(&eloop.exceptions) < 0) + r = -1; +#endif /* CONFIG_ELOOP_KQUEUE */ + + return r; +} + + static void eloop_sock_table_destroy(struct eloop_sock_table *table) { if (table) { @@ -905,6 +1066,9 @@ void eloop_run(void) #ifdef CONFIG_ELOOP_EPOLL int timeout_ms = -1; #endif /* CONFIG_ELOOP_EPOLL */ +#ifdef CONFIG_ELOOP_KQUEUE + struct timespec ts; +#endif /* CONFIG_ELOOP_KQUEUE */ int res; struct os_reltime tv, now; @@ -949,6 +1113,10 @@ void eloop_run(void) _tv.tv_sec = tv.sec; _tv.tv_usec = tv.usec; #endif /* CONFIG_ELOOP_SELECT */ +#ifdef CONFIG_ELOOP_KQUEUE + ts.tv_sec = tv.sec; + ts.tv_nsec = tv.usec * 1000L; +#endif /* CONFIG_ELOOP_KQUEUE */ } #ifdef CONFIG_ELOOP_POLL @@ -974,6 +1142,15 @@ void eloop_run(void) eloop.count, timeout_ms); } #endif /* CONFIG_ELOOP_EPOLL */ +#ifdef CONFIG_ELOOP_KQUEUE + if (eloop.count == 0) { + res = 0; + } else { + res = kevent(eloop.kqueuefd, NULL, 0, + eloop.kqueue_events, eloop.kqueue_nevents, + timeout ? &ts : NULL); + } +#endif /* CONFIG_ELOOP_KQUEUE */ if (res < 0 && errno != EINTR && errno != 0) { wpa_printf(MSG_ERROR, "eloop: %s: %s", #ifdef CONFIG_ELOOP_POLL @@ -985,6 +1162,10 @@ void eloop_run(void) #ifdef CONFIG_ELOOP_EPOLL "epoll" #endif /* CONFIG_ELOOP_EPOLL */ +#ifdef CONFIG_ELOOP_KQUEUE + "kqueue" +#endif /* CONFIG_ELOOP_EKQUEUE */ + , strerror(errno)); goto out; } @@ -995,6 +1176,7 @@ void eloop_run(void) eloop_process_pending_signals(); + /* check if some registered timeouts have occurred */ timeout = dl_list_first(&eloop.timeout, struct eloop_timeout, list); @@ -1040,6 +1222,9 @@ void eloop_run(void) #ifdef CONFIG_ELOOP_EPOLL eloop_sock_table_dispatch(eloop.epoll_events, res); #endif /* CONFIG_ELOOP_EPOLL */ +#ifdef CONFIG_ELOOP_KQUEUE + eloop_sock_table_dispatch(eloop.kqueue_events, res); +#endif /* CONFIG_ELOOP_KQUEUE */ } eloop.terminate = 0; @@ -1092,11 +1277,17 @@ void eloop_destroy(void) os_free(eloop.pollfds); os_free(eloop.pollfds_map); #endif /* CONFIG_ELOOP_POLL */ +#if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE) + os_free(eloop.fd_table); +#endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */ #ifdef CONFIG_ELOOP_EPOLL - os_free(eloop.epoll_table); os_free(eloop.epoll_events); close(eloop.epollfd); #endif /* CONFIG_ELOOP_EPOLL */ +#ifdef CONFIG_ELOOP_KQUEUE + os_free(eloop.kqueue_events); + close(eloop.kqueuefd); +#endif /* CONFIG_ELOOP_KQUEUE */ } @@ -1135,6 +1326,17 @@ void eloop_wait_for_read_sock(int sock) FD_SET(sock, &rfds); select(sock + 1, &rfds, NULL, NULL, NULL); #endif /* defined(CONFIG_ELOOP_SELECT) || defined(CONFIG_ELOOP_EPOLL) */ +#ifdef CONFIG_ELOOP_KQUEUE + int kfd; + struct kevent ke1, ke2; + + kfd = kqueue(); + if (kfd == -1) + return; + EV_SET(&ke1, sock, EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, 0); + kevent(kfd, &ke1, 1, &ke2, 1, NULL); + close(kfd); +#endif /* CONFIG_ELOOP_KQUEUE */ } #ifdef CONFIG_ELOOP_SELECT diff --git a/contrib/wpa/src/utils/eloop.h b/contrib/wpa/src/utils/eloop.h index 07b8c0dc3352..97af16f0130a 100644 --- a/contrib/wpa/src/utils/eloop.h +++ b/contrib/wpa/src/utils/eloop.h @@ -312,6 +312,14 @@ int eloop_register_signal_terminate(eloop_signal_handler handler, int eloop_register_signal_reconfig(eloop_signal_handler handler, void *user_data); +/** + * eloop_sock_requeue - Requeue sockets + * + * Requeue sockets after forking because some implementations require this, + * such as epoll and kqueue. + */ +int eloop_sock_requeue(void); + /** * eloop_run - Start the event loop * diff --git a/contrib/wpa/src/utils/eloop_win.c b/contrib/wpa/src/utils/eloop_win.c index de47fb21837c..9c8b12be8ad8 100644 --- a/contrib/wpa/src/utils/eloop_win.c +++ b/contrib/wpa/src/utils/eloop_win.c @@ -692,3 +692,9 @@ void eloop_wait_for_read_sock(int sock) WSAEventSelect(sock, event, 0); WSACloseEvent(event); } + + +int eloop_sock_requeue(void) +{ + return 0; +} diff --git a/contrib/wpa/src/utils/ext_password.c b/contrib/wpa/src/utils/ext_password.c index 06131197a311..5615bd72a714 100644 --- a/contrib/wpa/src/utils/ext_password.c +++ b/contrib/wpa/src/utils/ext_password.c @@ -16,10 +16,6 @@ #include "ext_password_i.h" -#ifdef CONFIG_EXT_PASSWORD_TEST -extern struct ext_password_backend ext_password_test; -#endif /* CONFIG_EXT_PASSWORD_TEST */ - static const struct ext_password_backend *backends[] = { #ifdef CONFIG_EXT_PASSWORD_TEST &ext_password_test, diff --git a/contrib/wpa/src/utils/ext_password_i.h b/contrib/wpa/src/utils/ext_password_i.h index 043e7312c62f..948eaf5421b9 100644 --- a/contrib/wpa/src/utils/ext_password_i.h +++ b/contrib/wpa/src/utils/ext_password_i.h @@ -20,4 +20,10 @@ struct ext_password_backend { struct wpabuf * ext_password_alloc(size_t len); +/* Available ext_password backends */ + +#ifdef CONFIG_EXT_PASSWORD_TEST +extern const struct ext_password_backend ext_password_test; +#endif /* CONFIG_EXT_PASSWORD_TEST */ + #endif /* EXT_PASSWORD_I_H */ diff --git a/contrib/wpa/src/utils/http_curl.c b/contrib/wpa/src/utils/http_curl.c index 653eb541ab47..a06aae8d9b9d 100644 --- a/contrib/wpa/src/utils/http_curl.c +++ b/contrib/wpa/src/utils/http_curl.c @@ -26,6 +26,9 @@ #include "common.h" #include "xml-utils.h" #include "http-utils.h" +#ifdef EAP_TLS_OPENSSL +#include "crypto/tls_openssl.h" +#endif /* EAP_TLS_OPENSSL */ struct http_ctx { @@ -421,6 +424,28 @@ ASN1_SEQUENCE(LogotypeExtn) = { IMPLEMENT_ASN1_FUNCTIONS(LogotypeExtn); +#ifdef OPENSSL_IS_BORINGSSL +#define sk_LogotypeInfo_num(st) \ +sk_num(CHECKED_CAST(_STACK *, STACK_OF(LogotypeInfo) *, (st))) +#define sk_LogotypeInfo_value(st, i) (LogotypeInfo *) \ +sk_value(CHECKED_CAST(_STACK *, const STACK_OF(LogotypeInfo) *, (st)), (i)) +#define sk_LogotypeImage_num(st) \ +sk_num(CHECKED_CAST(_STACK *, STACK_OF(LogotypeImage) *, (st))) +#define sk_LogotypeImage_value(st, i) (LogotypeImage *) \ +sk_value(CHECKED_CAST(_STACK *, const STACK_OF(LogotypeImage) *, (st)), (i)) +#define sk_LogotypeAudio_num(st) \ +sk_num(CHECKED_CAST(_STACK *, STACK_OF(LogotypeAudio) *, (st))) +#define sk_LogotypeAudio_value(st, i) (LogotypeAudio *) \ +sk_value(CHECK_CAST(_STACK *, const STACK_OF(LogotypeAudio) *, (st)), (i)) +#define sk_HashAlgAndValue_num(st) \ +sk_num(CHECKED_CAST(_STACK *, STACK_OF(HashAlgAndValue) *, (st))) +#define sk_HashAlgAndValue_value(st, i) (HashAlgAndValue *) \ +sk_value(CHECKED_CAST(_STACK *, const STACK_OF(HashAlgAndValue) *, (st)), (i)) +#define sk_ASN1_IA5STRING_num(st) \ +sk_num(CHECKED_CAST(_STACK *, STACK_OF(ASN1_IA5STRING) *, (st))) +#define sk_ASN1_IA5STRING_value(st, i) (ASN1_IA5STRING *) \ +sk_value(CHECKED_CAST(_STACK *, const STACK_OF(ASN1_IA5STRING) *, (st)), (i)) +#else /* OPENSSL_IS_BORINGSSL */ #define sk_LogotypeInfo_num(st) SKM_sk_num(LogotypeInfo, (st)) #define sk_LogotypeInfo_value(st, i) SKM_sk_value(LogotypeInfo, (st), (i)) #define sk_LogotypeImage_num(st) SKM_sk_num(LogotypeImage, (st)) @@ -431,6 +456,7 @@ IMPLEMENT_ASN1_FUNCTIONS(LogotypeExtn); #define sk_HashAlgAndValue_value(st, i) SKM_sk_value(HashAlgAndValue, (st), (i)) #define sk_ASN1_IA5STRING_num(st) SKM_sk_num(ASN1_IA5STRING, (st)) #define sk_ASN1_IA5STRING_value(st, i) SKM_sk_value(ASN1_IA5STRING, (st), (i)) +#endif /* OPENSSL_IS_BORINGSSL */ static void add_logo(struct http_ctx *ctx, struct http_cert *hcert, @@ -618,13 +644,25 @@ static void i2r_LogotypeImageInfo(LogotypeImageInfo *info, BIO *out, int indent) } else { BIO_printf(out, "%*stype: default (1)\n", indent, ""); } + val = ASN1_INTEGER_get(info->fileSize); + BIO_printf(out, "%*sfileSize: %ld\n", indent, "", val); val = ASN1_INTEGER_get(info->xSize); BIO_printf(out, "%*sxSize: %ld\n", indent, "", val); val = ASN1_INTEGER_get(info->ySize); BIO_printf(out, "%*sySize: %ld\n", indent, "", val); if (info->resolution) { - BIO_printf(out, "%*sresolution\n", indent, ""); - /* TODO */ + BIO_printf(out, "%*sresolution [%d]\n", indent, "", + info->resolution->type); + switch (info->resolution->type) { + case 0: + val = ASN1_INTEGER_get(info->resolution->d.numBits); + BIO_printf(out, "%*snumBits: %ld\n", indent, "", val); + break; + case 1: + val = ASN1_INTEGER_get(info->resolution->d.tableSize); + BIO_printf(out, "%*stableSize: %ld\n", indent, "", val); + break; + } } if (info->language) { BIO_printf(out, "%*slanguage: ", indent, ""); @@ -981,6 +1019,26 @@ static int curl_cb_ssl_verify(int preverify_ok, X509_STORE_CTX *x509_ctx) if (depth == 0 && preverify_ok && validate_server_cert(ctx, cert) < 0) return 0; +#ifdef OPENSSL_IS_BORINGSSL + if (depth == 0 && ctx->ocsp != NO_OCSP && preverify_ok) { + enum ocsp_result res; + + res = check_ocsp_resp(ssl_ctx, ssl, cert, ctx->peer_issuer, + ctx->peer_issuer_issuer); + if (res == OCSP_REVOKED) { + preverify_ok = 0; + wpa_printf(MSG_INFO, "OCSP: certificate revoked"); + if (err == X509_V_OK) + X509_STORE_CTX_set_error( + x509_ctx, X509_V_ERR_CERT_REVOKED); + } else if (res != OCSP_GOOD && (ctx->ocsp == MANDATORY_OCSP)) { + preverify_ok = 0; + wpa_printf(MSG_INFO, + "OCSP: bad certificate status response"); + } + } +#endif /* OPENSSL_IS_BORINGSSL */ + if (!preverify_ok) ctx->last_err = "TLS validation failed"; @@ -1156,6 +1214,7 @@ static int ocsp_resp_cb(SSL *s, void *arg) wpa_printf(MSG_INFO, "OpenSSL: Could not find current server certificate from OCSP response%s", (ctx->ocsp == MANDATORY_OCSP) ? "" : " (OCSP not required)"); + OCSP_CERTID_free(id); OCSP_BASICRESP_free(basic); OCSP_RESPONSE_free(rsp); if (ctx->ocsp == MANDATORY_OCSP) @@ -1163,6 +1222,7 @@ static int ocsp_resp_cb(SSL *s, void *arg) ctx->last_err = "Could not find current server certificate from OCSP response"; return (ctx->ocsp == MANDATORY_OCSP) ? 0 : 1; } + OCSP_CERTID_free(id); if (!OCSP_check_validity(this_update, next_update, 5 * 60, -1)) { tls_show_errors(__func__, "OpenSSL: OCSP status times invalid"); @@ -1273,6 +1333,16 @@ static CURL * setup_curl_post(struct http_ctx *ctx, const char *address, #ifdef EAP_TLS_OPENSSL curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, curl_cb_ssl); curl_easy_setopt(curl, CURLOPT_SSL_CTX_DATA, ctx); +#ifdef OPENSSL_IS_BORINGSSL + /* For now, using the CURLOPT_SSL_VERIFYSTATUS option only + * with BoringSSL since the OpenSSL specific callback hack to + * enable OCSP is not available with BoringSSL. The OCSP + * implementation within libcurl is not sufficient for the + * Hotspot 2.0 OSU needs, so cannot use this with OpenSSL. + */ + if (ctx->ocsp != NO_OCSP) + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYSTATUS, 1L); +#endif /* OPENSSL_IS_BORINGSSL */ #endif /* EAP_TLS_OPENSSL */ } else { curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); diff --git a/contrib/wpa/src/utils/module_tests.h b/contrib/wpa/src/utils/module_tests.h new file mode 100644 index 000000000000..3bfe4ad026cc --- /dev/null +++ b/contrib/wpa/src/utils/module_tests.h @@ -0,0 +1,20 @@ +/* + * Module tests + * Copyright (c) 2014-2015, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef MODULE_TESTS_H +#define MODULE_TESTS_H + +int wpas_module_tests(void); +int hapd_module_tests(void); + +int utils_module_tests(void); +int wps_module_tests(void); +int common_module_tests(void); +int crypto_module_tests(void); + +#endif /* MODULE_TESTS_H */ diff --git a/contrib/wpa/src/utils/os.h b/contrib/wpa/src/utils/os.h index 9e496fb65978..e8f0b792738a 100644 --- a/contrib/wpa/src/utils/os.h +++ b/contrib/wpa/src/utils/os.h @@ -657,6 +657,10 @@ int os_exec(const char *program, const char *arg, int wait_completion); #if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS) #define TEST_FAIL() testing_test_fail() int testing_test_fail(void); +extern char wpa_trace_fail_func[256]; +extern unsigned int wpa_trace_fail_after; +extern char wpa_trace_test_fail_func[256]; +extern unsigned int wpa_trace_test_fail_after; #else #define TEST_FAIL() 0 #endif diff --git a/contrib/wpa/src/utils/os_unix.c b/contrib/wpa/src/utils/os_unix.c index 488995c5fa46..a8cc7cb4bb7c 100644 --- a/contrib/wpa/src/utils/os_unix.c +++ b/contrib/wpa/src/utils/os_unix.c @@ -399,6 +399,7 @@ void os_program_deinit(void) if (total) wpa_printf(MSG_INFO, "MEMLEAK: total %lu bytes", (unsigned long) total); + wpa_trace_deinit(); #endif /* WPA_TRACE */ } @@ -461,11 +462,7 @@ char * os_readfile(const char *name, size_t *len) int os_file_exists(const char *fname) { - FILE *f = fopen(fname, "rb"); - if (f == NULL) - return 0; - fclose(f); - return 1; + return access(fname, F_OK) == 0; } diff --git a/contrib/wpa/src/utils/pcsc_funcs.c b/contrib/wpa/src/utils/pcsc_funcs.c index 6f5ea9396213..383ed3ddfeed 100644 --- a/contrib/wpa/src/utils/pcsc_funcs.c +++ b/contrib/wpa/src/utils/pcsc_funcs.c @@ -11,7 +11,11 @@ */ #include "includes.h" +#ifdef __APPLE__ +#include +#else #include +#endif #include "common.h" #include "pcsc_funcs.h" @@ -110,7 +114,11 @@ typedef enum { SCARD_GSM_SIM, SCARD_USIM } sim_types; struct scard_data { SCARDCONTEXT ctx; SCARDHANDLE card; +#ifdef __APPLE__ + uint32_t protocol; +#else DWORD protocol; +#endif sim_types sim_type; int pin1_required; }; @@ -275,7 +283,7 @@ static int scard_parse_fsp_templ(unsigned char *buf, size_t buf_len, pos++; if (pos >= end) return -1; - if ((pos + pos[0]) < end) + if (pos[0] < end - pos) end = pos + 1 + pos[0]; pos++; wpa_hexdump(MSG_DEBUG, "SCARD: file header FSP template", @@ -504,7 +512,12 @@ static int scard_get_aid(struct scard_data *scard, unsigned char *aid, struct scard_data * scard_init(const char *reader) { long ret; - unsigned long len, pos; +#ifdef __APPLE__ + uint32_t len; +#else + unsigned long len; +#endif + unsigned long pos; struct scard_data *scard; #ifdef CONFIG_NATIVE_WINDOWS TCHAR *readers = NULL; @@ -605,7 +618,7 @@ struct scard_data * scard_init(const char *reader) readers = NULL; wpa_printf(MSG_DEBUG, "SCARD: card=0x%x active_protocol=%lu (%s)", - (unsigned int) scard->card, scard->protocol, + (unsigned int) scard->card, (unsigned long) scard->protocol, scard->protocol == SCARD_PROTOCOL_T0 ? "T0" : "T1"); ret = SCardBeginTransaction(scard->card); @@ -764,7 +777,11 @@ static long scard_transmit(struct scard_data *scard, unsigned char *_recv, size_t *recv_len) { long ret; +#ifdef __APPLE__ + uint32_t rlen; +#else unsigned long rlen; +#endif wpa_hexdump_key(MSG_DEBUG, "SCARD: scard_transmit: send", _send, send_len); @@ -1385,7 +1402,7 @@ int scard_umts_auth(struct scard_data *scard, const unsigned char *_rand, end = buf + len; /* RES */ - if (pos[0] > RES_MAX_LEN || pos + pos[0] > end) { + if (pos[0] > RES_MAX_LEN || pos[0] > end - pos) { wpa_printf(MSG_DEBUG, "SCARD: Invalid RES"); return -1; } @@ -1395,7 +1412,7 @@ int scard_umts_auth(struct scard_data *scard, const unsigned char *_rand, wpa_hexdump(MSG_DEBUG, "SCARD: RES", res, *res_len); /* CK */ - if (pos[0] != CK_LEN || pos + CK_LEN > end) { + if (pos[0] != CK_LEN || CK_LEN > end - pos) { wpa_printf(MSG_DEBUG, "SCARD: Invalid CK"); return -1; } @@ -1405,7 +1422,7 @@ int scard_umts_auth(struct scard_data *scard, const unsigned char *_rand, wpa_hexdump(MSG_DEBUG, "SCARD: CK", ck, CK_LEN); /* IK */ - if (pos[0] != IK_LEN || pos + IK_LEN > end) { + if (pos[0] != IK_LEN || IK_LEN > end - pos) { wpa_printf(MSG_DEBUG, "SCARD: Invalid IK"); return -1; } diff --git a/contrib/wpa/src/utils/platform.h b/contrib/wpa/src/utils/platform.h index 46cfe785e180..813987eb6606 100644 --- a/contrib/wpa/src/utils/platform.h +++ b/contrib/wpa/src/utils/platform.h @@ -15,7 +15,7 @@ \ __ptr->__val; \ }) -#define get_unaligned_le16(p) le16_to_cpu(get_unaligned((uint16_t *)(p))) -#define get_unaligned_le32(p) le32_to_cpu(get_unaligned((uint32_t *)(p))) +#define get_unaligned_le16(p) le16_to_cpu(get_unaligned((le16 *)(p))) +#define get_unaligned_le32(p) le32_to_cpu(get_unaligned((le32 *)(p))) #endif /* PLATFORM_H */ diff --git a/contrib/wpa/src/utils/radiotap.c b/contrib/wpa/src/utils/radiotap.c index c9a502335592..71996eb7908f 100644 --- a/contrib/wpa/src/utils/radiotap.c +++ b/contrib/wpa/src/utils/radiotap.c @@ -13,8 +13,8 @@ * * See COPYING for more details. */ -#include "radiotap_iter.h" #include "platform.h" +#include "radiotap_iter.h" /* function prototypes and related defs are in radiotap_iter.h */ diff --git a/contrib/wpa/src/utils/radiotap.h b/contrib/wpa/src/utils/radiotap.h index 0572e7c963da..460af23d8124 100644 --- a/contrib/wpa/src/utils/radiotap.h +++ b/contrib/wpa/src/utils/radiotap.h @@ -65,12 +65,12 @@ struct ieee80211_radiotap_header { * new fields does not count. */ uint8_t it_pad; - uint16_t it_len; /* length of the whole + le16 it_len; /* length of the whole * header in bytes, including * it_version, it_pad, * it_len, and data fields. */ - uint32_t it_present; /* A bitmap telling which + le32 it_present; /* A bitmap telling which * fields are present. Set bit 31 * (0x80000000) to extend the * bitmap by another 32 bits. diff --git a/contrib/wpa/src/utils/radiotap_iter.h b/contrib/wpa/src/utils/radiotap_iter.h index b768c85baace..6ea07e3b1b83 100644 --- a/contrib/wpa/src/utils/radiotap_iter.h +++ b/contrib/wpa/src/utils/radiotap_iter.h @@ -67,7 +67,7 @@ struct ieee80211_radiotap_iterator { const struct ieee80211_radiotap_namespace *current_namespace; unsigned char *_arg, *_next_ns_data; - uint32_t *_next_bitmap; + le32 *_next_bitmap; unsigned char *this_arg; #ifdef RADIOTAP_SUPPORT_OVERRIDES diff --git a/contrib/wpa/src/utils/trace.c b/contrib/wpa/src/utils/trace.c index 8484d277d24b..d72cf604f8e9 100644 --- a/contrib/wpa/src/utils/trace.c +++ b/contrib/wpa/src/utils/trace.c @@ -366,4 +366,13 @@ void wpa_trace_check_ref(const void *addr) } } + +void wpa_trace_deinit(void) +{ +#ifdef WPA_TRACE_BFD + free(syms); + syms = NULL; +#endif /* WPA_TRACE_BFD */ +} + #endif /* WPA_TRACE */ diff --git a/contrib/wpa/src/utils/trace.h b/contrib/wpa/src/utils/trace.h index 43ed86c19978..d1636de07728 100644 --- a/contrib/wpa/src/utils/trace.h +++ b/contrib/wpa/src/utils/trace.h @@ -66,4 +66,6 @@ void wpa_trace_dump_funcname(const char *title, void *pc); #endif /* WPA_TRACE_BFD */ +void wpa_trace_deinit(void); + #endif /* TRACE_H */ diff --git a/contrib/wpa/src/utils/utils_module_tests.c b/contrib/wpa/src/utils/utils_module_tests.c index 41511b9999a6..abdb79c9879c 100644 --- a/contrib/wpa/src/utils/utils_module_tests.c +++ b/contrib/wpa/src/utils/utils_module_tests.c @@ -16,6 +16,7 @@ #include "utils/base64.h" #include "utils/ip_addr.h" #include "utils/eloop.h" +#include "utils/module_tests.h" struct printf_test_data { diff --git a/contrib/wpa/src/utils/wpa_debug.c b/contrib/wpa/src/utils/wpa_debug.c index 61c0d5ce68c7..f7acf6b9f698 100644 --- a/contrib/wpa/src/utils/wpa_debug.c +++ b/contrib/wpa/src/utils/wpa_debug.c @@ -148,7 +148,7 @@ int wpa_debug_open_linux_tracing(void) strtok_r(line, " ", &tmp2); tmp_path = strtok_r(NULL, " ", &tmp2); fstype = strtok_r(NULL, " ", &tmp2); - if (strcmp(fstype, "debugfs") == 0) { + if (fstype && strcmp(fstype, "debugfs") == 0) { path = tmp_path; break; } @@ -517,16 +517,18 @@ int wpa_debug_reopen_file(void) { #ifdef CONFIG_DEBUG_FILE int rv; - if (last_path) { - char *tmp = os_strdup(last_path); - wpa_debug_close_file(); - rv = wpa_debug_open_file(tmp); - os_free(tmp); - } else { - wpa_printf(MSG_ERROR, "Last-path was not set, cannot " - "re-open log file."); - rv = -1; - } + char *tmp; + + if (!last_path) + return 0; /* logfile not used */ + + tmp = os_strdup(last_path); + if (!tmp) + return -1; + + wpa_debug_close_file(); + rv = wpa_debug_open_file(tmp); + os_free(tmp); return rv; #else /* CONFIG_DEBUG_FILE */ return 0; diff --git a/contrib/wpa/src/utils/wpabuf.c b/contrib/wpa/src/utils/wpabuf.c index 11e7323619de..96cb25cc1764 100644 --- a/contrib/wpa/src/utils/wpabuf.c +++ b/contrib/wpa/src/utils/wpabuf.c @@ -310,3 +310,33 @@ void wpabuf_printf(struct wpabuf *buf, char *fmt, ...) wpabuf_overflow(buf, res); buf->used += res; } + + +/** + * wpabuf_parse_bin - Parse a null terminated string of binary data to a wpabuf + * @buf: Buffer with null terminated string (hexdump) of binary data + * Returns: wpabuf or %NULL on failure + * + * The string len must be a multiple of two and contain only hexadecimal digits. + */ +struct wpabuf * wpabuf_parse_bin(const char *buf) +{ + size_t len; + struct wpabuf *ret; + + len = os_strlen(buf); + if (len & 0x01) + return NULL; + len /= 2; + + ret = wpabuf_alloc(len); + if (ret == NULL) + return NULL; + + if (hexstr2bin(buf, wpabuf_put(ret, len), len)) { + wpabuf_free(ret); + return NULL; + } + + return ret; +} diff --git a/contrib/wpa/src/utils/wpabuf.h b/contrib/wpa/src/utils/wpabuf.h index c3ef1bae3667..01da41b324d6 100644 --- a/contrib/wpa/src/utils/wpabuf.h +++ b/contrib/wpa/src/utils/wpabuf.h @@ -37,6 +37,7 @@ void * wpabuf_put(struct wpabuf *buf, size_t len); struct wpabuf * wpabuf_concat(struct wpabuf *a, struct wpabuf *b); struct wpabuf * wpabuf_zeropad(struct wpabuf *buf, size_t len); void wpabuf_printf(struct wpabuf *buf, char *fmt, ...) PRINTF_FORMAT(2, 3); +struct wpabuf * wpabuf_parse_bin(const char *buf); /** @@ -81,7 +82,7 @@ static inline const void * wpabuf_head(const struct wpabuf *buf) static inline const u8 * wpabuf_head_u8(const struct wpabuf *buf) { - return wpabuf_head(buf); + return (const u8 *) wpabuf_head(buf); } /** @@ -96,42 +97,42 @@ static inline void * wpabuf_mhead(struct wpabuf *buf) static inline u8 * wpabuf_mhead_u8(struct wpabuf *buf) { - return wpabuf_mhead(buf); + return (u8 *) wpabuf_mhead(buf); } static inline void wpabuf_put_u8(struct wpabuf *buf, u8 data) { - u8 *pos = wpabuf_put(buf, 1); + u8 *pos = (u8 *) wpabuf_put(buf, 1); *pos = data; } static inline void wpabuf_put_le16(struct wpabuf *buf, u16 data) { - u8 *pos = wpabuf_put(buf, 2); + u8 *pos = (u8 *) wpabuf_put(buf, 2); WPA_PUT_LE16(pos, data); } static inline void wpabuf_put_le32(struct wpabuf *buf, u32 data) { - u8 *pos = wpabuf_put(buf, 4); + u8 *pos = (u8 *) wpabuf_put(buf, 4); WPA_PUT_LE32(pos, data); } static inline void wpabuf_put_be16(struct wpabuf *buf, u16 data) { - u8 *pos = wpabuf_put(buf, 2); + u8 *pos = (u8 *) wpabuf_put(buf, 2); WPA_PUT_BE16(pos, data); } static inline void wpabuf_put_be24(struct wpabuf *buf, u32 data) { - u8 *pos = wpabuf_put(buf, 3); + u8 *pos = (u8 *) wpabuf_put(buf, 3); WPA_PUT_BE24(pos, data); } static inline void wpabuf_put_be32(struct wpabuf *buf, u32 data) { - u8 *pos = wpabuf_put(buf, 4); + u8 *pos = (u8 *) wpabuf_put(buf, 4); WPA_PUT_BE32(pos, data); } diff --git a/contrib/wpa/src/utils/xml_libxml2.c b/contrib/wpa/src/utils/xml_libxml2.c index c92839461dad..7b6d2764b0ed 100644 --- a/contrib/wpa/src/utils/xml_libxml2.c +++ b/contrib/wpa/src/utils/xml_libxml2.c @@ -212,6 +212,8 @@ char * xml_node_to_str(struct xml_node_ctx *ctx, xml_node_t *node) xmlDocSetRootElement(doc, n); xmlDocDumpFormatMemory(doc, &buf, &bufsiz, 0); xmlFreeDoc(doc); + if (!buf) + return NULL; pos = (char *) buf; if (strncmp(pos, "'); diff --git a/contrib/wpa/src/wps/wps.c b/contrib/wpa/src/wps/wps.c index fbaf85aabab4..fade6b6905dc 100644 --- a/contrib/wpa/src/wps/wps.c +++ b/contrib/wpa/src/wps/wps.c @@ -19,6 +19,10 @@ int wps_version_number = 0x20; int wps_testing_dummy_cred = 0; int wps_corrupt_pkhash = 0; +int wps_force_auth_types_in_use = 0; +u16 wps_force_auth_types = 0; +int wps_force_encr_types_in_use = 0; +u16 wps_force_encr_types = 0; #endif /* CONFIG_WPS_TESTING */ @@ -170,7 +174,7 @@ void wps_deinit(struct wps_data *data) } else if (data->registrar) wps_registrar_unlock_pin(data->wps->registrar, data->uuid_e); - wpabuf_free(data->dh_privkey); + wpabuf_clear_free(data->dh_privkey); wpabuf_free(data->dh_pubkey_e); wpabuf_free(data->dh_pubkey_r); wpabuf_free(data->last_msg); diff --git a/contrib/wpa/src/wps/wps.h b/contrib/wpa/src/wps/wps.h index 2c91d1678c15..2505d2d9f246 100644 --- a/contrib/wpa/src/wps/wps.h +++ b/contrib/wpa/src/wps/wps.h @@ -1,6 +1,6 @@ /* * Wi-Fi Protected Setup - * Copyright (c) 2007-2013, Jouni Malinen + * Copyright (c) 2007-2016, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -663,6 +663,16 @@ struct wps_context { */ u16 encr_types; + /** + * encr_types_rsn - Enabled encryption types for RSN (WPS_ENCR_*) + */ + u16 encr_types_rsn; + + /** + * encr_types_wpa - Enabled encryption types for WPA (WPS_ENCR_*) + */ + u16 encr_types_wpa; + /** * auth_types - Authentication types (bit field of WPS_AUTH_*) */ @@ -827,7 +837,7 @@ int wps_build_credential_wrap(struct wpabuf *msg, unsigned int wps_pin_checksum(unsigned int pin); unsigned int wps_pin_valid(unsigned int pin); -unsigned int wps_generate_pin(void); +int wps_generate_pin(unsigned int *pin); int wps_pin_str_valid(const char *pin); void wps_free_pending_msgs(struct upnp_pending_message *msgs); diff --git a/contrib/wpa/src/wps/wps_attr_build.c b/contrib/wpa/src/wps/wps_attr_build.c index b689357a280a..770f5e90cbde 100644 --- a/contrib/wpa/src/wps/wps_attr_build.c +++ b/contrib/wpa/src/wps/wps_attr_build.c @@ -1,6 +1,6 @@ /* * Wi-Fi Protected Setup - attribute building - * Copyright (c) 2008, Jouni Malinen + * Copyright (c) 2008-2016, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -20,10 +20,10 @@ int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg) { - struct wpabuf *pubkey; + struct wpabuf *pubkey = NULL; wpa_printf(MSG_DEBUG, "WPS: * Public Key"); - wpabuf_free(wps->dh_privkey); + wpabuf_clear_free(wps->dh_privkey); wps->dh_privkey = NULL; if (wps->dev_pw_id != DEV_PW_DEFAULT && wps->wps->dh_privkey && wps->wps->dh_ctx) { @@ -298,7 +298,16 @@ int wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg) auth_types &= ~WPS_AUTH_WPA; auth_types &= ~WPS_AUTH_WPA2; auth_types &= ~WPS_AUTH_SHARED; - wpa_printf(MSG_DEBUG, "WPS: * Authentication Type Flags"); +#ifdef CONFIG_WPS_TESTING + if (wps_force_auth_types_in_use) { + wpa_printf(MSG_DEBUG, + "WPS: Testing - replace auth type 0x%x with 0x%x", + auth_types, wps_force_auth_types); + auth_types = wps_force_auth_types; + } +#endif /* CONFIG_WPS_TESTING */ + wpa_printf(MSG_DEBUG, "WPS: * Authentication Type Flags (0x%x)", + auth_types); wpabuf_put_be16(msg, ATTR_AUTH_TYPE_FLAGS); wpabuf_put_be16(msg, 2); wpabuf_put_be16(msg, auth_types); @@ -310,7 +319,16 @@ int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg) { u16 encr_types = WPS_ENCR_TYPES; encr_types &= ~WPS_ENCR_WEP; - wpa_printf(MSG_DEBUG, "WPS: * Encryption Type Flags"); +#ifdef CONFIG_WPS_TESTING + if (wps_force_encr_types_in_use) { + wpa_printf(MSG_DEBUG, + "WPS: Testing - replace encr type 0x%x with 0x%x", + encr_types, wps_force_encr_types); + encr_types = wps_force_encr_types; + } +#endif /* CONFIG_WPS_TESTING */ + wpa_printf(MSG_DEBUG, "WPS: * Encryption Type Flags (0x%x)", + encr_types); wpabuf_put_be16(msg, ATTR_ENCR_TYPE_FLAGS); wpabuf_put_be16(msg, 2); wpabuf_put_be16(msg, encr_types); @@ -395,7 +413,8 @@ int wps_build_oob_dev_pw(struct wpabuf *msg, u16 dev_pw_id, dev_pw_id); addr[0] = wpabuf_head(pubkey); hash_len = wpabuf_len(pubkey); - sha256_vector(1, addr, &hash_len, pubkey_hash); + if (sha256_vector(1, addr, &hash_len, pubkey_hash) < 0) + return -1; #ifdef CONFIG_WPS_TESTING if (wps_corrupt_pkhash) { wpa_hexdump(MSG_DEBUG, "WPS: Real Public Key Hash", diff --git a/contrib/wpa/src/wps/wps_attr_parse.c b/contrib/wpa/src/wps/wps_attr_parse.c index 11a967ba0ef1..756d57e876c5 100644 --- a/contrib/wpa/src/wps/wps_attr_parse.c +++ b/contrib/wpa/src/wps/wps_attr_parse.c @@ -83,10 +83,10 @@ static int wps_parse_vendor_ext_wfa(struct wps_parse_attr *attr, const u8 *pos, const u8 *end = pos + len; u8 id, elen; - while (pos + 2 <= end) { + while (end - pos >= 2) { id = *pos++; elen = *pos++; - if (pos + elen > end) + if (elen > end - pos) break; if (wps_set_vendor_ext_wfa_subelem(attr, id, elen, pos) < 0) return -1; diff --git a/contrib/wpa/src/wps/wps_attr_process.c b/contrib/wpa/src/wps/wps_attr_process.c index eadb22fe2e78..e8c4579309ab 100644 --- a/contrib/wpa/src/wps/wps_attr_process.c +++ b/contrib/wpa/src/wps/wps_attr_process.c @@ -229,6 +229,16 @@ static int wps_workaround_cred_key(struct wps_credential *cred) cred->key_len--; #endif /* CONFIG_WPS_STRICT */ } + + + if (cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK) && + (cred->key_len < 8 || has_ctrl_char(cred->key, cred->key_len))) { + wpa_printf(MSG_INFO, "WPS: Reject credential with invalid WPA/WPA2-Personal passphrase"); + wpa_hexdump_ascii_key(MSG_INFO, "WPS: Network Key", + cred->key, cred->key_len); + return -1; + } + return 0; } diff --git a/contrib/wpa/src/wps/wps_common.c b/contrib/wpa/src/wps/wps_common.c index 88f85fe83f05..2e3472177de9 100644 --- a/contrib/wpa/src/wps/wps_common.c +++ b/contrib/wpa/src/wps/wps_common.c @@ -90,7 +90,7 @@ int wps_derive_keys(struct wps_data *wps) } /* Own DH private key is not needed anymore */ - wpabuf_free(wps->dh_privkey); + wpabuf_clear_free(wps->dh_privkey); wps->dh_privkey = NULL; wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH shared key", dh_shared); @@ -100,7 +100,7 @@ int wps_derive_keys(struct wps_data *wps) len[0] = wpabuf_len(dh_shared); sha256_vector(1, addr, len, dhkey); wpa_hexdump_key(MSG_DEBUG, "WPS: DHKey", dhkey, sizeof(dhkey)); - wpabuf_free(dh_shared); + wpabuf_clear_free(dh_shared); /* KDK = HMAC-SHA-256_DHKey(N1 || EnrolleeMAC || N2) */ addr[0] = wps->nonce_e; @@ -129,23 +129,26 @@ int wps_derive_keys(struct wps_data *wps) } -void wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd, - size_t dev_passwd_len) +int wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd, + size_t dev_passwd_len) { u8 hash[SHA256_MAC_LEN]; - hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, dev_passwd, - (dev_passwd_len + 1) / 2, hash); + if (hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, dev_passwd, + (dev_passwd_len + 1) / 2, hash) < 0) + return -1; os_memcpy(wps->psk1, hash, WPS_PSK_LEN); - hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, - dev_passwd + (dev_passwd_len + 1) / 2, - dev_passwd_len / 2, hash); + if (hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, + dev_passwd + (dev_passwd_len + 1) / 2, + dev_passwd_len / 2, hash) < 0) + return -1; os_memcpy(wps->psk2, hash, WPS_PSK_LEN); wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: Device Password", dev_passwd, dev_passwd_len); wpa_hexdump_key(MSG_DEBUG, "WPS: PSK1", wps->psk1, WPS_PSK_LEN); wpa_hexdump_key(MSG_DEBUG, "WPS: PSK2", wps->psk2, WPS_PSK_LEN); + return 0; } @@ -173,7 +176,7 @@ struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr, wpabuf_put_data(decrypted, encr + block_size, encr_len - block_size); if (aes_128_cbc_decrypt(wps->keywrapkey, encr, wpabuf_mhead(decrypted), wpabuf_len(decrypted))) { - wpabuf_free(decrypted); + wpabuf_clear_free(decrypted); return NULL; } @@ -184,14 +187,14 @@ struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr, pad = *pos; if (pad > wpabuf_len(decrypted)) { wpa_printf(MSG_DEBUG, "WPS: Invalid PKCS#5 v2.0 pad value"); - wpabuf_free(decrypted); + wpabuf_clear_free(decrypted); return NULL; } for (i = 0; i < pad; i++) { if (*pos-- != pad) { wpa_printf(MSG_DEBUG, "WPS: Invalid PKCS#5 v2.0 pad " "string"); - wpabuf_free(decrypted); + wpabuf_clear_free(decrypted); return NULL; } } @@ -235,20 +238,18 @@ unsigned int wps_pin_valid(unsigned int pin) * wps_generate_pin - Generate a random PIN * Returns: Eight digit PIN (i.e., including the checksum digit) */ -unsigned int wps_generate_pin(void) +int wps_generate_pin(unsigned int *pin) { unsigned int val; /* Generate seven random digits for the PIN */ - if (random_get_bytes((unsigned char *) &val, sizeof(val)) < 0) { - struct os_time now; - os_get_time(&now); - val = os_random() ^ now.sec ^ now.usec; - } + if (random_get_bytes((unsigned char *) &val, sizeof(val)) < 0) + return -1; val %= 10000000; /* Append checksum digit */ - return val * 10 + wps_pin_checksum(val); + *pin = val * 10 + wps_pin_checksum(val); + return 0; } @@ -375,7 +376,7 @@ struct wpabuf * wps_get_oob_cred(struct wps_context *wps, int rf_band, wps_build_mac_addr(plain, wps->dev.mac_addr) || wps_build_wfa_ext(plain, 0, NULL, 0)) { os_free(data.new_psk); - wpabuf_free(plain); + wpabuf_clear_free(plain); return NULL; } @@ -423,7 +424,7 @@ struct wpabuf * wps_build_nfc_pw_token(u16 dev_pw_id, wps_build_wfa_ext(data, 0, NULL, 0)) { wpa_printf(MSG_ERROR, "WPS: Failed to build NFC password " "token"); - wpabuf_free(data); + wpabuf_clear_free(data); return NULL; } @@ -660,7 +661,7 @@ int wps_nfc_gen_dh(struct wpabuf **pubkey, struct wpabuf **privkey) wpabuf_free(*pubkey); *pubkey = pub; - wpabuf_free(*privkey); + wpabuf_clear_free(*privkey); *privkey = priv; return 0; @@ -691,7 +692,7 @@ struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey, } *id = 0x10 + val % 0xfff0; - wpabuf_free(*dev_pw); + wpabuf_clear_free(*dev_pw); *dev_pw = pw; return wps_nfc_token_build(ndef, *id, *pubkey, *dev_pw); diff --git a/contrib/wpa/src/wps/wps_defs.h b/contrib/wpa/src/wps/wps_defs.h index a23b979d2e3c..301864da433d 100644 --- a/contrib/wpa/src/wps/wps_defs.h +++ b/contrib/wpa/src/wps/wps_defs.h @@ -14,6 +14,10 @@ extern int wps_version_number; extern int wps_testing_dummy_cred; extern int wps_corrupt_pkhash; +extern int wps_force_auth_types_in_use; +extern u16 wps_force_auth_types; +extern int wps_force_encr_types_in_use; +extern u16 wps_force_encr_types; #define WPS_VERSION wps_version_number #else /* CONFIG_WPS_TESTING */ diff --git a/contrib/wpa/src/wps/wps_enrollee.c b/contrib/wpa/src/wps/wps_enrollee.c index 9321b721abd7..417507740d7a 100644 --- a/contrib/wpa/src/wps/wps_enrollee.c +++ b/contrib/wpa/src/wps/wps_enrollee.c @@ -173,7 +173,8 @@ static struct wpabuf * wps_build_m3(struct wps_data *wps) wpa_printf(MSG_DEBUG, "WPS: No Device Password available"); return NULL; } - wps_derive_psk(wps, wps->dev_password, wps->dev_password_len); + if (wps_derive_psk(wps, wps->dev_password, wps->dev_password_len) < 0) + return NULL; if (wps->wps->ap && random_pool_ready() != 1) { wpa_printf(MSG_INFO, @@ -224,11 +225,11 @@ static struct wpabuf * wps_build_m5(struct wps_data *wps) wps_build_encr_settings(wps, msg, plain) || wps_build_wfa_ext(msg, 0, NULL, 0) || wps_build_authenticator(wps, msg)) { - wpabuf_free(plain); + wpabuf_clear_free(plain); wpabuf_free(msg); return NULL; } - wpabuf_free(plain); + wpabuf_clear_free(plain); wps->state = RECV_M6; return msg; @@ -394,11 +395,11 @@ static struct wpabuf * wps_build_m7(struct wps_data *wps) wps_build_encr_settings(wps, msg, plain) || wps_build_wfa_ext(msg, 0, NULL, 0) || wps_build_authenticator(wps, msg)) { - wpabuf_free(plain); + wpabuf_clear_free(plain); wpabuf_free(msg); return NULL; } - wpabuf_free(plain); + wpabuf_clear_free(plain); if (wps->wps->ap && wps->wps->registrar) { /* @@ -1007,11 +1008,11 @@ static enum wps_process_res wps_process_m2(struct wps_data *wps, eattr.key_wrap_auth) || wps_process_creds(wps, eattr.cred, eattr.cred_len, eattr.num_cred, attr->version2 != NULL)) { - wpabuf_free(decrypted); + wpabuf_clear_free(decrypted); wps->state = SEND_WSC_NACK; return WPS_CONTINUE; } - wpabuf_free(decrypted); + wpabuf_clear_free(decrypted); wps->state = WPS_MSG_DONE; return WPS_CONTINUE; @@ -1112,7 +1113,7 @@ static enum wps_process_res wps_process_m4(struct wps_data *wps, } if (wps_validate_m4_encr(decrypted, attr->version2 != NULL) < 0) { - wpabuf_free(decrypted); + wpabuf_clear_free(decrypted); wps->state = SEND_WSC_NACK; return WPS_CONTINUE; } @@ -1122,11 +1123,11 @@ static enum wps_process_res wps_process_m4(struct wps_data *wps, if (wps_parse_msg(decrypted, &eattr) < 0 || wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) || wps_process_r_snonce1(wps, eattr.r_snonce1)) { - wpabuf_free(decrypted); + wpabuf_clear_free(decrypted); wps->state = SEND_WSC_NACK; return WPS_CONTINUE; } - wpabuf_free(decrypted); + wpabuf_clear_free(decrypted); wps->state = SEND_M5; return WPS_CONTINUE; @@ -1165,7 +1166,7 @@ static enum wps_process_res wps_process_m6(struct wps_data *wps, } if (wps_validate_m6_encr(decrypted, attr->version2 != NULL) < 0) { - wpabuf_free(decrypted); + wpabuf_clear_free(decrypted); wps->state = SEND_WSC_NACK; return WPS_CONTINUE; } @@ -1175,11 +1176,11 @@ static enum wps_process_res wps_process_m6(struct wps_data *wps, if (wps_parse_msg(decrypted, &eattr) < 0 || wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) || wps_process_r_snonce2(wps, eattr.r_snonce2)) { - wpabuf_free(decrypted); + wpabuf_clear_free(decrypted); wps->state = SEND_WSC_NACK; return WPS_CONTINUE; } - wpabuf_free(decrypted); + wpabuf_clear_free(decrypted); if (wps->wps->ap) wps->wps->event_cb(wps->wps->cb_ctx, WPS_EV_AP_PIN_SUCCESS, @@ -1236,7 +1237,7 @@ static enum wps_process_res wps_process_m8(struct wps_data *wps, if (wps_validate_m8_encr(decrypted, wps->wps->ap, attr->version2 != NULL) < 0) { - wpabuf_free(decrypted); + wpabuf_clear_free(decrypted); wps->state = SEND_WSC_NACK; return WPS_CONTINUE; } @@ -1249,11 +1250,11 @@ static enum wps_process_res wps_process_m8(struct wps_data *wps, eattr.num_cred, attr->version2 != NULL) || wps_process_ap_settings_e(wps, &eattr, decrypted, attr->version2 != NULL)) { - wpabuf_free(decrypted); + wpabuf_clear_free(decrypted); wps->state = SEND_WSC_NACK; return WPS_CONTINUE; } - wpabuf_free(decrypted); + wpabuf_clear_free(decrypted); wps->state = WPS_MSG_DONE; return WPS_CONTINUE; diff --git a/contrib/wpa/src/wps/wps_i.h b/contrib/wpa/src/wps/wps_i.h index f7154f8734bb..fe0c60bd120b 100644 --- a/contrib/wpa/src/wps/wps_i.h +++ b/contrib/wpa/src/wps/wps_i.h @@ -132,8 +132,8 @@ struct wps_data { void wps_kdf(const u8 *key, const u8 *label_prefix, size_t label_prefix_len, const char *label, u8 *res, size_t res_len); int wps_derive_keys(struct wps_data *wps); -void wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd, - size_t dev_passwd_len); +int wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd, + size_t dev_passwd_len); struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr, size_t encr_len); void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg, diff --git a/contrib/wpa/src/wps/wps_module_tests.c b/contrib/wpa/src/wps/wps_module_tests.c index 350630768be4..23bed4b36e01 100644 --- a/contrib/wpa/src/wps/wps_module_tests.c +++ b/contrib/wpa/src/wps/wps_module_tests.c @@ -9,6 +9,7 @@ #include "utils/includes.h" #include "utils/common.h" +#include "utils/module_tests.h" #include "wps_attr_parse.h" struct wps_attr_parse_test { @@ -17,7 +18,7 @@ struct wps_attr_parse_test { int extra; }; -const struct wps_attr_parse_test wps_attr_parse_test_cases[] = { +static const struct wps_attr_parse_test wps_attr_parse_test_cases[] = { /* Empty message */ { "", 0, 0 }, /* Truncated attribute header */ diff --git a/contrib/wpa/src/wps/wps_registrar.c b/contrib/wpa/src/wps/wps_registrar.c index 4ca3a42d4c73..fac8bd837f2f 100644 --- a/contrib/wpa/src/wps/wps_registrar.c +++ b/contrib/wpa/src/wps/wps_registrar.c @@ -1,6 +1,6 @@ /* * Wi-Fi Protected Setup - Registrar - * Copyright (c) 2008-2013, Jouni Malinen + * Copyright (c) 2008-2016, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -703,7 +703,7 @@ void wps_registrar_deinit(struct wps_registrar *reg) eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL); eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); wps_registrar_flush(reg); - wpabuf_free(reg->extra_cred); + wpabuf_clear_free(reg->extra_cred); os_free(reg); } @@ -1577,13 +1577,13 @@ int wps_build_credential_wrap(struct wpabuf *msg, if (wbuf == NULL) return -1; if (wps_build_credential(wbuf, cred)) { - wpabuf_free(wbuf); + wpabuf_clear_free(wbuf); return -1; } wpabuf_put_be16(msg, ATTR_CRED); wpabuf_put_be16(msg, wpabuf_len(wbuf)); wpabuf_put_buf(msg, wbuf); - wpabuf_free(wbuf); + wpabuf_clear_free(wbuf); return 0; } @@ -1606,6 +1606,9 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg) wps->cred.ssid_len = wps->wps->ssid_len; /* Select the best authentication and encryption type */ + wpa_printf(MSG_DEBUG, + "WPS: Own auth types 0x%x - masked Enrollee auth types 0x%x", + wps->wps->auth_types, wps->auth_type); if (wps->auth_type & WPS_AUTH_WPA2PSK) wps->auth_type = WPS_AUTH_WPA2PSK; else if (wps->auth_type & WPS_AUTH_WPAPSK) @@ -1619,6 +1622,14 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg) } wps->cred.auth_type = wps->auth_type; + wpa_printf(MSG_DEBUG, + "WPS: Own encr types 0x%x (rsn: 0x%x, wpa: 0x%x) - masked Enrollee encr types 0x%x", + wps->wps->encr_types, wps->wps->encr_types_rsn, + wps->wps->encr_types_wpa, wps->encr_type); + if (wps->wps->ap && wps->auth_type == WPS_AUTH_WPA2PSK) + wps->encr_type &= wps->wps->encr_types_rsn; + else if (wps->wps->ap && wps->auth_type == WPS_AUTH_WPAPSK) + wps->encr_type &= wps->wps->encr_types_wpa; if (wps->auth_type == WPS_AUTH_WPA2PSK || wps->auth_type == WPS_AUTH_WPAPSK) { if (wps->encr_type & WPS_ENCR_AES) @@ -1740,14 +1751,14 @@ use_provided: return -1; if (wps_build_credential(cred, &wps->cred)) { - wpabuf_free(cred); + wpabuf_clear_free(cred); return -1; } wpabuf_put_be16(msg, ATTR_CRED); wpabuf_put_be16(msg, wpabuf_len(cred)); wpabuf_put_buf(msg, cred); - wpabuf_free(cred); + wpabuf_clear_free(cred); skip_cred_build: if (wps->wps->registrar->extra_cred) { @@ -1785,7 +1796,7 @@ static struct wpabuf * wps_build_ap_cred(struct wps_data *wps) } if (wps_build_ap_settings(wps, plain)) { - wpabuf_free(plain); + wpabuf_clear_free(plain); wpabuf_free(msg); return NULL; } @@ -1793,7 +1804,7 @@ static struct wpabuf * wps_build_ap_cred(struct wps_data *wps) wpabuf_put_be16(msg, ATTR_CRED); wpabuf_put_be16(msg, wpabuf_len(plain)); wpabuf_put_buf(msg, plain); - wpabuf_free(plain); + wpabuf_clear_free(plain); return msg; } @@ -1853,10 +1864,10 @@ static struct wpabuf * wps_build_m2(struct wps_data *wps) wps_build_key_wrap_auth(wps, plain) || wps_build_encr_settings(wps, msg, plain)) { wpabuf_free(msg); - wpabuf_free(plain); + wpabuf_clear_free(plain); return NULL; } - wpabuf_free(plain); + wpabuf_clear_free(plain); config_in_m2 = 1; } #endif /* CONFIG_WPS_NFC */ @@ -1917,7 +1928,8 @@ static struct wpabuf * wps_build_m4(struct wps_data *wps) wpa_printf(MSG_DEBUG, "WPS: Building Message M4"); - wps_derive_psk(wps, wps->dev_password, wps->dev_password_len); + if (wps_derive_psk(wps, wps->dev_password, wps->dev_password_len) < 0) + return NULL; plain = wpabuf_alloc(200); if (plain == NULL) @@ -1938,11 +1950,11 @@ static struct wpabuf * wps_build_m4(struct wps_data *wps) wps_build_encr_settings(wps, msg, plain) || wps_build_wfa_ext(msg, 0, NULL, 0) || wps_build_authenticator(wps, msg)) { - wpabuf_free(plain); + wpabuf_clear_free(plain); wpabuf_free(msg); return NULL; } - wpabuf_free(plain); + wpabuf_clear_free(plain); wps->state = RECV_M5; return msg; @@ -1973,11 +1985,11 @@ static struct wpabuf * wps_build_m6(struct wps_data *wps) wps_build_encr_settings(wps, msg, plain) || wps_build_wfa_ext(msg, 0, NULL, 0) || wps_build_authenticator(wps, msg)) { - wpabuf_free(plain); + wpabuf_clear_free(plain); wpabuf_free(msg); return NULL; } - wpabuf_free(plain); + wpabuf_clear_free(plain); wps->wps_pin_revealed = 1; wps->state = RECV_M7; @@ -2010,11 +2022,11 @@ static struct wpabuf * wps_build_m8(struct wps_data *wps) wps_build_encr_settings(wps, msg, plain) || wps_build_wfa_ext(msg, 0, NULL, 0) || wps_build_authenticator(wps, msg)) { - wpabuf_free(plain); - wpabuf_free(msg); + wpabuf_clear_free(plain); + wpabuf_clear_free(msg); return NULL; } - wpabuf_free(plain); + wpabuf_clear_free(plain); wps->state = RECV_DONE; return msg; @@ -2343,6 +2355,23 @@ static int wps_process_auth_type_flags(struct wps_data *wps, const u8 *auth) wpa_printf(MSG_DEBUG, "WPS: Enrollee Authentication Type flags 0x%x", auth_types); +#ifdef WPS_WORKAROUNDS + /* + * Some deployed implementations seem to advertise incorrect information + * in this attribute. A value of 0x1b (WPA2 + WPA + WPAPSK + OPEN, but + * no WPA2PSK) has been reported to be used. Add WPA2PSK to the list to + * avoid issues with building Credentials that do not use the strongest + * actually supported authentication option (that device does support + * WPA2PSK even when it does not claim it here). + */ + if ((auth_types & + (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) == + (WPS_AUTH_WPA2 | WPS_AUTH_WPAPSK)) { + wpa_printf(MSG_DEBUG, + "WPS: Workaround - assume Enrollee supports WPA2PSK based on claimed WPA2 support"); + auth_types |= WPS_AUTH_WPA2PSK; + } +#endif /* WPS_WORKAROUNDS */ wps->auth_type = wps->wps->auth_types & auth_types; if (wps->auth_type == 0) { wpa_printf(MSG_DEBUG, "WPS: No match in supported " @@ -2757,7 +2786,7 @@ static enum wps_process_res wps_process_m5(struct wps_data *wps, } if (wps_validate_m5_encr(decrypted, attr->version2 != NULL) < 0) { - wpabuf_free(decrypted); + wpabuf_clear_free(decrypted); wps->state = SEND_WSC_NACK; return WPS_CONTINUE; } @@ -2767,11 +2796,11 @@ static enum wps_process_res wps_process_m5(struct wps_data *wps, if (wps_parse_msg(decrypted, &eattr) < 0 || wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) || wps_process_e_snonce1(wps, eattr.e_snonce1)) { - wpabuf_free(decrypted); + wpabuf_clear_free(decrypted); wps->state = SEND_WSC_NACK; return WPS_CONTINUE; } - wpabuf_free(decrypted); + wpabuf_clear_free(decrypted); wps->state = SEND_M6; return WPS_CONTINUE; @@ -2909,7 +2938,7 @@ static enum wps_process_res wps_process_m7(struct wps_data *wps, if (wps_validate_m7_encr(decrypted, wps->wps->ap || wps->er, attr->version2 != NULL) < 0) { - wpabuf_free(decrypted); + wpabuf_clear_free(decrypted); wps->state = SEND_WSC_NACK; return WPS_CONTINUE; } @@ -2920,12 +2949,12 @@ static enum wps_process_res wps_process_m7(struct wps_data *wps, wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) || wps_process_e_snonce2(wps, eattr.e_snonce2) || wps_process_ap_settings_r(wps, &eattr)) { - wpabuf_free(decrypted); + wpabuf_clear_free(decrypted); wps->state = SEND_WSC_NACK; return WPS_CONTINUE; } - wpabuf_free(decrypted); + wpabuf_clear_free(decrypted); wps->state = SEND_M8; return WPS_CONTINUE; diff --git a/contrib/wpa/src/wps/wps_upnp.c b/contrib/wpa/src/wps/wps_upnp.c index 44318e094252..0c458c6adef9 100644 --- a/contrib/wpa/src/wps/wps_upnp.c +++ b/contrib/wpa/src/wps/wps_upnp.c @@ -1082,6 +1082,7 @@ upnp_wps_get_iface(struct upnp_wps_device_sm *sm, void *priv) void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm, void *priv) { struct upnp_wps_device_interface *iface; + struct upnp_wps_peer *peer; if (!sm) return; @@ -1102,8 +1103,13 @@ void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm, void *priv) iface->wps->registrar); dl_list_del(&iface->list); - if (iface->peer.wps) - wps_deinit(iface->peer.wps); + while ((peer = dl_list_first(&iface->peers, struct upnp_wps_peer, + list))) { + if (peer->wps) + wps_deinit(peer->wps); + dl_list_del(&peer->list); + os_free(peer); + } os_free(iface->ctx->ap_pin); os_free(iface->ctx); os_free(iface); @@ -1141,6 +1147,7 @@ upnp_wps_device_init(struct upnp_wps_device_ctx *ctx, struct wps_context *wps, } wpa_printf(MSG_DEBUG, "WPS UPnP: Init interface instance %p", iface); + dl_list_init(&iface->peers); iface->ctx = ctx; iface->wps = wps; iface->priv = priv; diff --git a/contrib/wpa/src/wps/wps_upnp.h b/contrib/wpa/src/wps/wps_upnp.h index 87b7ab14160b..b6f6df5ec519 100644 --- a/contrib/wpa/src/wps/wps_upnp.h +++ b/contrib/wpa/src/wps/wps_upnp.h @@ -11,11 +11,14 @@ #ifndef WPS_UPNP_H #define WPS_UPNP_H +#include "utils/list.h" + struct upnp_wps_device_sm; struct wps_context; struct wps_data; struct upnp_wps_peer { + struct dl_list list; struct wps_data *wps; }; diff --git a/contrib/wpa/src/wps/wps_upnp_i.h b/contrib/wpa/src/wps/wps_upnp_i.h index f289fe685ac7..6a7c627253e3 100644 --- a/contrib/wpa/src/wps/wps_upnp_i.h +++ b/contrib/wpa/src/wps/wps_upnp_i.h @@ -109,8 +109,7 @@ struct upnp_wps_device_interface { struct wps_context *wps; void *priv; - /* FIX: maintain separate structures for each UPnP peer */ - struct upnp_wps_peer peer; + struct dl_list peers; /* active UPnP peer sessions */ }; /* diff --git a/contrib/wpa/src/wps/wps_upnp_ssdp.c b/contrib/wpa/src/wps/wps_upnp_ssdp.c index 968fc03f92e7..a685ce4c3c0f 100644 --- a/contrib/wpa/src/wps/wps_upnp_ssdp.c +++ b/contrib/wpa/src/wps/wps_upnp_ssdp.c @@ -100,12 +100,6 @@ static int line_length(const char *l) } -static int str_starts(const char *str, const char *start) -{ - return os_strncmp(str, start, os_strlen(start)) == 0; -} - - /*************************************************************************** * Advertisements. * These are multicast to the world to tell them we are here. diff --git a/contrib/wpa/src/wps/wps_upnp_web.c b/contrib/wpa/src/wps/wps_upnp_web.c index d5b0b5b26e9d..7548e8432a68 100644 --- a/contrib/wpa/src/wps/wps_upnp_web.c +++ b/contrib/wpa/src/wps/wps_upnp_web.c @@ -300,7 +300,8 @@ static void http_put_empty(struct wpabuf *buf, enum http_reply_code code) * would appear to be required (given that we will be closing it!). */ static void web_connection_parse_get(struct upnp_wps_device_sm *sm, - struct http_request *hreq, char *filename) + struct http_request *hreq, + const char *filename) { struct wpabuf *buf; /* output buffer, allocated */ char *put_length_here; @@ -409,6 +410,15 @@ send_buf: } +static void wps_upnp_peer_del(struct upnp_wps_peer *peer) +{ + dl_list_del(&peer->list); + if (peer->wps) + wps_deinit(peer->wps); + os_free(peer); +} + + static enum http_reply_code web_process_get_device_info(struct upnp_wps_device_sm *sm, struct wpabuf **reply, const char **replyname) @@ -426,7 +436,9 @@ web_process_get_device_info(struct upnp_wps_device_sm *sm, if (!iface || iface->ctx->ap_pin == NULL) return HTTP_INTERNAL_SERVER_ERROR; - peer = &iface->peer; + peer = os_zalloc(sizeof(*peer)); + if (!peer) + return HTTP_INTERNAL_SERVER_ERROR; /* * Request for DeviceInfo, i.e., M1 TLVs. This is a start of WPS @@ -436,9 +448,6 @@ web_process_get_device_info(struct upnp_wps_device_sm *sm, * registration. */ - if (peer->wps) - wps_deinit(peer->wps); - os_memset(&cfg, 0, sizeof(cfg)); cfg.wps = iface->wps; cfg.pin = (u8 *) iface->ctx->ap_pin; @@ -455,8 +464,22 @@ web_process_get_device_info(struct upnp_wps_device_sm *sm, *reply = NULL; if (*reply == NULL) { wpa_printf(MSG_INFO, "WPS UPnP: Failed to get DeviceInfo"); + os_free(peer); return HTTP_INTERNAL_SERVER_ERROR; } + + if (dl_list_len(&iface->peers) > 3) { + struct upnp_wps_peer *old; + + old = dl_list_first(&iface->peers, struct upnp_wps_peer, list); + if (old) { + wpa_printf(MSG_DEBUG, "WPS UPnP: Drop oldest active session"); + wps_upnp_peer_del(old); + } + } + dl_list_add_tail(&iface->peers, &peer->list); + /* TODO: Could schedule a timeout to free the entry */ + *replyname = name; return HTTP_OK; } @@ -472,6 +495,8 @@ web_process_put_message(struct upnp_wps_device_sm *sm, char *data, enum wps_process_res res; enum wsc_op_code op_code; struct upnp_wps_device_interface *iface; + struct wps_parse_attr attr; + struct upnp_wps_peer *tmp, *peer; iface = dl_list_first(&sm->interfaces, struct upnp_wps_device_interface, list); @@ -487,11 +512,56 @@ web_process_put_message(struct upnp_wps_device_sm *sm, char *data, msg = xml_get_base64_item(data, "NewInMessage", &ret); if (msg == NULL) return ret; - res = wps_process_msg(iface->peer.wps, WSC_UPnP, msg); - if (res == WPS_FAILURE) + + if (wps_parse_msg(msg, &attr)) { + wpa_printf(MSG_DEBUG, + "WPS UPnP: Could not parse PutMessage - NewInMessage"); + wpabuf_free(msg); + return HTTP_BAD_REQUEST; + } + + /* Find a matching active peer session */ + peer = NULL; + dl_list_for_each(tmp, &iface->peers, struct upnp_wps_peer, list) { + if (!tmp->wps) + continue; + if (attr.enrollee_nonce && + os_memcmp(tmp->wps->nonce_e, attr.enrollee_nonce, + WPS_NONCE_LEN) != 0) + continue; /* Enrollee nonce mismatch */ + if (attr.msg_type && + *attr.msg_type != WPS_M2 && + *attr.msg_type != WPS_M2D && + attr.registrar_nonce && + os_memcmp(tmp->wps->nonce_r, attr.registrar_nonce, + WPS_NONCE_LEN) != 0) + continue; /* Registrar nonce mismatch */ + peer = tmp; + break; + } + if (!peer) { + /* + Try to use the first entry in case message could work with + * it. The actual handler function will reject this, if needed. + * This maintains older behavior where only a single peer entry + * was supported. + */ + peer = dl_list_first(&iface->peers, struct upnp_wps_peer, list); + } + if (!peer || !peer->wps) { + wpa_printf(MSG_DEBUG, "WPS UPnP: No active peer entry found"); + wpabuf_free(msg); + return HTTP_BAD_REQUEST; + } + + res = wps_process_msg(peer->wps, WSC_UPnP, msg); + if (res == WPS_FAILURE) { *reply = NULL; - else - *reply = wps_get_msg(iface->peer.wps, &op_code); + wpa_printf(MSG_DEBUG, "WPS UPnP: Drop active peer session"); + wps_upnp_peer_del(peer); + } else { + *reply = wps_get_msg(peer->wps, &op_code); + } wpabuf_free(msg); if (*reply == NULL) return HTTP_INTERNAL_SERVER_ERROR; diff --git a/contrib/wpa/wpa_supplicant/Android.mk b/contrib/wpa/wpa_supplicant/Android.mk new file mode 100644 index 000000000000..a8d6a7f944e9 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/Android.mk @@ -0,0 +1,1680 @@ +# +# Copyright (C) 2008 The Android Open Source Project +# +# This software may be distributed under the terms of the BSD license. +# See README for more details. +# + +LOCAL_PATH := $(call my-dir) +PKG_CONFIG ?= pkg-config + +ifneq ($(BOARD_WPA_SUPPLICANT_DRIVER),) + CONFIG_DRIVER_$(BOARD_WPA_SUPPLICANT_DRIVER) := y +endif + +include $(LOCAL_PATH)/android.config + +# To ignore possible wrong network configurations +L_CFLAGS = -DWPA_IGNORE_CONFIG_ERRORS + +L_CFLAGS += -DVERSION_STR_POSTFIX=\"-$(PLATFORM_VERSION)\" + +# Set Android log name +L_CFLAGS += -DANDROID_LOG_NAME=\"wpa_supplicant\" + +# Disable unused parameter warnings +L_CFLAGS += -Wno-unused-parameter + +# Set Android extended P2P functionality +L_CFLAGS += -DANDROID_P2P + +ifeq ($(BOARD_WPA_SUPPLICANT_PRIVATE_LIB),) +L_CFLAGS += -DANDROID_LIB_STUB +endif + +# Disable roaming in wpa_supplicant +ifdef CONFIG_NO_ROAMING +L_CFLAGS += -DCONFIG_NO_ROAMING +endif + +# Use Android specific directory for control interface sockets +L_CFLAGS += -DCONFIG_CTRL_IFACE_CLIENT_DIR=\"/data/misc/wifi/sockets\" +L_CFLAGS += -DCONFIG_CTRL_IFACE_DIR=\"/data/misc/wifi/sockets\" + +# Use Android specific directory for wpa_cli command completion history +L_CFLAGS += -DCONFIG_WPA_CLI_HISTORY_DIR=\"/data/misc/wifi\" + +# To force sizeof(enum) = 4 +ifeq ($(TARGET_ARCH),arm) +L_CFLAGS += -mabi=aapcs-linux +endif + +# C++ flags for binder interface +L_CPPFLAGS := -std=c++11 -Wall -Werror +# TODO: Remove these allowed warnings later. +L_CPPFLAGS += -Wno-unused-variable -Wno-unused-parameter +L_CPPFLAGS += -Wno-unused-private-field + +INCLUDES = $(LOCAL_PATH) +INCLUDES += $(LOCAL_PATH)/src +INCLUDES += $(LOCAL_PATH)/src/common +# INCLUDES += $(LOCAL_PATH)/src/crypto # To force proper includes +INCLUDES += $(LOCAL_PATH)/src/drivers +INCLUDES += $(LOCAL_PATH)/src/eap_common +INCLUDES += $(LOCAL_PATH)/src/eapol_supp +INCLUDES += $(LOCAL_PATH)/src/eap_peer +INCLUDES += $(LOCAL_PATH)/src/eap_server +INCLUDES += $(LOCAL_PATH)/src/hlr_auc_gw +INCLUDES += $(LOCAL_PATH)/src/l2_packet +INCLUDES += $(LOCAL_PATH)/src/radius +INCLUDES += $(LOCAL_PATH)/src/rsn_supp +INCLUDES += $(LOCAL_PATH)/src/tls +INCLUDES += $(LOCAL_PATH)/src/utils +INCLUDES += $(LOCAL_PATH)/src/wps +INCLUDES += system/security/keystore/include +ifdef CONFIG_DRIVER_NL80211 +ifneq ($(wildcard external/libnl),) +INCLUDES += external/libnl/include +else +INCLUDES += external/libnl-headers +endif +endif + +ifdef CONFIG_FIPS +CONFIG_NO_RANDOM_POOL= +CONFIG_OPENSSL_CMAC=y +endif + +OBJS = config.c +OBJS += notify.c +OBJS += bss.c +OBJS += eap_register.c +OBJS += src/utils/common.c +OBJS += src/utils/wpa_debug.c +OBJS += src/utils/wpabuf.c +OBJS += wmm_ac.c +OBJS_p = wpa_passphrase.c +OBJS_p += src/utils/common.c +OBJS_p += src/utils/wpa_debug.c +OBJS_p += src/utils/wpabuf.c +OBJS_c = wpa_cli.c src/common/wpa_ctrl.c +OBJS_c += src/utils/wpa_debug.c +OBJS_c += src/utils/common.c +OBJS_c += src/common/cli.c +OBJS_d = +OBJS_priv = + +ifndef CONFIG_OS +ifdef CONFIG_NATIVE_WINDOWS +CONFIG_OS=win32 +else +CONFIG_OS=unix +endif +endif + +ifeq ($(CONFIG_OS), internal) +L_CFLAGS += -DOS_NO_C_LIB_DEFINES +endif + +OBJS += src/utils/os_$(CONFIG_OS).c +OBJS_p += src/utils/os_$(CONFIG_OS).c +OBJS_c += src/utils/os_$(CONFIG_OS).c + +ifdef CONFIG_WPA_TRACE +L_CFLAGS += -DWPA_TRACE +OBJS += src/utils/trace.c +OBJS_p += src/utils/trace.c +OBJS_c += src/utils/trace.c +LDFLAGS += -rdynamic +L_CFLAGS += -funwind-tables +ifdef CONFIG_WPA_TRACE_BFD +L_CFLAGS += -DWPA_TRACE_BFD +LIBS += -lbfd +LIBS_p += -lbfd +LIBS_c += -lbfd +endif +endif + +ifndef CONFIG_ELOOP +CONFIG_ELOOP=eloop +endif +OBJS += src/utils/$(CONFIG_ELOOP).c +OBJS_c += src/utils/$(CONFIG_ELOOP).c + +ifdef CONFIG_ELOOP_POLL +L_CFLAGS += -DCONFIG_ELOOP_POLL +endif + +ifdef CONFIG_ELOOP_EPOLL +L_CFLAGS += -DCONFIG_ELOOP_EPOLL +endif + +ifdef CONFIG_EAPOL_TEST +L_CFLAGS += -Werror -DEAPOL_TEST +endif + +ifdef CONFIG_HT_OVERRIDES +L_CFLAGS += -DCONFIG_HT_OVERRIDES +endif + +ifdef CONFIG_VHT_OVERRIDES +L_CFLAGS += -DCONFIG_VHT_OVERRIDES +endif + +ifndef CONFIG_BACKEND +CONFIG_BACKEND=file +endif + +ifeq ($(CONFIG_BACKEND), file) +OBJS += config_file.c +ifndef CONFIG_NO_CONFIG_BLOBS +NEED_BASE64=y +endif +L_CFLAGS += -DCONFIG_BACKEND_FILE +endif + +ifeq ($(CONFIG_BACKEND), winreg) +OBJS += config_winreg.c +endif + +ifeq ($(CONFIG_BACKEND), none) +OBJS += config_none.c +endif + +ifdef CONFIG_NO_CONFIG_WRITE +L_CFLAGS += -DCONFIG_NO_CONFIG_WRITE +endif + +ifdef CONFIG_NO_CONFIG_BLOBS +L_CFLAGS += -DCONFIG_NO_CONFIG_BLOBS +endif + +ifdef CONFIG_NO_SCAN_PROCESSING +L_CFLAGS += -DCONFIG_NO_SCAN_PROCESSING +endif + +ifdef CONFIG_SUITEB +L_CFLAGS += -DCONFIG_SUITEB +NEED_SHA256=y +NEED_AES_OMAC1=y +endif + +ifdef CONFIG_SUITEB192 +L_CFLAGS += -DCONFIG_SUITEB192 +NEED_SHA384=y +endif + +ifdef CONFIG_IEEE80211W +L_CFLAGS += -DCONFIG_IEEE80211W +NEED_SHA256=y +NEED_AES_OMAC1=y +endif + +ifdef CONFIG_IEEE80211R +L_CFLAGS += -DCONFIG_IEEE80211R +OBJS += src/rsn_supp/wpa_ft.c +NEED_SHA256=y +NEED_AES_OMAC1=y +endif + +ifdef CONFIG_MESH +NEED_80211_COMMON=y +NEED_SHA256=y +NEED_AES_SIV=y +NEED_AES_OMAC1=y +NEED_AES_CTR=y +CONFIG_SAE=y +CONFIG_AP=y +L_CFLAGS += -DCONFIG_MESH +OBJS += mesh.c +OBJS += mesh_mpm.c +OBJS += mesh_rsn.c +endif + +ifdef CONFIG_SAE +L_CFLAGS += -DCONFIG_SAE +OBJS += src/common/sae.c +NEED_ECC=y +NEED_DH_GROUPS=y +endif + +ifdef CONFIG_WNM +L_CFLAGS += -DCONFIG_WNM +OBJS += wnm_sta.c +endif + +ifdef CONFIG_TDLS +L_CFLAGS += -DCONFIG_TDLS +OBJS += src/rsn_supp/tdls.c +NEED_SHA256=y +NEED_AES_OMAC1=y +endif + +ifdef CONFIG_TDLS_TESTING +L_CFLAGS += -DCONFIG_TDLS_TESTING +endif + +ifdef CONFIG_PEERKEY +L_CFLAGS += -DCONFIG_PEERKEY +endif + +ifndef CONFIG_NO_WPA +OBJS += src/rsn_supp/wpa.c +OBJS += src/rsn_supp/preauth.c +OBJS += src/rsn_supp/pmksa_cache.c +OBJS += src/rsn_supp/peerkey.c +OBJS += src/rsn_supp/wpa_ie.c +OBJS += src/common/wpa_common.c +NEED_AES=y +NEED_SHA1=y +NEED_MD5=y +NEED_RC4=y +else +L_CFLAGS += -DCONFIG_NO_WPA +endif + +ifdef CONFIG_IBSS_RSN +NEED_RSN_AUTHENTICATOR=y +L_CFLAGS += -DCONFIG_IBSS_RSN +L_CFLAGS += -DCONFIG_NO_VLAN +OBJS += ibss_rsn.c +endif + +ifdef CONFIG_P2P +OBJS += p2p_supplicant.c +OBJS += p2p_supplicant_sd.c +OBJS += src/p2p/p2p.c +OBJS += src/p2p/p2p_utils.c +OBJS += src/p2p/p2p_parse.c +OBJS += src/p2p/p2p_build.c +OBJS += src/p2p/p2p_go_neg.c +OBJS += src/p2p/p2p_sd.c +OBJS += src/p2p/p2p_pd.c +OBJS += src/p2p/p2p_invitation.c +OBJS += src/p2p/p2p_dev_disc.c +OBJS += src/p2p/p2p_group.c +OBJS += src/ap/p2p_hostapd.c +OBJS += src/utils/bitfield.c +L_CFLAGS += -DCONFIG_P2P +NEED_GAS=y +NEED_OFFCHANNEL=y +CONFIG_WPS=y +CONFIG_AP=y +ifdef CONFIG_P2P_STRICT +L_CFLAGS += -DCONFIG_P2P_STRICT +endif +endif + +ifdef CONFIG_WIFI_DISPLAY +L_CFLAGS += -DCONFIG_WIFI_DISPLAY +OBJS += wifi_display.c +endif + +ifdef CONFIG_HS20 +OBJS += hs20_supplicant.c +L_CFLAGS += -DCONFIG_HS20 +CONFIG_INTERWORKING=y +NEED_AES_OMAC1=y +endif + +ifdef CONFIG_INTERWORKING +OBJS += interworking.c +L_CFLAGS += -DCONFIG_INTERWORKING +NEED_GAS=y +endif + +ifdef CONFIG_FST +L_CFLAGS += -DCONFIG_FST +OBJS += src/fst/fst.c +OBJS += src/fst/fst_session.c +OBJS += src/fst/fst_iface.c +OBJS += src/fst/fst_group.c +OBJS += src/fst/fst_ctrl_aux.c +ifdef CONFIG_FST_TEST +L_CFLAGS += -DCONFIG_FST_TEST +endif +ifdef CONFIG_CTRL_IFACE +OBJS += src/fst/fst_ctrl_iface.c +endif +endif + + +include $(LOCAL_PATH)/src/drivers/drivers.mk + +ifdef CONFIG_AP +OBJS_d += $(DRV_BOTH_OBJS) +L_CFLAGS += $(DRV_BOTH_CFLAGS) +LDFLAGS += $(DRV_BOTH_LDFLAGS) +LIBS += $(DRV_BOTH_LIBS) +else +NEED_AP_MLME= +OBJS_d += $(DRV_WPA_OBJS) +L_CFLAGS += $(DRV_WPA_CFLAGS) +LDFLAGS += $(DRV_WPA_LDFLAGS) +LIBS += $(DRV_WPA_LIBS) +endif + +ifndef CONFIG_L2_PACKET +CONFIG_L2_PACKET=linux +endif + +OBJS_l2 += src/l2_packet/l2_packet_$(CONFIG_L2_PACKET).c + +ifeq ($(CONFIG_L2_PACKET), pcap) +ifdef CONFIG_WINPCAP +L_CFLAGS += -DCONFIG_WINPCAP +LIBS += -lwpcap -lpacket +LIBS_w += -lwpcap +else +LIBS += -ldnet -lpcap +endif +endif + +ifeq ($(CONFIG_L2_PACKET), winpcap) +LIBS += -lwpcap -lpacket +LIBS_w += -lwpcap +endif + +ifeq ($(CONFIG_L2_PACKET), freebsd) +LIBS += -lpcap +endif + +ifdef CONFIG_ERP +L_CFLAGS += -DCONFIG_ERP +NEED_SHA256=y +NEED_HMAC_SHA256_KDF=y +endif + +ifdef CONFIG_EAP_TLS +# EAP-TLS +ifeq ($(CONFIG_EAP_TLS), dyn) +L_CFLAGS += -DEAP_TLS_DYNAMIC +EAPDYN += src/eap_peer/eap_tls.so +else +L_CFLAGS += -DEAP_TLS +OBJS += src/eap_peer/eap_tls.c +endif +TLS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_UNAUTH_TLS +# EAP-UNAUTH-TLS +L_CFLAGS += -DEAP_UNAUTH_TLS +ifndef CONFIG_EAP_TLS +OBJS += src/eap_peer/eap_tls.c +TLS_FUNCS=y +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_PEAP +# EAP-PEAP +ifeq ($(CONFIG_EAP_PEAP), dyn) +L_CFLAGS += -DEAP_PEAP_DYNAMIC +EAPDYN += src/eap_peer/eap_peap.so +else +L_CFLAGS += -DEAP_PEAP +OBJS += src/eap_peer/eap_peap.c +OBJS += src/eap_common/eap_peap_common.c +endif +TLS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_TTLS +# EAP-TTLS +ifeq ($(CONFIG_EAP_TTLS), dyn) +L_CFLAGS += -DEAP_TTLS_DYNAMIC +EAPDYN += src/eap_peer/eap_ttls.so +else +L_CFLAGS += -DEAP_TTLS +OBJS += src/eap_peer/eap_ttls.c +endif +TLS_FUNCS=y +ifndef CONFIG_FIPS +MS_FUNCS=y +CHAP=y +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_MD5 +# EAP-MD5 +ifeq ($(CONFIG_EAP_MD5), dyn) +L_CFLAGS += -DEAP_MD5_DYNAMIC +EAPDYN += src/eap_peer/eap_md5.so +else +L_CFLAGS += -DEAP_MD5 +OBJS += src/eap_peer/eap_md5.c +endif +CHAP=y +CONFIG_IEEE8021X_EAPOL=y +endif + +# backwards compatibility for old spelling +ifdef CONFIG_MSCHAPV2 +ifndef CONFIG_EAP_MSCHAPV2 +CONFIG_EAP_MSCHAPV2=y +endif +endif + +ifdef CONFIG_EAP_MSCHAPV2 +# EAP-MSCHAPv2 +ifeq ($(CONFIG_EAP_MSCHAPV2), dyn) +L_CFLAGS += -DEAP_MSCHAPv2_DYNAMIC +EAPDYN += src/eap_peer/eap_mschapv2.so +EAPDYN += src/eap_peer/mschapv2.so +else +L_CFLAGS += -DEAP_MSCHAPv2 +OBJS += src/eap_peer/eap_mschapv2.c +OBJS += src/eap_peer/mschapv2.c +endif +MS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_GTC +# EAP-GTC +ifeq ($(CONFIG_EAP_GTC), dyn) +L_CFLAGS += -DEAP_GTC_DYNAMIC +EAPDYN += src/eap_peer/eap_gtc.so +else +L_CFLAGS += -DEAP_GTC +OBJS += src/eap_peer/eap_gtc.c +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_OTP +# EAP-OTP +ifeq ($(CONFIG_EAP_OTP), dyn) +L_CFLAGS += -DEAP_OTP_DYNAMIC +EAPDYN += src/eap_peer/eap_otp.so +else +L_CFLAGS += -DEAP_OTP +OBJS += src/eap_peer/eap_otp.c +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_SIM +# EAP-SIM +ifeq ($(CONFIG_EAP_SIM), dyn) +L_CFLAGS += -DEAP_SIM_DYNAMIC +EAPDYN += src/eap_peer/eap_sim.so +else +L_CFLAGS += -DEAP_SIM +OBJS += src/eap_peer/eap_sim.c +endif +CONFIG_IEEE8021X_EAPOL=y +CONFIG_EAP_SIM_COMMON=y +NEED_AES_CBC=y +endif + +ifdef CONFIG_EAP_LEAP +# EAP-LEAP +ifeq ($(CONFIG_EAP_LEAP), dyn) +L_CFLAGS += -DEAP_LEAP_DYNAMIC +EAPDYN += src/eap_peer/eap_leap.so +else +L_CFLAGS += -DEAP_LEAP +OBJS += src/eap_peer/eap_leap.c +endif +MS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_PSK +# EAP-PSK +ifeq ($(CONFIG_EAP_PSK), dyn) +L_CFLAGS += -DEAP_PSK_DYNAMIC +EAPDYN += src/eap_peer/eap_psk.so +else +L_CFLAGS += -DEAP_PSK +OBJS += src/eap_peer/eap_psk.c src/eap_common/eap_psk_common.c +endif +CONFIG_IEEE8021X_EAPOL=y +NEED_AES=y +NEED_AES_OMAC1=y +NEED_AES_ENCBLOCK=y +NEED_AES_EAX=y +endif + +ifdef CONFIG_EAP_AKA +# EAP-AKA +ifeq ($(CONFIG_EAP_AKA), dyn) +L_CFLAGS += -DEAP_AKA_DYNAMIC +EAPDYN += src/eap_peer/eap_aka.so +else +L_CFLAGS += -DEAP_AKA +OBJS += src/eap_peer/eap_aka.c +endif +CONFIG_IEEE8021X_EAPOL=y +CONFIG_EAP_SIM_COMMON=y +NEED_AES_CBC=y +endif + +ifdef CONFIG_EAP_PROXY +L_CFLAGS += -DCONFIG_EAP_PROXY +OBJS += src/eap_peer/eap_proxy_$(CONFIG_EAP_PROXY).c +include $(LOCAL_PATH)/eap_proxy_$(CONFIG_EAP_PROXY).mk +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_AKA_PRIME +# EAP-AKA' +ifeq ($(CONFIG_EAP_AKA_PRIME), dyn) +L_CFLAGS += -DEAP_AKA_PRIME_DYNAMIC +else +L_CFLAGS += -DEAP_AKA_PRIME +endif +NEED_SHA256=y +endif + +ifdef CONFIG_EAP_SIM_COMMON +OBJS += src/eap_common/eap_sim_common.c +NEED_AES=y +NEED_FIPS186_2_PRF=y +endif + +ifdef CONFIG_EAP_FAST +# EAP-FAST +ifeq ($(CONFIG_EAP_FAST), dyn) +L_CFLAGS += -DEAP_FAST_DYNAMIC +EAPDYN += src/eap_peer/eap_fast.so +EAPDYN += src/eap_common/eap_fast_common.c +else +L_CFLAGS += -DEAP_FAST +OBJS += src/eap_peer/eap_fast.c src/eap_peer/eap_fast_pac.c +OBJS += src/eap_common/eap_fast_common.c +endif +TLS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +NEED_T_PRF=y +endif + +ifdef CONFIG_EAP_PAX +# EAP-PAX +ifeq ($(CONFIG_EAP_PAX), dyn) +L_CFLAGS += -DEAP_PAX_DYNAMIC +EAPDYN += src/eap_peer/eap_pax.so +else +L_CFLAGS += -DEAP_PAX +OBJS += src/eap_peer/eap_pax.c src/eap_common/eap_pax_common.c +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_SAKE +# EAP-SAKE +ifeq ($(CONFIG_EAP_SAKE), dyn) +L_CFLAGS += -DEAP_SAKE_DYNAMIC +EAPDYN += src/eap_peer/eap_sake.so +else +L_CFLAGS += -DEAP_SAKE +OBJS += src/eap_peer/eap_sake.c src/eap_common/eap_sake_common.c +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_GPSK +# EAP-GPSK +ifeq ($(CONFIG_EAP_GPSK), dyn) +L_CFLAGS += -DEAP_GPSK_DYNAMIC +EAPDYN += src/eap_peer/eap_gpsk.so +else +L_CFLAGS += -DEAP_GPSK +OBJS += src/eap_peer/eap_gpsk.c src/eap_common/eap_gpsk_common.c +endif +CONFIG_IEEE8021X_EAPOL=y +ifdef CONFIG_EAP_GPSK_SHA256 +L_CFLAGS += -DEAP_GPSK_SHA256 +endif +NEED_SHA256=y +NEED_AES_OMAC1=y +endif + +ifdef CONFIG_EAP_PWD +L_CFLAGS += -DEAP_PWD +OBJS += src/eap_peer/eap_pwd.c src/eap_common/eap_pwd_common.c +CONFIG_IEEE8021X_EAPOL=y +NEED_SHA256=y +endif + +ifdef CONFIG_EAP_EKE +# EAP-EKE +ifeq ($(CONFIG_EAP_EKE), dyn) +L_CFLAGS += -DEAP_EKE_DYNAMIC +EAPDYN += src/eap_peer/eap_eke.so +else +L_CFLAGS += -DEAP_EKE +OBJS += src/eap_peer/eap_eke.c src/eap_common/eap_eke_common.c +endif +CONFIG_IEEE8021X_EAPOL=y +NEED_DH_GROUPS=y +NEED_DH_GROUPS_ALL=y +NEED_SHA256=y +NEED_AES_CBC=y +endif + +ifdef CONFIG_WPS +# EAP-WSC +L_CFLAGS += -DCONFIG_WPS -DEAP_WSC +OBJS += wps_supplicant.c +OBJS += src/utils/uuid.c +OBJS += src/eap_peer/eap_wsc.c src/eap_common/eap_wsc_common.c +OBJS += src/wps/wps.c +OBJS += src/wps/wps_common.c +OBJS += src/wps/wps_attr_parse.c +OBJS += src/wps/wps_attr_build.c +OBJS += src/wps/wps_attr_process.c +OBJS += src/wps/wps_dev_attr.c +OBJS += src/wps/wps_enrollee.c +OBJS += src/wps/wps_registrar.c +CONFIG_IEEE8021X_EAPOL=y +NEED_DH_GROUPS=y +NEED_SHA256=y +NEED_BASE64=y +NEED_AES_CBC=y +NEED_MODEXP=y + +ifdef CONFIG_WPS_NFC +L_CFLAGS += -DCONFIG_WPS_NFC +OBJS += src/wps/ndef.c +NEED_WPS_OOB=y +endif + +ifdef NEED_WPS_OOB +L_CFLAGS += -DCONFIG_WPS_OOB +endif + +ifdef CONFIG_WPS_ER +CONFIG_WPS_UPNP=y +L_CFLAGS += -DCONFIG_WPS_ER +OBJS += src/wps/wps_er.c +OBJS += src/wps/wps_er_ssdp.c +endif + +ifdef CONFIG_WPS_UPNP +L_CFLAGS += -DCONFIG_WPS_UPNP +OBJS += src/wps/wps_upnp.c +OBJS += src/wps/wps_upnp_ssdp.c +OBJS += src/wps/wps_upnp_web.c +OBJS += src/wps/wps_upnp_event.c +OBJS += src/wps/wps_upnp_ap.c +OBJS += src/wps/upnp_xml.c +OBJS += src/wps/httpread.c +OBJS += src/wps/http_client.c +OBJS += src/wps/http_server.c +endif + +ifdef CONFIG_WPS_STRICT +L_CFLAGS += -DCONFIG_WPS_STRICT +OBJS += src/wps/wps_validate.c +endif + +ifdef CONFIG_WPS_TESTING +L_CFLAGS += -DCONFIG_WPS_TESTING +endif + +ifdef CONFIG_WPS_REG_DISABLE_OPEN +L_CFLAGS += -DCONFIG_WPS_REG_DISABLE_OPEN +endif + +endif + +ifdef CONFIG_EAP_IKEV2 +# EAP-IKEv2 +ifeq ($(CONFIG_EAP_IKEV2), dyn) +L_CFLAGS += -DEAP_IKEV2_DYNAMIC +EAPDYN += src/eap_peer/eap_ikev2.so src/eap_peer/ikev2.c +EAPDYN += src/eap_common/eap_ikev2_common.c src/eap_common/ikev2_common.c +else +L_CFLAGS += -DEAP_IKEV2 +OBJS += src/eap_peer/eap_ikev2.c src/eap_peer/ikev2.c +OBJS += src/eap_common/eap_ikev2_common.c src/eap_common/ikev2_common.c +endif +CONFIG_IEEE8021X_EAPOL=y +NEED_DH_GROUPS=y +NEED_DH_GROUPS_ALL=y +NEED_MODEXP=y +NEED_CIPHER=y +endif + +ifdef CONFIG_EAP_VENDOR_TEST +ifeq ($(CONFIG_EAP_VENDOR_TEST), dyn) +L_CFLAGS += -DEAP_VENDOR_TEST_DYNAMIC +EAPDYN += src/eap_peer/eap_vendor_test.so +else +L_CFLAGS += -DEAP_VENDOR_TEST +OBJS += src/eap_peer/eap_vendor_test.c +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_TNC +# EAP-TNC +L_CFLAGS += -DEAP_TNC +OBJS += src/eap_peer/eap_tnc.c +OBJS += src/eap_peer/tncc.c +NEED_BASE64=y +ifndef CONFIG_NATIVE_WINDOWS +ifndef CONFIG_DRIVER_BSD +LIBS += -ldl +endif +endif +endif + +ifdef CONFIG_IEEE8021X_EAPOL +# IEEE 802.1X/EAPOL state machines (e.g., for RADIUS authentication) +L_CFLAGS += -DIEEE8021X_EAPOL +OBJS += src/eapol_supp/eapol_supp_sm.c +OBJS += src/eap_peer/eap.c src/eap_peer/eap_methods.c +NEED_EAP_COMMON=y +ifdef CONFIG_DYNAMIC_EAP_METHODS +L_CFLAGS += -DCONFIG_DYNAMIC_EAP_METHODS +LIBS += -ldl -rdynamic +endif +endif + +ifdef CONFIG_AP +NEED_EAP_COMMON=y +NEED_RSN_AUTHENTICATOR=y +L_CFLAGS += -DCONFIG_AP +OBJS += ap.c +L_CFLAGS += -DCONFIG_NO_RADIUS +L_CFLAGS += -DCONFIG_NO_ACCOUNTING +L_CFLAGS += -DCONFIG_NO_VLAN +OBJS += src/ap/hostapd.c +OBJS += src/ap/wpa_auth_glue.c +OBJS += src/ap/utils.c +OBJS += src/ap/authsrv.c +OBJS += src/ap/ap_config.c +OBJS += src/utils/ip_addr.c +OBJS += src/ap/sta_info.c +OBJS += src/ap/tkip_countermeasures.c +OBJS += src/ap/ap_mlme.c +OBJS += src/ap/ieee802_1x.c +OBJS += src/eapol_auth/eapol_auth_sm.c +OBJS += src/ap/ieee802_11_auth.c +OBJS += src/ap/ieee802_11_shared.c +OBJS += src/ap/drv_callbacks.c +OBJS += src/ap/ap_drv_ops.c +OBJS += src/ap/beacon.c +OBJS += src/ap/bss_load.c +OBJS += src/ap/eap_user_db.c +OBJS += src/ap/neighbor_db.c +OBJS += src/ap/rrm.c +ifdef CONFIG_IEEE80211N +OBJS += src/ap/ieee802_11_ht.c +ifdef CONFIG_IEEE80211AC +OBJS += src/ap/ieee802_11_vht.c +endif +endif +ifdef CONFIG_WNM +OBJS += src/ap/wnm_ap.c +endif +ifdef CONFIG_MBO +OBJS += src/ap/mbo_ap.c +endif +ifdef CONFIG_CTRL_IFACE +OBJS += src/ap/ctrl_iface_ap.c +endif + +L_CFLAGS += -DEAP_SERVER -DEAP_SERVER_IDENTITY +OBJS += src/eap_server/eap_server.c +OBJS += src/eap_server/eap_server_identity.c +OBJS += src/eap_server/eap_server_methods.c + +ifdef CONFIG_IEEE80211N +L_CFLAGS += -DCONFIG_IEEE80211N +ifdef CONFIG_IEEE80211AC +L_CFLAGS += -DCONFIG_IEEE80211AC +endif +endif + +ifdef CONFIG_MBO +OBJS += mbo.c +L_CFLAGS += -DCONFIG_MBO +endif + +ifdef NEED_AP_MLME +OBJS += src/ap/wmm.c +OBJS += src/ap/ap_list.c +OBJS += src/ap/ieee802_11.c +OBJS += src/ap/hw_features.c +OBJS += src/ap/dfs.c +L_CFLAGS += -DNEED_AP_MLME +endif +ifdef CONFIG_WPS +L_CFLAGS += -DEAP_SERVER_WSC +OBJS += src/ap/wps_hostapd.c +OBJS += src/eap_server/eap_server_wsc.c +endif +ifdef CONFIG_INTERWORKING +OBJS += src/ap/gas_serv.c +endif +ifdef CONFIG_HS20 +OBJS += src/ap/hs20.c +endif +endif + +ifdef NEED_RSN_AUTHENTICATOR +L_CFLAGS += -DCONFIG_NO_RADIUS +NEED_AES_WRAP=y +OBJS += src/ap/wpa_auth.c +OBJS += src/ap/wpa_auth_ie.c +OBJS += src/ap/pmksa_cache_auth.c +ifdef CONFIG_IEEE80211R +OBJS += src/ap/wpa_auth_ft.c +endif +ifdef CONFIG_PEERKEY +OBJS += src/ap/peerkey_auth.c +endif +endif + +ifdef CONFIG_ACS +L_CFLAGS += -DCONFIG_ACS +OBJS += src/ap/acs.c +LIBS += -lm +endif + +ifdef CONFIG_PCSC +# PC/SC interface for smartcards (USIM, GSM SIM) +L_CFLAGS += -DPCSC_FUNCS -I/usr/include/PCSC +OBJS += src/utils/pcsc_funcs.c +# -lpthread may not be needed depending on how pcsc-lite was configured +ifdef CONFIG_NATIVE_WINDOWS +#Once MinGW gets support for WinScard, -lwinscard could be used instead of the +#dynamic symbol loading that is now used in pcsc_funcs.c +#LIBS += -lwinscard +else +LIBS += -lpcsclite -lpthread +endif +endif + +ifdef CONFIG_SIM_SIMULATOR +L_CFLAGS += -DCONFIG_SIM_SIMULATOR +NEED_MILENAGE=y +endif + +ifdef CONFIG_USIM_SIMULATOR +L_CFLAGS += -DCONFIG_USIM_SIMULATOR +NEED_MILENAGE=y +endif + +ifdef NEED_MILENAGE +OBJS += src/crypto/milenage.c +NEED_AES_ENCBLOCK=y +endif + +ifdef CONFIG_PKCS12 +L_CFLAGS += -DPKCS12_FUNCS +endif + +ifdef CONFIG_SMARTCARD +L_CFLAGS += -DCONFIG_SMARTCARD +endif + +ifdef MS_FUNCS +OBJS += src/crypto/ms_funcs.c +NEED_DES=y +NEED_MD4=y +endif + +ifdef CHAP +OBJS += src/eap_common/chap.c +endif + +ifdef TLS_FUNCS +NEED_DES=y +# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, EAP_TTLS, and EAP_FAST) +OBJS += src/eap_peer/eap_tls_common.c +ifndef CONFIG_FIPS +NEED_TLS_PRF=y +NEED_SHA1=y +NEED_MD5=y +endif +endif + +ifndef CONFIG_TLS +CONFIG_TLS=openssl +endif + +ifdef CONFIG_TLSV11 +L_CFLAGS += -DCONFIG_TLSV11 +endif + +ifdef CONFIG_TLSV12 +L_CFLAGS += -DCONFIG_TLSV12 +NEED_SHA256=y +endif + +ifeq ($(CONFIG_TLS), openssl) +ifdef TLS_FUNCS +L_CFLAGS += -DEAP_TLS_OPENSSL +OBJS += src/crypto/tls_openssl.c +OBJS += src/crypto/tls_openssl_ocsp.c +LIBS += -lssl +endif +OBJS += src/crypto/crypto_openssl.c +OBJS_p += src/crypto/crypto_openssl.c +ifdef NEED_FIPS186_2_PRF +OBJS += src/crypto/fips_prf_openssl.c +endif +NEED_SHA256=y +NEED_TLS_PRF_SHA256=y +LIBS += -lcrypto +LIBS_p += -lcrypto +ifdef CONFIG_TLS_ADD_DL +LIBS += -ldl +LIBS_p += -ldl +endif +endif + +ifeq ($(CONFIG_TLS), gnutls) +ifdef TLS_FUNCS +OBJS += src/crypto/tls_gnutls.c +LIBS += -lgnutls -lgpg-error +endif +OBJS += src/crypto/crypto_gnutls.c +OBJS_p += src/crypto/crypto_gnutls.c +ifdef NEED_FIPS186_2_PRF +OBJS += src/crypto/fips_prf_internal.c +OBJS += src/crypto/sha1-internal.c +endif +LIBS += -lgcrypt +LIBS_p += -lgcrypt +CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_RC4=y +CONFIG_INTERNAL_DH_GROUP5=y +endif + +ifeq ($(CONFIG_TLS), internal) +ifndef CONFIG_CRYPTO +CONFIG_CRYPTO=internal +endif +ifdef TLS_FUNCS +OBJS += src/crypto/crypto_internal-rsa.c +OBJS += src/crypto/tls_internal.c +OBJS += src/tls/tlsv1_common.c +OBJS += src/tls/tlsv1_record.c +OBJS += src/tls/tlsv1_cred.c +OBJS += src/tls/tlsv1_client.c +OBJS += src/tls/tlsv1_client_write.c +OBJS += src/tls/tlsv1_client_read.c +OBJS += src/tls/tlsv1_client_ocsp.c +OBJS += src/tls/asn1.c +OBJS += src/tls/rsa.c +OBJS += src/tls/x509v3.c +OBJS += src/tls/pkcs1.c +OBJS += src/tls/pkcs5.c +OBJS += src/tls/pkcs8.c +NEED_SHA256=y +NEED_BASE64=y +NEED_TLS_PRF=y +ifdef CONFIG_TLSV12 +NEED_TLS_PRF_SHA256=y +endif +NEED_MODEXP=y +NEED_CIPHER=y +L_CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT +endif +ifdef NEED_CIPHER +NEED_DES=y +OBJS += src/crypto/crypto_internal-cipher.c +endif +ifdef NEED_MODEXP +OBJS += src/crypto/crypto_internal-modexp.c +OBJS += src/tls/bignum.c +endif +ifeq ($(CONFIG_CRYPTO), libtomcrypt) +OBJS += src/crypto/crypto_libtomcrypt.c +OBJS_p += src/crypto/crypto_libtomcrypt.c +LIBS += -ltomcrypt -ltfm +LIBS_p += -ltomcrypt -ltfm +CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_RC4=y +CONFIG_INTERNAL_DH_GROUP5=y +endif +ifeq ($(CONFIG_CRYPTO), internal) +OBJS += src/crypto/crypto_internal.c +OBJS_p += src/crypto/crypto_internal.c +NEED_AES_ENC=y +L_CFLAGS += -DCONFIG_CRYPTO_INTERNAL +ifdef CONFIG_INTERNAL_LIBTOMMATH +L_CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH +ifdef CONFIG_INTERNAL_LIBTOMMATH_FAST +L_CFLAGS += -DLTM_FAST +endif +else +LIBS += -ltommath +LIBS_p += -ltommath +endif +CONFIG_INTERNAL_AES=y +CONFIG_INTERNAL_DES=y +CONFIG_INTERNAL_SHA1=y +CONFIG_INTERNAL_MD4=y +CONFIG_INTERNAL_MD5=y +CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_SHA384=y +CONFIG_INTERNAL_SHA512=y +CONFIG_INTERNAL_RC4=y +CONFIG_INTERNAL_DH_GROUP5=y +endif +ifeq ($(CONFIG_CRYPTO), cryptoapi) +OBJS += src/crypto/crypto_cryptoapi.c +OBJS_p += src/crypto/crypto_cryptoapi.c +L_CFLAGS += -DCONFIG_CRYPTO_CRYPTOAPI +CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_RC4=y +endif +endif + +ifeq ($(CONFIG_TLS), none) +ifdef TLS_FUNCS +OBJS += src/crypto/tls_none.c +L_CFLAGS += -DEAP_TLS_NONE +CONFIG_INTERNAL_AES=y +CONFIG_INTERNAL_SHA1=y +CONFIG_INTERNAL_MD5=y +endif +OBJS += src/crypto/crypto_none.c +OBJS_p += src/crypto/crypto_none.c +CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_RC4=y +endif + +ifdef TLS_FUNCS +ifdef CONFIG_SMARTCARD +ifndef CONFIG_NATIVE_WINDOWS +ifneq ($(CONFIG_L2_PACKET), freebsd) +LIBS += -ldl +endif +endif +endif +endif + +ifndef TLS_FUNCS +OBJS += src/crypto/tls_none.c +ifeq ($(CONFIG_TLS), internal) +CONFIG_INTERNAL_AES=y +CONFIG_INTERNAL_SHA1=y +CONFIG_INTERNAL_MD5=y +CONFIG_INTERNAL_RC4=y +endif +endif + +AESOBJS = # none so far (see below) +ifdef CONFIG_INTERNAL_AES +AESOBJS += src/crypto/aes-internal.c src/crypto/aes-internal-dec.c +endif + +ifneq ($(CONFIG_TLS), openssl) +NEED_INTERNAL_AES_WRAP=y +endif +ifdef CONFIG_OPENSSL_INTERNAL_AES_WRAP +# Seems to be needed at least with BoringSSL +NEED_INTERNAL_AES_WRAP=y +L_CFLAGS += -DCONFIG_OPENSSL_INTERNAL_AES_WRAP +endif +ifdef CONFIG_FIPS +# Have to use internal AES key wrap routines to use OpenSSL EVP since the +# OpenSSL AES_wrap_key()/AES_unwrap_key() API is not available in FIPS mode. +NEED_INTERNAL_AES_WRAP=y +endif + +ifdef NEED_INTERNAL_AES_WRAP +AESOBJS += src/crypto/aes-unwrap.c +endif +ifdef NEED_AES_EAX +AESOBJS += src/crypto/aes-eax.c +NEED_AES_CTR=y +endif +ifdef NEED_AES_CTR +AESOBJS += src/crypto/aes-ctr.c +endif +ifdef NEED_AES_ENCBLOCK +AESOBJS += src/crypto/aes-encblock.c +endif +ifdef NEED_AES_OMAC1 +NEED_AES_ENC=y +ifdef CONFIG_OPENSSL_CMAC +L_CFLAGS += -DCONFIG_OPENSSL_CMAC +else +AESOBJS += src/crypto/aes-omac1.c +endif +endif +ifdef NEED_AES_WRAP +NEED_AES_ENC=y +ifdef NEED_INTERNAL_AES_WRAP +AESOBJS += src/crypto/aes-wrap.c +endif +endif +ifdef NEED_AES_CBC +NEED_AES_ENC=y +ifneq ($(CONFIG_TLS), openssl) +AESOBJS += src/crypto/aes-cbc.c +endif +endif +ifdef NEED_AES_ENC +ifdef CONFIG_INTERNAL_AES +AESOBJS += src/crypto/aes-internal-enc.c +endif +endif +ifdef NEED_AES_SIV +AESOBJS += src/crypto/aes-siv.c +endif +ifdef NEED_AES +OBJS += $(AESOBJS) +endif + +SHA1OBJS = +ifdef NEED_SHA1 +ifneq ($(CONFIG_TLS), openssl) +SHA1OBJS += src/crypto/sha1.c +endif +SHA1OBJS += src/crypto/sha1-prf.c +ifdef CONFIG_INTERNAL_SHA1 +SHA1OBJS += src/crypto/sha1-internal.c +ifdef NEED_FIPS186_2_PRF +SHA1OBJS += src/crypto/fips_prf_internal.c +endif +endif +ifdef CONFIG_NO_WPA_PASSPHRASE +L_CFLAGS += -DCONFIG_NO_PBKDF2 +else +ifneq ($(CONFIG_TLS), openssl) +SHA1OBJS += src/crypto/sha1-pbkdf2.c +endif +endif +ifdef NEED_T_PRF +SHA1OBJS += src/crypto/sha1-tprf.c +endif +ifdef NEED_TLS_PRF +SHA1OBJS += src/crypto/sha1-tlsprf.c +endif +endif + +MD5OBJS = +ifndef CONFIG_FIPS +ifneq ($(CONFIG_TLS), openssl) +MD5OBJS += src/crypto/md5.c +endif +endif +ifdef NEED_MD5 +ifdef CONFIG_INTERNAL_MD5 +MD5OBJS += src/crypto/md5-internal.c +endif +OBJS += $(MD5OBJS) +OBJS_p += $(MD5OBJS) +endif + +ifdef NEED_MD4 +ifdef CONFIG_INTERNAL_MD4 +OBJS += src/crypto/md4-internal.c +endif +endif + +DESOBJS = # none needed when not internal +ifdef NEED_DES +ifdef CONFIG_INTERNAL_DES +DESOBJS += src/crypto/des-internal.c +endif +endif + +ifdef CONFIG_NO_RC4 +L_CFLAGS += -DCONFIG_NO_RC4 +endif + +ifdef NEED_RC4 +ifdef CONFIG_INTERNAL_RC4 +ifndef CONFIG_NO_RC4 +OBJS += src/crypto/rc4.c +endif +endif +endif + +SHA256OBJS = # none by default +ifdef NEED_SHA256 +L_CFLAGS += -DCONFIG_SHA256 +ifneq ($(CONFIG_TLS), openssl) +SHA256OBJS += src/crypto/sha256.c +endif +SHA256OBJS += src/crypto/sha256-prf.c +ifdef CONFIG_INTERNAL_SHA256 +SHA256OBJS += src/crypto/sha256-internal.c +endif +ifdef CONFIG_INTERNAL_SHA384 +L_CFLAGS += -DCONFIG_INTERNAL_SHA384 +SHA256OBJS += src/crypto/sha384-internal.c +endif +ifdef CONFIG_INTERNAL_SHA512 +L_CFLAGS += -DCONFIG_INTERNAL_SHA512 +SHA256OBJS += src/crypto/sha512-internal.c +endif +ifdef NEED_TLS_PRF_SHA256 +SHA256OBJS += src/crypto/sha256-tlsprf.c +endif +ifdef NEED_HMAC_SHA256_KDF +L_CFLAGS += -DCONFIG_HMAC_SHA256_KDF +SHA256OBJS += src/crypto/sha256-kdf.c +endif +OBJS += $(SHA256OBJS) +endif +ifdef NEED_SHA384 +L_CFLAGS += -DCONFIG_SHA384 +OBJS += src/crypto/sha384-prf.c +endif + +ifdef NEED_DH_GROUPS +OBJS += src/crypto/dh_groups.c +endif +ifdef NEED_DH_GROUPS_ALL +L_CFLAGS += -DALL_DH_GROUPS +endif +ifdef CONFIG_INTERNAL_DH_GROUP5 +ifdef NEED_DH_GROUPS +OBJS += src/crypto/dh_group5.c +endif +endif + +ifdef NEED_ECC +L_CFLAGS += -DCONFIG_ECC +endif + +ifdef CONFIG_NO_RANDOM_POOL +L_CFLAGS += -DCONFIG_NO_RANDOM_POOL +else +OBJS += src/crypto/random.c +endif + +ifdef CONFIG_CTRL_IFACE +ifeq ($(CONFIG_CTRL_IFACE), y) +ifdef CONFIG_NATIVE_WINDOWS +CONFIG_CTRL_IFACE=named_pipe +else +CONFIG_CTRL_IFACE=unix +endif +endif +L_CFLAGS += -DCONFIG_CTRL_IFACE +ifeq ($(CONFIG_CTRL_IFACE), unix) +L_CFLAGS += -DCONFIG_CTRL_IFACE_UNIX +OBJS += src/common/ctrl_iface_common.c +endif +ifeq ($(CONFIG_CTRL_IFACE), udp) +L_CFLAGS += -DCONFIG_CTRL_IFACE_UDP +endif +ifeq ($(CONFIG_CTRL_IFACE), named_pipe) +L_CFLAGS += -DCONFIG_CTRL_IFACE_NAMED_PIPE +endif +ifeq ($(CONFIG_CTRL_IFACE), udp-remote) +CONFIG_CTRL_IFACE=udp +L_CFLAGS += -DCONFIG_CTRL_IFACE_UDP +L_CFLAGS += -DCONFIG_CTRL_IFACE_UDP_REMOTE +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 +ifdef CONFIG_WPS +DBUS_OBJS += dbus/dbus_new_handlers_wps.c +endif +ifdef CONFIG_P2P +DBUS_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) +endif + +ifdef DBUS +DBUS_CFLAGS += -DCONFIG_DBUS +DBUS_OBJS += dbus/dbus_common.c +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 +endif + +ifdef CONFIG_READLINE +OBJS_c += src/utils/edit_readline.c +LIBS_c += -lncurses -lreadline +else +ifdef CONFIG_WPA_CLI_EDIT +OBJS_c += src/utils/edit.c +else +OBJS_c += src/utils/edit_simple.c +endif +endif + +ifdef CONFIG_NATIVE_WINDOWS +L_CFLAGS += -DCONFIG_NATIVE_WINDOWS +LIBS += -lws2_32 -lgdi32 -lcrypt32 +LIBS_c += -lws2_32 +LIBS_p += -lws2_32 -lgdi32 +ifeq ($(CONFIG_CRYPTO), cryptoapi) +LIBS_p += -lcrypt32 +endif +endif + +ifdef CONFIG_NO_STDOUT_DEBUG +L_CFLAGS += -DCONFIG_NO_STDOUT_DEBUG +ifndef CONFIG_CTRL_IFACE +L_CFLAGS += -DCONFIG_NO_WPA_MSG +endif +endif + +ifdef CONFIG_ANDROID_LOG +L_CFLAGS += -DCONFIG_ANDROID_LOG +endif + +ifdef CONFIG_IPV6 +# for eapol_test only +L_CFLAGS += -DCONFIG_IPV6 +endif + +ifdef NEED_BASE64 +OBJS += src/utils/base64.c +endif + +ifdef NEED_SME +OBJS += sme.c +L_CFLAGS += -DCONFIG_SME +endif + +OBJS += src/common/ieee802_11_common.c +OBJS += src/common/hw_features_common.c + +ifdef NEED_EAP_COMMON +OBJS += src/eap_common/eap_common.c +endif + +ifndef CONFIG_MAIN +CONFIG_MAIN=main +endif + +ifdef CONFIG_DEBUG_SYSLOG +L_CFLAGS += -DCONFIG_DEBUG_SYSLOG +ifdef CONFIG_DEBUG_SYSLOG_FACILITY +L_CFLAGS += -DLOG_HOSTAPD="$(CONFIG_DEBUG_SYSLOG_FACILITY)" +endif +endif + +ifdef CONFIG_DEBUG_LINUX_TRACING +L_CFLAGS += -DCONFIG_DEBUG_LINUX_TRACING +endif + +ifdef CONFIG_DEBUG_FILE +L_CFLAGS += -DCONFIG_DEBUG_FILE +endif + +ifdef CONFIG_DELAYED_MIC_ERROR_REPORT +L_CFLAGS += -DCONFIG_DELAYED_MIC_ERROR_REPORT +endif + +ifdef CONFIG_FIPS +L_CFLAGS += -DCONFIG_FIPS +endif + +OBJS += $(SHA1OBJS) $(DESOBJS) + +OBJS_p += $(SHA1OBJS) +OBJS_p += $(SHA256OBJS) + +ifdef CONFIG_BGSCAN_SIMPLE +L_CFLAGS += -DCONFIG_BGSCAN_SIMPLE +OBJS += bgscan_simple.c +NEED_BGSCAN=y +endif + +ifdef CONFIG_BGSCAN_LEARN +L_CFLAGS += -DCONFIG_BGSCAN_LEARN +OBJS += bgscan_learn.c +NEED_BGSCAN=y +endif + +ifdef NEED_BGSCAN +L_CFLAGS += -DCONFIG_BGSCAN +OBJS += bgscan.c +endif + +ifdef CONFIG_AUTOSCAN_EXPONENTIAL +L_CFLAGS += -DCONFIG_AUTOSCAN_EXPONENTIAL +OBJS += autoscan_exponential.c +NEED_AUTOSCAN=y +endif + +ifdef CONFIG_AUTOSCAN_PERIODIC +L_CFLAGS += -DCONFIG_AUTOSCAN_PERIODIC +OBJS += autoscan_periodic.c +NEED_AUTOSCAN=y +endif + +ifdef NEED_AUTOSCAN +L_CFLAGS += -DCONFIG_AUTOSCAN +OBJS += autoscan.c +endif + +ifdef CONFIG_EXT_PASSWORD_TEST +OBJS += src/utils/ext_password_test.c +L_CFLAGS += -DCONFIG_EXT_PASSWORD_TEST +NEED_EXT_PASSWORD=y +endif + +ifdef NEED_EXT_PASSWORD +OBJS += src/utils/ext_password.c +L_CFLAGS += -DCONFIG_EXT_PASSWORD +endif + +ifdef NEED_GAS +OBJS += src/common/gas.c +OBJS += gas_query.c +L_CFLAGS += -DCONFIG_GAS +NEED_OFFCHANNEL=y +endif + +ifdef NEED_OFFCHANNEL +OBJS += offchannel.c +L_CFLAGS += -DCONFIG_OFFCHANNEL +endif + +OBJS += src/drivers/driver_common.c + +OBJS += wpa_supplicant.c events.c blacklist.c wpas_glue.c scan.c +OBJS_t := $(OBJS) $(OBJS_l2) eapol_test.c +OBJS_t += src/radius/radius_client.c +OBJS_t += src/radius/radius.c +ifndef CONFIG_AP +OBJS_t += src/utils/ip_addr.c +endif +OBJS_t2 := $(OBJS) $(OBJS_l2) preauth_test.c +OBJS += $(CONFIG_MAIN).c + +ifdef CONFIG_PRIVSEP +OBJS_priv += $(OBJS_d) src/drivers/drivers.c +OBJS_priv += $(OBJS_l2) +OBJS_priv += src/utils/os_$(CONFIG_OS).c +OBJS_priv += src/utils/$(CONFIG_ELOOP).c +OBJS_priv += src/utils/common.c +OBJS_priv += src/utils/wpa_debug.c +OBJS_priv += src/utils/wpabuf.c +OBJS_priv += wpa_priv.c +ifdef CONFIG_DRIVER_NL80211 +OBJS_priv += src/common/ieee802_11_common.c +endif +OBJS += src/l2_packet/l2_packet_privsep.c +OBJS += src/drivers/driver_privsep.c +EXTRA_progs += wpa_priv +else +OBJS += $(OBJS_d) src/drivers/drivers.c +OBJS += $(OBJS_l2) +endif + +ifdef CONFIG_NDIS_EVENTS_INTEGRATED +L_CFLAGS += -DCONFIG_NDIS_EVENTS_INTEGRATED +OBJS += src/drivers/ndis_events.c +EXTRALIBS += -loleaut32 -lole32 -luuid +ifdef PLATFORMSDKLIB +EXTRALIBS += $(PLATFORMSDKLIB)/WbemUuid.Lib +else +EXTRALIBS += WbemUuid.Lib +endif +endif + +ifndef LDO +LDO=$(CC) +endif + +######################## + +include $(CLEAR_VARS) +LOCAL_MODULE := wpa_cli +LOCAL_MODULE_TAGS := debug +LOCAL_SHARED_LIBRARIES := libc libcutils liblog +LOCAL_CFLAGS := $(L_CFLAGS) +LOCAL_SRC_FILES := $(OBJS_c) +LOCAL_C_INCLUDES := $(INCLUDES) +include $(BUILD_EXECUTABLE) + +######################## +include $(CLEAR_VARS) +LOCAL_MODULE := wpa_supplicant +ifdef CONFIG_DRIVER_CUSTOM +LOCAL_STATIC_LIBRARIES := libCustomWifi +endif +ifneq ($(BOARD_WPA_SUPPLICANT_PRIVATE_LIB),) +LOCAL_STATIC_LIBRARIES += $(BOARD_WPA_SUPPLICANT_PRIVATE_LIB) +endif +LOCAL_SHARED_LIBRARIES := libc libcutils liblog +ifdef CONFIG_EAP_PROXY +LOCAL_STATIC_LIBRARIES += $(LIB_STATIC_EAP_PROXY) +LOCAL_SHARED_LIBRARIES += $(LIB_SHARED_EAP_PROXY) +endif +ifeq ($(CONFIG_TLS), openssl) +LOCAL_SHARED_LIBRARIES += libcrypto libssl libkeystore_binder +endif + +# With BoringSSL we need libkeystore-engine in order to provide access to +# keystore keys. +ifneq (,$(wildcard external/boringssl/flavor.mk)) +LOCAL_SHARED_LIBRARIES += libkeystore-engine +endif + +ifdef CONFIG_DRIVER_NL80211 +ifneq ($(wildcard external/libnl),) +LOCAL_SHARED_LIBRARIES += libnl +else +LOCAL_STATIC_LIBRARIES += libnl_2 +endif +endif +LOCAL_CFLAGS := $(L_CFLAGS) +LOCAL_SRC_FILES := $(OBJS) +LOCAL_C_INCLUDES := $(INCLUDES) +ifeq ($(DBUS), y) +LOCAL_SHARED_LIBRARIES += libdbus +endif +ifeq ($(WPA_SUPPLICANT_USE_BINDER), y) +LOCAL_SHARED_LIBRARIES += libbinder libutils +LOCAL_STATIC_LIBRARIES += libwpa_binder libwpa_binder_interface +endif +include $(BUILD_EXECUTABLE) + +######################## +# +#include $(CLEAR_VARS) +#LOCAL_MODULE := eapol_test +#ifdef CONFIG_DRIVER_CUSTOM +#LOCAL_STATIC_LIBRARIES := libCustomWifi +#endif +#LOCAL_SHARED_LIBRARIES := libc libcrypto libssl +#LOCAL_CFLAGS := $(L_CFLAGS) +#LOCAL_SRC_FILES := $(OBJS_t) +#LOCAL_C_INCLUDES := $(INCLUDES) +#include $(BUILD_EXECUTABLE) +# +######################## +# +#local_target_dir := $(TARGET_OUT)/etc/wifi +# +#include $(CLEAR_VARS) +#LOCAL_MODULE := wpa_supplicant.conf +#LOCAL_MODULE_CLASS := ETC +#LOCAL_MODULE_PATH := $(local_target_dir) +#LOCAL_SRC_FILES := $(LOCAL_MODULE) +#include $(BUILD_PREBUILT) +# +######################## + +include $(CLEAR_VARS) +LOCAL_MODULE = libwpa_client +LOCAL_CFLAGS = $(L_CFLAGS) +LOCAL_SRC_FILES = src/common/wpa_ctrl.c src/utils/os_$(CONFIG_OS).c +LOCAL_C_INCLUDES = $(INCLUDES) +LOCAL_SHARED_LIBRARIES := libcutils liblog +LOCAL_COPY_HEADERS_TO := libwpa_client +LOCAL_COPY_HEADERS := src/common/wpa_ctrl.h +LOCAL_COPY_HEADERS += src/common/qca-vendor.h +include $(BUILD_SHARED_LIBRARY) + +ifeq ($(WPA_SUPPLICANT_USE_BINDER), y) +### Binder interface library ### +######################## + +include $(CLEAR_VARS) +LOCAL_MODULE := libwpa_binder_interface +LOCAL_AIDL_INCLUDES := \ + $(LOCAL_PATH)/binder \ + frameworks/native/aidl/binder +LOCAL_EXPORT_C_INCLUDE_DIRS := \ + $(LOCAL_PATH)/binder +LOCAL_CPPFLAGS := $(L_CPPFLAGS) +LOCAL_SRC_FILES := \ + binder/binder_constants.cpp \ + binder/fi/w1/wpa_supplicant/ISupplicant.aidl \ + binder/fi/w1/wpa_supplicant/ISupplicantCallbacks.aidl \ + binder/fi/w1/wpa_supplicant/IIface.aidl +LOCAL_SHARED_LIBRARIES := libbinder +include $(BUILD_STATIC_LIBRARY) + +### Binder service library ### +######################## + +include $(CLEAR_VARS) +LOCAL_MODULE := libwpa_binder +LOCAL_CPPFLAGS := $(L_CPPFLAGS) +LOCAL_CFLAGS := $(L_CFLAGS) +LOCAL_C_INCLUDES := $(INCLUDES) +LOCAL_SRC_FILES := \ + binder/binder.cpp binder/binder_manager.cpp \ + binder/supplicant.cpp binder/iface.cpp +LOCAL_SHARED_LIBRARIES := \ + libbinder \ + libutils +LOCAL_STATIC_LIBRARIES := libwpa_binder_interface +include $(BUILD_STATIC_LIBRARY) + +endif # BINDER == y diff --git a/contrib/wpa/wpa_supplicant/ChangeLog b/contrib/wpa/wpa_supplicant/ChangeLog index facd90eea30c..f28055f4093e 100644 --- a/contrib/wpa/wpa_supplicant/ChangeLog +++ b/contrib/wpa/wpa_supplicant/ChangeLog @@ -1,5 +1,149 @@ ChangeLog for wpa_supplicant +2016-10-02 - v2.6 + * fixed WNM Sleep Mode processing when PMF is not enabled + [http://w1.fi/security/2015-6/] (CVE-2015-5310) + * fixed EAP-pwd last fragment validation + [http://w1.fi/security/2015-7/] (CVE-2015-5315) + * fixed EAP-pwd unexpected Confirm message processing + [http://w1.fi/security/2015-8/] (CVE-2015-5316) + * fixed WPS configuration update vulnerability with malformed passphrase + [http://w1.fi/security/2016-1/] (CVE-2016-4476) + * fixed configuration update vulnerability with malformed parameters set + over the local control interface + [http://w1.fi/security/2016-1/] (CVE-2016-4477) + * fixed TK configuration to the driver in EAPOL-Key 3/4 retry case + * extended channel switch support for P2P GO + * started to throttle control interface event message bursts to avoid + issues with monitor sockets running out of buffer space + * mesh mode fixes/improvements + - generate proper AID for peer + - enable WMM by default + - add VHT support + - fix PMKID derivation + - improve robustness on various exchanges + - fix peer link counting in reconnect case + - improve mesh joining behavior + - allow DTIM period to be configured + - allow HT to be disabled (disable_ht=1) + - add MESH_PEER_ADD and MESH_PEER_REMOVE commands + - add support for PMKSA caching + - add minimal support for SAE group negotiation + - allow pairwise/group cipher to be configured in the network profile + - use ieee80211w profile parameter to enable/disable PMF and derive + a separate TX IGTK if PMF is enabled instead of using MGTK + incorrectly + - fix AEK and MTK derivation + - remove GTKdata and IGTKdata from Mesh Peering Confirm/Close + - note: these changes are not fully backwards compatible for secure + (RSN) mesh network + * fixed PMKID derivation with SAE + * added support for requesting and fetching arbitrary ANQP-elements + without internal support in wpa_supplicant for the specific element + (anqp[265]= in "BSS " command output) + * P2P + - filter control characters in group client device names to be + consistent with other P2P peer cases + - support VHT 80+80 MHz and 160 MHz + - indicate group completion in P2P Client role after data association + instead of already after the WPS provisioning step + - improve group-join operation to use SSID, if known, to filter BSS + entries + - added optional ssid= argument to P2P_CONNECT for join case + - added P2P_GROUP_MEMBER command to fetch client interface address + * P2PS + - fix follow-on PD Response behavior + - fix PD Response generation for unknown peer + - fix persistent group reporting + - add channel policy to PD Request + - add group SSID to the P2PS-PROV-DONE event + - allow "P2P_CONNECT p2ps" to be used without specifying the + default PIN + * BoringSSL + - support for OCSP stapling + - support building of h20-osu-client + * D-Bus + - add ExpectDisconnect() + - add global config parameters as properties + - add SaveConfig() + - add VendorElemAdd(), VendorElemGet(), VendorElemRem() + * fixed Suite B 192-bit AKM to use proper PMK length + (note: this makes old releases incompatible with the fixed behavior) + * improved PMF behavior for cases where the AP and STA has different + configuration by not trying to connect in some corner cases where the + connection cannot succeed + * added option to reopen debug log (e.g., to rotate the file) upon + receipt of SIGHUP signal + * EAP-pwd: added support for Brainpool Elliptic Curves + (with OpenSSL 1.0.2 and newer) + * fixed EAPOL reauthentication after FT protocol run + * fixed FTIE generation for 4-way handshake after FT protocol run + * extended INTERFACE_ADD command to allow certain type (sta/ap) + interface to be created + * fixed and improved various FST operations + * added 80+80 MHz and 160 MHz VHT support for IBSS/mesh + * fixed SIGNAL_POLL in IBSS and mesh cases + * added an option to abort an ongoing scan (used to speed up connection + and can also be done with the new ABORT_SCAN command) + * TLS client + - do not verify CA certificates when ca_cert is not specified + - support validating server certificate hash + - support SHA384 and SHA512 hashes + - add signature_algorithms extension into ClientHello + - support TLS v1.2 signature algorithm with SHA384 and SHA512 + - support server certificate probing + - allow specific TLS versions to be disabled with phase2 parameter + - support extKeyUsage + - support PKCS #5 v2.0 PBES2 + - support PKCS #5 with PKCS #12 style key decryption + - minimal support for PKCS #12 + - support OCSP stapling (including ocsp_multi) + * OpenSSL + - support OpenSSL 1.1 API changes + - drop support for OpenSSL 0.9.8 + - drop support for OpenSSL 1.0.0 + * added support for multiple schedule scan plans (sched_scan_plans) + * added support for external server certificate chain validation + (tls_ext_cert_check=1 in the network profile phase1 parameter) + * made phase2 parser more strict about correct use of auth= and + autheap= values + * improved GAS offchannel operations with comeback request + * added SIGNAL_MONITOR command to request signal strength monitoring + events + * added command for retrieving HS 2.0 icons with in-memory storage + (REQ_HS20_ICON, GET_HS20_ICON, DEL_HS20_ICON commands and + RX-HS20-ICON event) + * enabled ACS support for AP mode operations with wpa_supplicant + * EAP-PEAP: fixed interoperability issue with Windows 2012r2 server + ("Invalid Compound_MAC in cryptobinding TLV") + * EAP-TTLS: fixed success after fragmented final Phase 2 message + * VHT: added interoperability workaround for 80+80 and 160 MHz channels + * WNM: workaround for broken AP operating class behavior + * added kqueue(2) support for eloop (CONFIG_ELOOP_KQUEUE) + * nl80211: + - add support for full station state operations + - do not add NL80211_ATTR_SMPS_MODE attribute if HT is disabled + - add NL80211_ATTR_PREV_BSSID with Connect command + - fix IEEE 802.1X/WEP EAP reauthentication and rekeying to use + unencrypted EAPOL frames + * added initial MBO support; number of extensions to WNM BSS Transition + Management + * added support for PBSS/PCP and P2P on 60 GHz + * Interworking: add credential realm to EAP-TLS identity + * fixed EAPOL-Key Request Secure bit to be 1 if PTK is set + * HS 2.0: add support for configuring frame filters + * added POLL_STA command to check connectivity in AP mode + * added initial functionality for location related operations + * started to ignore pmf=1/2 parameter for non-RSN networks + * added wps_disabled=1 network profile parameter to allow AP mode to + be started without enabling WPS + * wpa_cli: added action script support for AP-ENABLED and AP-DISABLED + events + * improved Public Action frame addressing + - add gas_address3 configuration parameter to control Address 3 + behavior + * number of small fixes + 2015-09-27 - v2.5 * fixed P2P validation of SSID element length before copying it [http://w1.fi/security/2015-1/] (CVE-2015-1863) diff --git a/contrib/wpa/wpa_supplicant/README b/contrib/wpa/wpa_supplicant/README index f9c65d2e0ff5..11ab01a9c171 100644 --- a/contrib/wpa/wpa_supplicant/README +++ b/contrib/wpa/wpa_supplicant/README @@ -1,7 +1,7 @@ WPA Supplicant ============== -Copyright (c) 2003-2015, Jouni Malinen and contributors +Copyright (c) 2003-2016, Jouni Malinen and contributors All Rights Reserved. This program is licensed under the BSD license (the one with @@ -72,11 +72,13 @@ Supported WPA/IEEE 802.11i features: * EAP-TTLS/CHAP * EAP-SIM * EAP-AKA + * EAP-AKA' * EAP-PSK * EAP-PAX * EAP-SAKE * EAP-IKEv2 * EAP-GPSK + * EAP-pwd * LEAP (note: requires special support from the driver for IEEE 802.11 authentication) (following methods are supported, but since they do not generate keying @@ -163,18 +165,12 @@ systems. In case of Windows builds, WinPcap is used by default Optional libraries for EAP-TLS, EAP-PEAP, and EAP-TTLS: -- OpenSSL (tested with 0.9.7c and 0.9.7d, and 0.9.8 versions; assumed to +- OpenSSL (tested with 1.0.1 and 1.0.2 versions; assumed to work with most relatively recent versions; this is likely to be available with most distributions, http://www.openssl.org/) - GnuTLS - internal TLSv1 implementation -TLS options for EAP-FAST: -- OpenSSL 0.9.8d _with_ openssl-0.9.8d-tls-extensions.patch applied - (i.e., the default OpenSSL package does not include support for - extensions needed for EAP-FAST) -- internal TLSv1 implementation - One of these libraries is needed when EAP-TLS, EAP-PEAP, EAP-TTLS, or EAP-FAST support is enabled. WPA-PSK mode does not require this or EAPOL/EAP implementation. A configuration file, .config, for compilation is @@ -308,7 +304,7 @@ Following build time configuration options are used to control IEEE 802.1X/EAPOL and EAP state machines and all EAP methods. Including TLS, PEAP, or TTLS will require linking wpa_supplicant with OpenSSL library for TLS implementation. Alternatively, GnuTLS or the internal -TLSv1 implementation can be used for TLS functionaly. +TLSv1 implementation can be used for TLS functionality. CONFIG_IEEE8021X_EAPOL=y CONFIG_EAP_MD5=y @@ -320,15 +316,17 @@ CONFIG_EAP_GTC=y CONFIG_EAP_OTP=y CONFIG_EAP_SIM=y CONFIG_EAP_AKA=y +CONFIG_EAP_AKA_PRIME=y CONFIG_EAP_PSK=y CONFIG_EAP_SAKE=y CONFIG_EAP_GPSK=y CONFIG_EAP_PAX=y CONFIG_EAP_LEAP=y CONFIG_EAP_IKEV2=y +CONFIG_EAP_PWD=y Following option can be used to include GSM SIM/USIM interface for GSM/UMTS -authentication algorithm (for EAP-SIM/EAP-AKA). This requires pcsc-lite +authentication algorithm (for EAP-SIM/EAP-AKA/EAP-AKA'). This requires pcsc-lite (http://www.linuxnet.com/) for smart card access. CONFIG_PCSC=y @@ -409,10 +407,10 @@ Command line options -------------------- usage: - wpa_supplicant [-BddfhKLqqtuvwW] [-P] [-g] \ + wpa_supplicant [-BddfhKLqqtuvW] [-P] [-g] \ [-G] \ -i -c [-C] [-D] [-p] \ - [-b [-N -i -c [-C] [-D] \ + [-b [-MN -i -c [-C] [-D] \ [-p] [-b] [-m] ... options: @@ -435,8 +433,8 @@ options: -q = decrease debugging verbosity (-qq even less) -u = enable DBus control interface -v = show version - -w = wait for interface to be added, if needed -W = wait for a control interface monitor before starting + -M = start describing matching interface -N = start describing new interface -m = Configuration file for the P2P Device @@ -479,6 +477,22 @@ wpa_supplicant \ -c wpa2.conf -i wlan1 -D wext +If the interfaces on which wpa_supplicant is to run are not known or do +not exist, wpa_supplicant can match an interface when it arrives. Each +matched interface is separated with -M argument and the -i argument now +allows for pattern matching. + +As an example, the following command would start wpa_supplicant for a +specific wired interface called lan0, any interface starting with wlan +and lastly any other interface. Each match has its own configuration +file, and for the wired interface a specific driver has also been given. + +wpa_supplicant \ + -M -c wpa_wired.conf -ilan0 -D wired \ + -M -c wpa1.conf -iwlan* \ + -M -c wpa2.conf + + If the interface is added in a Linux bridge (e.g., br0), the bridge interface needs to be configured to wpa_supplicant in addition to the main interface: @@ -500,7 +514,7 @@ reloading can be triggered with 'wpa_cli reconfigure' command. Configuration file can include one or more network blocks, e.g., one for each used SSID. wpa_supplicant will automatically select the best -betwork based on the order of network blocks in the configuration +network based on the order of network blocks in the configuration file, network security level (WPA/WPA2 is preferred), and signal strength. @@ -792,7 +806,7 @@ addresses, etc. One wpa_cli process in "action" mode needs to be started for each interface. For example, the following command starts wpa_cli for the -default ingterface (-i can be used to select the interface in case of +default interface (-i can be used to select the interface in case of more than one interface being used at the same time): wpa_cli -a/sbin/wpa_action.sh -B @@ -1008,8 +1022,8 @@ event message is indicated that the external processing can start. Once the operation has been completed, "RADIO_WORK done " is used to indicate that to wpa_supplicant. This allows other radio works to be performed. If this command is forgotten (e.g., due to the external -program terminating), wpa_supplicant will time out the radio owrk item -and send "EXT-RADIO-WORK-TIMEOUT " event ot indicate that this has +program terminating), wpa_supplicant will time out the radio work item +and send "EXT-RADIO-WORK-TIMEOUT " event to indicate that this has happened. "RADIO_WORK done " can also be used to cancel items that have not yet been started. diff --git a/contrib/wpa/wpa_supplicant/README-HS20 b/contrib/wpa/wpa_supplicant/README-HS20 index 161dc06a2ddd..e4eed2074f91 100644 --- a/contrib/wpa/wpa_supplicant/README-HS20 +++ b/contrib/wpa/wpa_supplicant/README-HS20 @@ -229,7 +229,7 @@ Credentials can be pre-configured for automatic network selection: # # sp_priority: Credential priority within a provisioning SP # This is the priority of the credential among all credentials -# provisionined by the same SP (i.e., for entries that have identical +# provisioned by the same SP (i.e., for entries that have identical # provisioning_sp value). The range of this priority is 0-255 with 0 # being the highest and 255 the lower priority. # @@ -564,3 +564,68 @@ OK <3>RX-ANQP 02:00:00:00:01:00 Roaming Consortium list <3>RX-HS20-ANQP 02:00:00:00:01:00 HS Capability List <3>ANQP fetch completed + + +Hotspot 2.0 Rel 2 online signup and OSEN +---------------------------------------- + +Following parameters can be used to create a network profile for +link-layer protected Hotspot 2.0 online signup connection with +OSEN. Note that ssid and identify (NAI) values need to be set based on +the information for the selected provider in the OSU Providers list +ANQP-element. + +network={ + ssid="HS 2.0 OSU" + proto=OSEN + key_mgmt=OSEN + pairwise=CCMP + group=GTK_NOT_USED + eap=WFA-UNAUTH-TLS + identity="anonymous@example.com" + ca_cert="osu-ca.pem" + ocsp=2 +} + + +Hotspot 2.0 connection with external network selection +------------------------------------------------------ + +When an component controlling wpa_supplicant takes care of Interworking +network selection, following configuration and network profile +parameters can be used to configure a temporary network profile for a +Hotspot 2.0 connection (e.g., with SET, ADD_NETWORK, SET_NETWORK, and +SELECT_NETWORK control interface commands): + +interworking=1 +hs20=1 +auto_interworking=0 + +network={ + ssid="test-hs20" + proto=RSN + key_mgmt=WPA-EAP + pairwise=CCMP + anonymous_identity="anonymous@example.com" + identity="hs20-test@example.com" + password="password" + ca_cert="ca.pem" + eap=TTLS + phase2="auth=MSCHAPV2" + update_identifier=54321 + #ocsp=2 +} + + +These parameters are set based on the PPS MO credential and/or NAI Realm +list ANQP-element: + +anonymous_identity: Credential/UsernamePassword/Username with username part + replaced with "anonymous" +identity: Credential/UsernamePassword/Username +password: Credential/UsernamePassword/Password +update_identifier: PPS/UpdateIdentifier +ca_cert: from the downloaded trust root based on PPS information +eap: Credential/UsernamePassword/EAPMethod or NAI Realm list +phase2: Credential/UsernamePassword/EAPMethod or NAI Realm list +ocsp: Credential/CheckAAAServerCertStatus diff --git a/contrib/wpa/wpa_supplicant/README-P2P b/contrib/wpa/wpa_supplicant/README-P2P index 6a5b032124a9..23ac7fa056a4 100644 --- a/contrib/wpa/wpa_supplicant/README-P2P +++ b/contrib/wpa/wpa_supplicant/README-P2P @@ -151,6 +151,7 @@ join-a-group style PD instead of GO Negotiation style PD. p2p_connect [display|keypad|p2ps] [persistent|persistent=] [join|auth] [go_intent=<0..15>] [freq=] [ht40] [vht] [provdisc] [auto] + [ssid=] Start P2P group formation with a discovered P2P peer. This includes optional group owner negotiation, group interface setup, provisioning, @@ -195,11 +196,17 @@ connection. out whether the peer device is operating as a GO and if so, use join-a-group operation rather than GO Negotiation. +"ssid=" can be used to specify the Group SSID for join +operations. This allows the P2P Client interface to filter scan results +based on SSID to avoid selecting an incorrect BSS entry in case the same +P2P Device or Interface address have been used in multiple groups +recently. + P2PS attribute changes to p2p_connect command: P2PS supports two WPS provisioning methods namely PIN method and P2PS default. -The remaining paramters hold same role as in legacy P2P. In case of P2PS default -config method "p2ps" keyword is added in p2p_connect command. +The remaining parameters hold same role as in legacy P2P. In case of P2PS +default config method "p2ps" keyword is added in p2p_connect command. For example: p2p_connect 02:0a:f5:85:11:00 12345670 p2ps persistent join @@ -281,7 +288,7 @@ group interface is used as a parameter for this command. p2p_cancel Cancel an ongoing P2P group formation and joining-a-group related -operation. This operations unauthorizes the specific peer device (if any +operation. This operation unauthorizes the specific peer device (if any had been authorized to start group formation), stops P2P find (if in progress), stops pending operations for join-a-group, and removes the P2P group interface (if one was used) that is in the WPS provisioning @@ -633,12 +640,17 @@ p2p_set managed <0/1> Disable/enable managed P2P Device operations. This is disabled by default. -p2p_set listen_channel <1/6/11> +p2p_set listen_channel [] Set P2P Listen channel. This is mainly meant for testing purposes and changing the Listen channel during normal operations can result in protocol failures. +When specifying a social channel on the 2.4 GHz band (1/6/11) there is +no need to specify the operating class since it defaults to 81. When +specifying a social channel on the 60 GHz band (2), specify the 60 GHz +operating class (180). + p2p_set ssid_postfix Set postfix string to be added to the automatically generated P2P SSID @@ -650,7 +662,7 @@ p2p_set per_sta_psk <0/1> Disabled(default)/enables use of per-client PSK in the P2P groups. This can be used to request GO to assign a unique PSK for each client during WPS provisioning. When enabled, this allow clients to be removed from -the group securily with p2p_remove_client command since that client's +the group securely with p2p_remove_client command since that client's PSK is removed at the same time to prevent it from connecting back using the old PSK. When per-client PSK is not used, the client can still be disconnected, but it will be able to re-join the group since the PSK it diff --git a/contrib/wpa/wpa_supplicant/README-Windows.txt b/contrib/wpa/wpa_supplicant/README-Windows.txt new file mode 100644 index 000000000000..7288abd9a161 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/README-Windows.txt @@ -0,0 +1,299 @@ +wpa_supplicant for Windows +========================== + +Copyright (c) 2003-2009, Jouni Malinen and contributors +All Rights Reserved. + +This program is licensed under the BSD license (the one with +advertisement clause removed). + + +wpa_supplicant has support for being used as a WPA/WPA2/IEEE 802.1X +Supplicant on Windows. The current port requires that WinPcap +(http://winpcap.polito.it/) is installed for accessing packets and the +driver interface. Both release versions 3.0 and 3.1 are supported. + +The current port is still somewhat experimental. It has been tested +mainly on Windows XP (SP2) with limited set of NDIS drivers. In +addition, the current version has been reported to work with Windows +2000. + +All security modes have been verified to work (at least complete +authentication and successfully ping a wired host): +- plaintext +- static WEP / open system authentication +- static WEP / shared key authentication +- IEEE 802.1X with dynamic WEP keys +- WPA-PSK, TKIP, CCMP, TKIP+CCMP +- WPA-EAP, TKIP, CCMP, TKIP+CCMP +- WPA2-PSK, TKIP, CCMP, TKIP+CCMP +- WPA2-EAP, TKIP, CCMP, TKIP+CCMP + + +Building wpa_supplicant with mingw +---------------------------------- + +The default build setup for wpa_supplicant is to use MinGW and +cross-compiling from Linux to MinGW/Windows. It should also be +possible to build this under Windows using the MinGW tools, but that +is not tested nor supported and is likely to require some changes to +the Makefile unless cygwin is used. + + +Building wpa_supplicant with MSVC +--------------------------------- + +wpa_supplicant can be built with Microsoft Visual C++ compiler. This +has been tested with Microsoft Visual C++ Toolkit 2003 and Visual +Studio 2005 using the included nmake.mak as a Makefile for nmake. IDE +can also be used by creating a project that includes the files and +defines mentioned in nmake.mak. Example VS2005 solution and project +files are included in vs2005 subdirectory. This can be used as a +starting point for building the programs with VS2005 IDE. Visual Studio +2008 Express Edition is also able to use these project files. + +WinPcap development package is needed for the build and this can be +downloaded from http://www.winpcap.org/install/bin/WpdPack_4_0_2.zip. The +default nmake.mak expects this to be unpacked into C:\dev\WpdPack so +that Include and Lib directories are in this directory. The files can be +stored elsewhere as long as the WINPCAPDIR in nmake.mak is updated to +match with the selected directory. In case a project file in the IDE is +used, these Include and Lib directories need to be added to project +properties as additional include/library directories. + +OpenSSL source package can be downloaded from +http://www.openssl.org/source/openssl-0.9.8i.tar.gz and built and +installed following instructions in INSTALL.W32. Note that if EAP-FAST +support will be included in the wpa_supplicant, OpenSSL needs to be +patched to# support it openssl-0.9.8i-tls-extensions.patch. The example +nmake.mak file expects OpenSSL to be installed into C:\dev\openssl, but +this directory can be modified by changing OPENSSLDIR variable in +nmake.mak. + +If you do not need EAP-FAST support, you may also be able to use Win32 +binary installation package of OpenSSL from +http://www.slproweb.com/products/Win32OpenSSL.html instead of building +the library yourself. In this case, you will need to copy Include and +Lib directories in suitable directory, e.g., C:\dev\openssl for the +default nmake.mak. Copy {Win32OpenSSLRoot}\include into +C:\dev\openssl\include and make C:\dev\openssl\lib subdirectory with +files from {Win32OpenSSLRoot}\VC (i.e., libeay*.lib and ssleay*.lib). +This will end up using dynamically linked OpenSSL (i.e., .dll files are +needed) for it. Alternative, you can copy files from +{Win32OpenSSLRoot}\VC\static to create a static build (no OpenSSL .dll +files needed). + + +Building wpa_supplicant for cygwin +---------------------------------- + +wpa_supplicant can be built for cygwin by installing the needed +development packages for cygwin. This includes things like compiler, +make, openssl development package, etc. In addition, developer's pack +for WinPcap (WPdpack.zip) from +http://winpcap.polito.it/install/default.htm is needed. + +.config file should enable only one driver interface, +CONFIG_DRIVER_NDIS. In addition, include directories may need to be +added to match the system. An example configuration is available in +defconfig. The library and include files for WinPcap will either need +to be installed in compiler/linker default directories or their +location will need to be adding to .config when building +wpa_supplicant. + +Othen than this, the build should be more or less identical to Linux +version, i.e., just run make after having created .config file. An +additional tool, win_if_list.exe, can be built by running "make +win_if_list". + + +Building wpa_gui +---------------- + +wpa_gui uses Qt application framework from Trolltech. It can be built +with the open source version of Qt4 and MinGW. Following commands can +be used to build the binary in the Qt 4 Command Prompt: + +# go to the root directory of wpa_supplicant source code +cd wpa_gui-qt4 +qmake -o Makefile wpa_gui.pro +make +# the wpa_gui.exe binary is created into 'release' subdirectory + + +Using wpa_supplicant for Windows +-------------------------------- + +wpa_supplicant, wpa_cli, and wpa_gui behave more or less identically to +Linux version, so instructions in README and example wpa_supplicant.conf +should be applicable for most parts. In addition, there is another +version of wpa_supplicant, wpasvc.exe, which can be used as a Windows +service and which reads its configuration from registry instead of +text file. + +When using access points in "hidden SSID" mode, ap_scan=2 mode need to +be used (see wpa_supplicant.conf for more information). + +Windows NDIS/WinPcap uses quite long interface names, so some care +will be needed when starting wpa_supplicant. Alternatively, the +adapter description can be used as the interface name which may be +easier since it is usually in more human-readable +format. win_if_list.exe can be used to find out the proper interface +name. + +Example steps in starting up wpa_supplicant: + +# win_if_list.exe +ifname: \Device\NPF_GenericNdisWanAdapter +description: Generic NdisWan adapter + +ifname: \Device\NPF_{769E012B-FD17-4935-A5E3-8090C38E25D2} +description: Atheros Wireless Network Adapter (Microsoft's Packet Scheduler) + +ifname: \Device\NPF_{732546E7-E26C-48E3-9871-7537B020A211} +description: Intel 8255x-based Integrated Fast Ethernet (Microsoft's Packet Scheduler) + + +Since the example configuration used Atheros WLAN card, the middle one +is the correct interface in this case. The interface name for -i +command line option is the full string following "ifname:" (the +"\Device\NPF_" prefix can be removed). In other words, wpa_supplicant +would be started with the following command: + +# wpa_supplicant.exe -i'{769E012B-FD17-4935-A5E3-8090C38E25D2}' -c wpa_supplicant.conf -d + +-d optional enables some more debugging (use -dd for even more, if +needed). It can be left out if debugging information is not needed. + +With the alternative mechanism for selecting the interface, this +command has identical results in this case: + +# wpa_supplicant.exe -iAtheros -c wpa_supplicant.conf -d + + +Simple configuration example for WPA-PSK: + +#ap_scan=2 +ctrl_interface= +network={ + ssid="test" + key_mgmt=WPA-PSK + proto=WPA + pairwise=TKIP + psk="secret passphrase" +} + +(remove '#' from the comment out ap_scan line to enable mode in which +wpa_supplicant tries to associate with the SSID without doing +scanning; this allows APs with hidden SSIDs to be used) + + +wpa_cli.exe and wpa_gui.exe can be used to interact with the +wpa_supplicant.exe program in the same way as with Linux. Note that +ctrl_interface is using UNIX domain sockets when built for cygwin, but +the native build for Windows uses named pipes and the contents of the +ctrl_interface configuration item is used to control access to the +interface. Anyway, this variable has to be included in the configuration +to enable the control interface. + + +Example SDDL string formats: + +(local admins group has permission, but nobody else): + +ctrl_interface=SDDL=D:(A;;GA;;;BA) + +("A" == "access allowed", "GA" == GENERIC_ALL == all permissions, and +"BA" == "builtin administrators" == the local admins. The empty fields +are for flags and object GUIDs, none of which should be required in this +case.) + +(local admins and the local "power users" group have permissions, +but nobody else): + +ctrl_interface=SDDL=D:(A;;GA;;;BA)(A;;GA;;;PU) + +(One ACCESS_ALLOWED ACE for GENERIC_ALL for builtin administrators, and +one ACCESS_ALLOWED ACE for GENERIC_ALL for power users.) + +(close to wide open, but you have to be a valid user on +the machine): + +ctrl_interface=SDDL=D:(A;;GA;;;AU) + +(One ACCESS_ALLOWED ACE for GENERIC_ALL for the "authenticated users" +group.) + +This one would allow absolutely everyone (including anonymous +users) -- this is *not* recommended, since named pipes can be attached +to from anywhere on the network (i.e. there's no "this machine only" +like there is with 127.0.0.1 sockets): + +ctrl_interface=SDDL=D:(A;;GA;;;BU)(A;;GA;;;AN) + +(BU == "builtin users", "AN" == "anonymous") + +See also [1] for the format of ACEs, and [2] for the possible strings +that can be used for principal names. + +[1] +http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthz/security/ace_strings.asp +[2] +http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthz/security/sid_strings.asp + + +Starting wpa_supplicant as a Windows service (wpasvc.exe) +--------------------------------------------------------- + +wpa_supplicant can be started as a Windows service by using wpasvc.exe +program that is alternative build of wpa_supplicant.exe. Most of the +core functionality of wpasvc.exe is identical to wpa_supplicant.exe, +but it is using Windows registry for configuration information instead +of a text file and command line parameters. In addition, it can be +registered as a service that can be started automatically or manually +like any other Windows service. + +The root of wpa_supplicant configuration in registry is +HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant. This level includes global +parameters and a 'interfaces' subkey with all the interface configuration +(adapter to confname mapping). Each such mapping is a subkey that has +'adapter', 'config', and 'ctrl_interface' values. + +This program can be run either as a normal command line application, +e.g., for debugging, with 'wpasvc.exe app' or as a Windows service. +Service need to be registered with 'wpasvc.exe reg '. Alternatively, 'wpasvc.exe reg' can be used to register +the service with the current location of wpasvc.exe. After this, wpasvc +can be started like any other Windows service (e.g., 'net start wpasvc') +or it can be configured to start automatically through the Services tool +in administrative tasks. The service can be unregistered with +'wpasvc.exe unreg'. + +If the service is set to start during system bootup to make the +network connection available before any user has logged in, there may +be a long (half a minute or so) delay in starting up wpa_supplicant +due to WinPcap needing a driver called "Network Monitor Driver" which +is started by default on demand. + +To speed up wpa_supplicant start during system bootup, "Network +Monitor Driver" can be configured to be started sooner by setting its +startup type to System instead of the default Demand. To do this, open +up Device Manager, select Show Hidden Devices, expand the "Non +Plug-and-Play devices" branch, double click "Network Monitor Driver", +go to the Driver tab, and change the Demand setting to System instead. + +Configuration data is in HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\configs +key. Each configuration profile has its own key under this. In terms of text +files, each profile would map to a separate text file with possibly multiple +networks. Under each profile, there is a networks key that lists all +networks as a subkey. Each network has set of values in the same way as +network block in the configuration file. In addition, blobs subkey has +possible blobs as values. + +HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\configs\test\networks\0000 + ssid="example" + key_mgmt=WPA-PSK + +See win_example.reg for an example on how to setup wpasvc.exe +parameters in registry. It can also be imported to registry as a +starting point for the configuration. diff --git a/contrib/wpa/wpa_supplicant/android.config b/contrib/wpa/wpa_supplicant/android.config new file mode 100644 index 000000000000..02505bb991aa --- /dev/null +++ b/contrib/wpa/wpa_supplicant/android.config @@ -0,0 +1,492 @@ +# Example wpa_supplicant build time configuration +# +# This file lists the configuration options that are used when building the +# hostapd binary. All lines starting with # are ignored. Configuration option +# lines must be commented out complete, if they are not to be included, i.e., +# just setting VARIABLE=n is not disabling that variable. +# +# This file is included in Makefile, so variables like CFLAGS and LIBS can also +# be modified from here. In most cases, these lines should use += in order not +# to override previous values of the variables. + + +# Uncomment following two lines and fix the paths if you have installed OpenSSL +# or GnuTLS in non-default location +#CFLAGS += -I/usr/local/openssl/include +#LIBS += -L/usr/local/openssl/lib + +# Some Red Hat versions seem to include kerberos header files from OpenSSL, but +# the kerberos files are not in the default include path. Following line can be +# used to fix build issues on such systems (krb5.h not found). +#CFLAGS += -I/usr/include/kerberos + +# Driver interface for generic Linux wireless extensions +# Note: WEXT is deprecated in the current Linux kernel version and no new +# functionality is added to it. nl80211-based interface is the new +# replacement for WEXT and its use allows wpa_supplicant to properly control +# the driver to improve existing functionality like roaming and to support new +# functionality. +#CONFIG_DRIVER_WEXT=y + +# Driver interface for Linux drivers using the nl80211 kernel interface +#CONFIG_DRIVER_NL80211=y +CONFIG_LIBNL20=y + +# QCA vendor extensions to nl80211 +CONFIG_DRIVER_NL80211_QCA=y + +# Driver interface for FreeBSD net80211 layer (e.g., Atheros driver) +#CONFIG_DRIVER_BSD=y +#CFLAGS += -I/usr/local/include +#LIBS += -L/usr/local/lib +#LIBS_p += -L/usr/local/lib +#LIBS_c += -L/usr/local/lib + +# Driver interface for Windows NDIS +#CONFIG_DRIVER_NDIS=y +#CFLAGS += -I/usr/include/w32api/ddk +#LIBS += -L/usr/local/lib +# For native build using mingw +#CONFIG_NATIVE_WINDOWS=y +# Additional directories for cross-compilation on Linux host for mingw target +#CFLAGS += -I/opt/mingw/mingw32/include/ddk +#LIBS += -L/opt/mingw/mingw32/lib +#CC=mingw32-gcc +# By default, driver_ndis uses WinPcap for low-level operations. This can be +# replaced with the following option which replaces WinPcap calls with NDISUIO. +# However, this requires that WZC is disabled (net stop wzcsvc) before starting +# wpa_supplicant. +# CONFIG_USE_NDISUIO=y + +# Driver interface for wired Ethernet drivers +#CONFIG_DRIVER_WIRED=y + +# Driver interface for the Broadcom RoboSwitch family +#CONFIG_DRIVER_ROBOSWITCH=y + +# Driver interface for no driver (e.g., WPS ER only) +#CONFIG_DRIVER_NONE=y + +# Solaris libraries +#LIBS += -lsocket -ldlpi -lnsl +#LIBS_c += -lsocket + +# Enable IEEE 802.1X Supplicant (automatically included if any EAP method is +# included) +CONFIG_IEEE8021X_EAPOL=y + +# EAP-MD5 +CONFIG_EAP_MD5=y + +# EAP-MSCHAPv2 +CONFIG_EAP_MSCHAPV2=y + +# EAP-TLS +CONFIG_EAP_TLS=y + +# EAL-PEAP +CONFIG_EAP_PEAP=y + +# EAP-TTLS +CONFIG_EAP_TTLS=y + +# EAP-FAST +# Note: Default OpenSSL package does not include support for all the +# functionality needed for EAP-FAST. If EAP-FAST is enabled with OpenSSL, +# the OpenSSL library must be patched (openssl-0.9.8d-tls-extensions.patch) +# to add the needed functions. +#CONFIG_EAP_FAST=y + +# EAP-GTC +CONFIG_EAP_GTC=y + +# EAP-OTP +CONFIG_EAP_OTP=y + +# EAP-SIM (enable CONFIG_PCSC, if EAP-SIM is used) +CONFIG_EAP_SIM=y + +# EAP-PSK (experimental; this is _not_ needed for WPA-PSK) +#CONFIG_EAP_PSK=y + +# EAP-pwd (secure authentication using only a password) +CONFIG_EAP_PWD=y + +# EAP-PAX +#CONFIG_EAP_PAX=y + +# LEAP +CONFIG_EAP_LEAP=y + +# EAP-AKA (enable CONFIG_PCSC, if EAP-AKA is used) +CONFIG_EAP_AKA=y + +# EAP-AKA' (enable CONFIG_PCSC, if EAP-AKA' is used). +# This requires CONFIG_EAP_AKA to be enabled, too. +CONFIG_EAP_AKA_PRIME=y + +# Enable USIM simulator (Milenage) for EAP-AKA +#CONFIG_USIM_SIMULATOR=y + +# EAP-SAKE +#CONFIG_EAP_SAKE=y + +# EAP-GPSK +#CONFIG_EAP_GPSK=y +# Include support for optional SHA256 cipher suite in EAP-GPSK +#CONFIG_EAP_GPSK_SHA256=y + +# EAP-TNC and related Trusted Network Connect support (experimental) +#CONFIG_EAP_TNC=y + +# Wi-Fi Protected Setup (WPS) +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 +# registrar. +#CONFIG_WPS_REG_DISABLE_OPEN=y +# Enable WPS support with NFC config method +CONFIG_WPS_NFC=y + +# EAP-IKEv2 +#CONFIG_EAP_IKEV2=y + +# PKCS#12 (PFX) support (used to read private key and certificate file from +# a file that usually has extension .p12 or .pfx) +CONFIG_PKCS12=y + +# Smartcard support (i.e., private key on a smartcard), e.g., with openssl +# engine. +CONFIG_SMARTCARD=y + +# PC/SC interface for smartcards (USIM, GSM SIM) +# Enable this if EAP-SIM or EAP-AKA is included +#CONFIG_PCSC=y + +# Support HT overrides (disable HT/HT40, mask MCS rates, etc.) +#CONFIG_HT_OVERRIDES=y + +# Support VHT overrides (disable VHT, mask MCS rates, etc.) +#CONFIG_VHT_OVERRIDES=y + +# Development testing +#CONFIG_EAPOL_TEST=y + +# Select control interface backend for external programs, e.g, wpa_cli: +# unix = UNIX domain sockets (default for Linux/*BSD) +# udp = UDP sockets using localhost (127.0.0.1) +# named_pipe = Windows Named Pipe (default for Windows) +# udp-remote = UDP sockets with remote access (only for tests systems/purpose) +# y = use default (backwards compatibility) +# If this option is commented out, control interface is not included in the +# build. +CONFIG_CTRL_IFACE=y + +# Include support for GNU Readline and History Libraries in wpa_cli. +# When building a wpa_cli binary for distribution, please note that these +# libraries are licensed under GPL and as such, BSD license may not apply for +# the resulting binary. +#CONFIG_READLINE=y + +# Include internal line edit mode in wpa_cli. This can be used as a replacement +# for GNU Readline to provide limited command line editing and history support. +CONFIG_WPA_CLI_EDIT=y + +# Remove debugging code that is printing out debug message to stdout. +# This can be used to reduce the size of the wpa_supplicant considerably +# if debugging code is not needed. The size reduction can be around 35% +# (e.g., 90 kB). +#CONFIG_NO_STDOUT_DEBUG=y + +# Remove WPA support, e.g., for wired-only IEEE 802.1X supplicant, to save +# 35-50 kB in code size. +#CONFIG_NO_WPA=y + +# Remove IEEE 802.11i/WPA-Personal ASCII passphrase support +# This option can be used to reduce code size by removing support for +# converting ASCII passphrases into PSK. If this functionality is removed, the +# PSK can only be configured as the 64-octet hexstring (e.g., from +# wpa_passphrase). This saves about 0.5 kB in code size. +#CONFIG_NO_WPA_PASSPHRASE=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 + +# Select configuration backend: +# file = text file (e.g., wpa_supplicant.conf; note: the configuration file +# path is given on command line, not here; this option is just used to +# select the backend that allows configuration files to be used) +# winreg = Windows registry (see win_example.reg for an example) +CONFIG_BACKEND=file + +# Remove configuration write functionality (i.e., to allow the configuration +# file to be updated based on runtime configuration changes). The runtime +# configuration can still be changed, the changes are just not going to be +# persistent over restarts. This option can be used to reduce code size by +# about 3.5 kB. +#CONFIG_NO_CONFIG_WRITE=y + +# Remove support for configuration blobs to reduce code size by about 1.5 kB. +#CONFIG_NO_CONFIG_BLOBS=y + +# Select program entry point implementation: +# main = UNIX/POSIX like main() function (default) +# main_winsvc = Windows service (read parameters from registry) +# main_none = Very basic example (development use only) +#CONFIG_MAIN=main + +# Select wrapper for operating system and C library specific functions +# unix = UNIX/POSIX like systems (default) +# win32 = Windows systems +# none = Empty template +CONFIG_OS=unix + +# Select event loop implementation +# eloop = select() loop (default) +# eloop_win = Windows events and WaitForMultipleObject() loop +CONFIG_ELOOP=eloop + +# Should we use poll instead of select? Select is used by default. +#CONFIG_ELOOP_POLL=y + +# Should we use epoll instead of select? Select is used by default. +#CONFIG_ELOOP_EPOLL=y + +# Select layer 2 packet implementation +# linux = Linux packet socket (default) +# pcap = libpcap/libdnet/WinPcap +# freebsd = FreeBSD libpcap +# winpcap = WinPcap with receive thread +# ndis = Windows NDISUIO (note: requires CONFIG_USE_NDISUIO=y) +# none = Empty template +CONFIG_L2_PACKET=linux + +# PeerKey handshake for Station to Station Link (IEEE 802.11e DLS) +CONFIG_PEERKEY=y + +# IEEE 802.11w (management frame protection), also known as PMF +# Driver support is also needed for IEEE 802.11w. +CONFIG_IEEE80211W=y + +# Select TLS implementation +# openssl = OpenSSL (default) +# gnutls = GnuTLS +# internal = Internal TLSv1 implementation (experimental) +# none = Empty template +#CONFIG_TLS=openssl + +# TLS-based EAP methods require at least TLS v1.0. Newer version of TLS (v1.1) +# can be enabled to get a stronger construction of messages when block ciphers +# are used. It should be noted that some existing TLS v1.0 -based +# implementation may not be compatible with TLS v1.1 message (ClientHello is +# sent prior to negotiating which version will be used) +#CONFIG_TLSV11=y + +# TLS-based EAP methods require at least TLS v1.0. Newer version of TLS (v1.2) +# can be enabled to enable use of stronger crypto algorithms. It should be +# noted that some existing TLS v1.0 -based implementation may not be compatible +# with TLS v1.2 message (ClientHello is sent prior to negotiating which version +# will be used) +#CONFIG_TLSV12=y + +# If CONFIG_TLS=internal is used, additional library and include paths are +# needed for LibTomMath. Alternatively, an integrated, minimal version of +# LibTomMath can be used. See beginning of libtommath.c for details on benefits +# and drawbacks of this option. +#CONFIG_INTERNAL_LIBTOMMATH=y +#ifndef CONFIG_INTERNAL_LIBTOMMATH +#LTM_PATH=/usr/src/libtommath-0.39 +#CFLAGS += -I$(LTM_PATH) +#LIBS += -L$(LTM_PATH) +#LIBS_p += -L$(LTM_PATH) +#endif +# At the cost of about 4 kB of additional binary size, the internal LibTomMath +# can be configured to include faster routines for exptmod, sqr, and div to +# speed up DH and RSA calculation considerably +#CONFIG_INTERNAL_LIBTOMMATH_FAST=y + +# Include NDIS event processing through WMI into wpa_supplicant/wpasvc. +# This is only for Windows builds and requires WMI-related header files and +# WbemUuid.Lib from Platform SDK even when building with MinGW. +#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 + +# Add introspection support for new DBus control interface +#CONFIG_CTRL_IFACE_DBUS_INTRO=y + +# Add support for Binder control interface +# Only applicable for Android platforms. +#CONFIG_CTRL_IFACE_BINDER=y + +# Add support for loading EAP methods dynamically as shared libraries. +# When this option is enabled, each EAP method can be either included +# statically (CONFIG_EAP_=y) or dynamically (CONFIG_EAP_=dyn). +# Dynamic EAP methods are build as shared objects (eap_*.so) and they need to +# be loaded in the beginning of the wpa_supplicant configuration file +# (see load_dynamic_eap parameter in the example file) before being used in +# the network blocks. +# +# Note that some shared parts of EAP methods are included in the main program +# and in order to be able to use dynamic EAP methods using these parts, the +# main program must have been build with the EAP method enabled (=y or =dyn). +# This means that EAP-TLS/PEAP/TTLS/FAST cannot be added as dynamic libraries +# unless at least one of them was included in the main build to force inclusion +# of the shared code. Similarly, at least one of EAP-SIM/AKA must be included +# in the main build to be able to load these methods dynamically. +# +# Please also note that using dynamic libraries will increase the total binary +# size. Thus, it may not be the best option for targets that have limited +# amount of memory/flash. +#CONFIG_DYNAMIC_EAP_METHODS=y + +# IEEE Std 802.11r-2008 (Fast BSS Transition) +CONFIG_IEEE80211R=y + +# Add support for writing debug log to a file (/tmp/wpa_supplicant-log-#.txt) +#CONFIG_DEBUG_FILE=y + +# Send debug messages to syslog instead of stdout +#CONFIG_DEBUG_SYSLOG=y +# Set syslog facility for debug messages +#CONFIG_DEBUG_SYSLOG_FACILITY=LOG_DAEMON + +# Add support for sending all debug messages (regardless of debug verbosity) +# to the Linux kernel tracing facility. This helps debug the entire stack by +# making it easy to record everything happening from the driver up into the +# same file, e.g., using trace-cmd. +#CONFIG_DEBUG_LINUX_TRACING=y + +# Add support for writing debug log to Android logcat instead of standard +# output +CONFIG_ANDROID_LOG=y + +# Enable privilege separation (see README 'Privilege separation' for details) +#CONFIG_PRIVSEP=y + +# Enable mitigation against certain attacks against TKIP by delaying Michael +# MIC error reports by a random amount of time between 0 and 60 seconds +#CONFIG_DELAYED_MIC_ERROR_REPORT=y + +# Enable tracing code for developer debugging +# This tracks use of memory allocations and other registrations and reports +# incorrect use with a backtrace of call (or allocation) location. +#CONFIG_WPA_TRACE=y +# For BSD, uncomment these. +#LIBS += -lexecinfo +#LIBS_p += -lexecinfo +#LIBS_c += -lexecinfo + +# Use libbfd to get more details for developer debugging +# This enables use of libbfd to get more detailed symbols for the backtraces +# generated by CONFIG_WPA_TRACE=y. +#CONFIG_WPA_TRACE_BFD=y +# For BSD, uncomment these. +#LIBS += -lbfd -liberty -lz +#LIBS_p += -lbfd -liberty -lz +#LIBS_c += -lbfd -liberty -lz + +# wpa_supplicant depends on strong random number generation being available +# from the operating system. os_get_random() function is used to fetch random +# data when needed, e.g., for key generation. On Linux and BSD systems, this +# works by reading /dev/urandom. It should be noted that the OS entropy pool +# needs to be properly initialized before wpa_supplicant is started. This is +# important especially on embedded devices that do not have a hardware random +# number generator and may by default start up with minimal entropy available +# for random number generation. +# +# As a safety net, wpa_supplicant is by default trying to internally collect +# additional entropy for generating random data to mix in with the data fetched +# from the OS. This by itself is not considered to be very strong, but it may +# help in cases where the system pool is not initialized properly. However, it +# is very strongly recommended that the system pool is initialized with enough +# entropy either by using hardware assisted random number generator or by +# storing state over device reboots. +# +# wpa_supplicant can be configured to maintain its own entropy store over +# restarts to enhance random number generation. This is not perfect, but it is +# much more secure than using the same sequence of random numbers after every +# reboot. This can be enabled with -e command line option. The +# specified file needs to be readable and writable by wpa_supplicant. +# +# If the os_get_random() is known to provide strong random data (e.g., on +# Linux/BSD, the board in question is known to have reliable source of random +# data from /dev/urandom), the internal wpa_supplicant random pool can be +# disabled. This will save some in binary size and CPU use. However, this +# should only be considered for builds that are known to be used on devices +# that meet the requirements described above. +#CONFIG_NO_RANDOM_POOL=y + +# IEEE 802.11n (High Throughput) support (mainly for AP mode) +CONFIG_IEEE80211N=y + +# Wireless Network Management (IEEE Std 802.11v-2011) +# Note: This is experimental and not complete implementation. +CONFIG_WNM=y + +# Interworking (IEEE 802.11u) +# 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 + +# Hotspot 2.0 +CONFIG_HS20=y + +# Disable roaming in wpa_supplicant +CONFIG_NO_ROAMING=y + +# AP mode operations with wpa_supplicant +# This can be used for controlling AP mode operations with wpa_supplicant. It +# 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 + +# 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 + +# Enable TDLS support +CONFIG_TDLS=y + +# Wi-Fi Direct +# This can be used to enable Wi-Fi Direct extensions for P2P using an external +# program to control the additional information exchanges in the messages. +CONFIG_WIFI_DISPLAY=y + +# Autoscan +# This can be used to enable automatic scan support in wpa_supplicant. +# See wpa_supplicant.conf for more information on autoscan usage. +# +# Enabling directly a module will enable autoscan support. +# For exponential module: +#CONFIG_AUTOSCAN_EXPONENTIAL=y +# For periodic module: +#CONFIG_AUTOSCAN_PERIODIC=y + +# Password (and passphrase, etc.) backend for external storage +# These optional mechanisms can be used to add support for storing passwords +# and other secrets in external (to wpa_supplicant) location. This allows, for +# example, operating system specific key storage to be used +# +# External password backend for testing purposes (developer use) +#CONFIG_EXT_PASSWORD_TEST=y + +# Enable Fast Session Transfer (FST) +#CONFIG_FST=y + +# Support Multi Band Operation +#CONFIG_MBO=y + +include $(wildcard $(LOCAL_PATH)/android_config_*.inc) diff --git a/contrib/wpa/wpa_supplicant/ap.c b/contrib/wpa/wpa_supplicant/ap.c index 7a4f4cf4fbe9..5afb772ba192 100644 --- a/contrib/wpa/wpa_supplicant/ap.c +++ b/contrib/wpa/wpa_supplicant/ap.c @@ -56,12 +56,32 @@ static void wpas_conf_ap_vht(struct wpa_supplicant *wpa_s, if (!conf->secondary_channel) goto no_vht; - center_chan = wpas_p2p_get_vht80_center(wpa_s, mode, channel); + switch (conf->vht_oper_chwidth) { + case VHT_CHANWIDTH_80MHZ: + case VHT_CHANWIDTH_80P80MHZ: + center_chan = wpas_p2p_get_vht80_center(wpa_s, mode, channel); + break; + case VHT_CHANWIDTH_160MHZ: + center_chan = wpas_p2p_get_vht160_center(wpa_s, mode, channel); + break; + default: + /* + * conf->vht_oper_chwidth might not be set for non-P2P GO cases, + * try oper_cwidth 160 MHz first then VHT 80 MHz, if 160 MHz is + * not supported. + */ + conf->vht_oper_chwidth = VHT_CHANWIDTH_160MHZ; + center_chan = wpas_p2p_get_vht160_center(wpa_s, mode, channel); + if (!center_chan) { + conf->vht_oper_chwidth = VHT_CHANWIDTH_80MHZ; + center_chan = wpas_p2p_get_vht80_center(wpa_s, mode, + channel); + } + break; + } if (!center_chan) goto no_vht; - /* Use 80 MHz channel */ - conf->vht_oper_chwidth = 1; conf->vht_oper_centr_freq_seg0_idx = center_chan; return; @@ -72,14 +92,24 @@ no_vht: conf->vht_oper_centr_freq_seg0_idx = conf->channel + conf->secondary_channel * 2; #endif /* CONFIG_P2P */ + conf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT; } #endif /* CONFIG_IEEE80211N */ -void wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid, - struct hostapd_config *conf) +int wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + struct hostapd_config *conf) { + conf->hw_mode = ieee80211_freq_to_chan(ssid->frequency, + &conf->channel); + + if (conf->hw_mode == NUM_HOSTAPD_MODES) { + wpa_printf(MSG_ERROR, "Unsupported AP mode frequency: %d MHz", + ssid->frequency); + return -1; + } + /* TODO: enable HT40 if driver supports it; * drop to 11b if driver does not support 11g */ @@ -166,6 +196,8 @@ void wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s, } } #endif /* CONFIG_IEEE80211N */ + + return 0; } @@ -179,15 +211,23 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, os_strlcpy(bss->iface, wpa_s->ifname, sizeof(bss->iface)); - conf->hw_mode = ieee80211_freq_to_chan(ssid->frequency, - &conf->channel); - if (conf->hw_mode == NUM_HOSTAPD_MODES) { - wpa_printf(MSG_ERROR, "Unsupported AP mode frequency: %d MHz", - ssid->frequency); + if (wpa_supplicant_conf_ap_ht(wpa_s, ssid, conf)) + return -1; + + if (ssid->pbss > 1) { + wpa_printf(MSG_ERROR, "Invalid pbss value(%d) for AP mode", + ssid->pbss); return -1; } + bss->pbss = ssid->pbss; - wpa_supplicant_conf_ap_ht(wpa_s, ssid, conf); +#ifdef CONFIG_ACS + if (ssid->acs) { + /* Setting channel to 0 in order to enable ACS */ + conf->channel = 0; + wpa_printf(MSG_DEBUG, "Use automatic channel selection"); + } +#endif /* CONFIG_ACS */ if (ieee80211_is_dfs(ssid->frequency) && wpa_s->conf->country[0]) { conf->ieee80211h = 1; @@ -229,12 +269,12 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, bss->force_per_enrollee_psk = wpa_s->global->p2p_per_sta_psk; if (ssid->p2p_group) { - os_memcpy(bss->ip_addr_go, wpa_s->parent->conf->ip_addr_go, 4); - os_memcpy(bss->ip_addr_mask, wpa_s->parent->conf->ip_addr_mask, + os_memcpy(bss->ip_addr_go, wpa_s->p2pdev->conf->ip_addr_go, 4); + os_memcpy(bss->ip_addr_mask, wpa_s->p2pdev->conf->ip_addr_mask, 4); os_memcpy(bss->ip_addr_start, - wpa_s->parent->conf->ip_addr_start, 4); - os_memcpy(bss->ip_addr_end, wpa_s->parent->conf->ip_addr_end, + wpa_s->p2pdev->conf->ip_addr_start, 4); + os_memcpy(bss->ip_addr_end, wpa_s->p2pdev->conf->ip_addr_end, 4); } #endif /* CONFIG_P2P */ @@ -254,7 +294,10 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) bss->wpa = ssid->proto; - bss->wpa_key_mgmt = ssid->key_mgmt; + if (ssid->key_mgmt == DEFAULT_KEY_MGMT) + bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK; + else + bss->wpa_key_mgmt = ssid->key_mgmt; bss->wpa_pairwise = ssid->pairwise_cipher; if (ssid->psk_set) { bin_clear_free(bss->ssid.wpa_psk, sizeof(*bss->ssid.wpa_psk)); @@ -263,6 +306,7 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, return -1; os_memcpy(bss->ssid.wpa_psk->psk, ssid->psk, PMK_LEN); bss->ssid.wpa_psk->group = 1; + bss->ssid.wpa_psk_set = 1; } else if (ssid->passphrase) { bss->ssid.wpa_passphrase = os_strdup(ssid->passphrase); } else if (ssid->wep_key_len[0] || ssid->wep_key_len[1] || @@ -297,13 +341,17 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, conf->beacon_int = wpa_s->conf->beacon_int; #ifdef CONFIG_P2P - if (wpa_s->conf->p2p_go_ctwindow > conf->beacon_int) { - wpa_printf(MSG_INFO, - "CTWindow (%d) is bigger than beacon interval (%d) - avoid configuring it", - wpa_s->conf->p2p_go_ctwindow, conf->beacon_int); - conf->p2p_go_ctwindow = 0; - } else { - conf->p2p_go_ctwindow = wpa_s->conf->p2p_go_ctwindow; + if (ssid->mode == WPAS_MODE_P2P_GO || + ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) { + if (wpa_s->conf->p2p_go_ctwindow > conf->beacon_int) { + wpa_printf(MSG_INFO, + "CTWindow (%d) is bigger than beacon interval (%d) - avoid configuring it", + wpa_s->conf->p2p_go_ctwindow, + conf->beacon_int); + conf->p2p_go_ctwindow = 0; + } else { + conf->p2p_go_ctwindow = wpa_s->conf->p2p_go_ctwindow; + } } #endif /* CONFIG_P2P */ @@ -372,6 +420,8 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, !(bss->wpa & 2))) goto no_wps; /* WPS2 does not allow WPA/TKIP-only * configuration */ + if (ssid->wps_disabled) + goto no_wps; bss->eap_server = 1; if (!ssid->ignore_broadcast_ssid) @@ -400,6 +450,8 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, os_memcpy(bss->uuid, wpa_s->conf->uuid, WPS_UUID_LEN); os_memcpy(bss->os_version, wpa_s->conf->os_version, 4); bss->pbc_in_m1 = wpa_s->conf->pbc_in_m1; + if (ssid->eap.fragment_size != DEFAULT_FRAGMENT_SIZE) + bss->fragment_size = ssid->eap.fragment_size; no_wps: #endif /* CONFIG_WPS */ @@ -416,6 +468,9 @@ no_wps: wpabuf_dup(wpa_s->conf->ap_vendor_elements); } + bss->ftm_responder = wpa_s->conf->ftm_responder; + bss->ftm_initiator = wpa_s->conf->ftm_initiator; + return 0; } @@ -448,14 +503,14 @@ static void ap_wps_event_cb(void *ctx, enum wps_event event, if (event == WPS_EV_FAIL) { struct wps_event_fail *fail = &data->fail; - if (wpa_s->parent && wpa_s->parent != wpa_s && + if (wpa_s->p2pdev && wpa_s->p2pdev != wpa_s && wpa_s == wpa_s->global->p2p_group_formation) { /* * src/ap/wps_hostapd.c has already sent this on the * main interface, so only send on the parent interface * here if needed. */ - wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_FAIL + wpa_msg(wpa_s->p2pdev, MSG_INFO, WPS_EVENT_FAIL "msg=%d config_error=%d", fail->msg, fail->config_error); } @@ -530,6 +585,11 @@ static void wpas_ap_configured_cb(void *ctx) { struct wpa_supplicant *wpa_s = ctx; +#ifdef CONFIG_ACS + if (wpa_s->current_ssid && wpa_s->current_ssid->acs) + wpa_s->assoc_freq = wpa_s->ap_iface->freq; +#endif /* CONFIG_ACS */ + wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); if (wpa_s->ap_configured_cb) @@ -595,8 +655,8 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, params.p2p = 1; #endif /* CONFIG_P2P */ - if (wpa_s->parent->set_ap_uapsd) - params.uapsd = wpa_s->parent->ap_uapsd; + if (wpa_s->p2pdev->set_ap_uapsd) + params.uapsd = wpa_s->p2pdev->ap_uapsd; else if (params.p2p && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD)) params.uapsd = 1; /* mandatory for P2P GO */ else @@ -605,12 +665,17 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, if (ieee80211_is_dfs(params.freq.freq)) params.freq.freq = 0; /* set channel after CAC */ + if (params.p2p) + wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_GO); + else + wpa_drv_get_ext_capa(wpa_s, WPA_IF_AP_BSS); + if (wpa_drv_associate(wpa_s, ¶ms) < 0) { wpa_msg(wpa_s, MSG_INFO, "Failed to start AP functionality"); return -1; } - wpa_s->ap_iface = hapd_iface = os_zalloc(sizeof(*wpa_s->ap_iface)); + wpa_s->ap_iface = hapd_iface = hostapd_alloc_iface(); if (hapd_iface == NULL) return -1; hapd_iface->owner = wpa_s; @@ -627,6 +692,13 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, return -1; } + /* Use the maximum oper channel width if it's given. */ + if (ssid->max_oper_chwidth) + conf->vht_oper_chwidth = ssid->max_oper_chwidth; + + ieee80211_freq_to_chan(ssid->vht_center_freq2, + &conf->vht_oper_centr_freq_seg1_idx); + os_memcpy(wpa_s->ap_iface->conf->wmm_ac_params, wpa_s->conf->wmm_ac_params, sizeof(wpa_s->conf->wmm_ac_params)); @@ -668,7 +740,7 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, } hapd_iface->bss[i]->msg_ctx = wpa_s; - hapd_iface->bss[i]->msg_ctx_parent = wpa_s->parent; + hapd_iface->bss[i]->msg_ctx_parent = wpa_s->p2pdev; hapd_iface->bss[i]->public_action_cb = ap_public_action_rx; hapd_iface->bss[i]->public_action_cb_ctx = wpa_s; hapd_iface->bss[i]->vendor_action_cb = ap_vendor_action_rx; @@ -864,7 +936,10 @@ int wpa_supplicant_ap_wps_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, return -1; if (pin == NULL) { - unsigned int rpin = wps_generate_pin(); + unsigned int rpin; + + if (wps_generate_pin(&rpin) < 0) + return -1; ret_len = os_snprintf(buf, buflen, "%08d", rpin); if (os_snprintf_error(buflen, ret_len)) return -1; @@ -930,7 +1005,8 @@ const char * wpas_wps_ap_pin_random(struct wpa_supplicant *wpa_s, int timeout) if (wpa_s->ap_iface == NULL) return NULL; hapd = wpa_s->ap_iface->bss[0]; - pin = wps_generate_pin(); + if (wps_generate_pin(&pin) < 0) + return NULL; os_snprintf(pin_txt, sizeof(pin_txt), "%08u", pin); os_free(hapd->conf->ap_pin); hapd->conf->ap_pin = os_strdup(pin_txt); @@ -1265,8 +1341,8 @@ int wpas_ap_wps_add_nfc_pw(struct wpa_supplicant *wpa_s, u16 pw_id, hapd = wpa_s->ap_iface->bss[0]; wps = hapd->wps; - if (wpa_s->parent->conf->wps_nfc_dh_pubkey == NULL || - wpa_s->parent->conf->wps_nfc_dh_privkey == NULL) { + if (wpa_s->p2pdev->conf->wps_nfc_dh_pubkey == NULL || + wpa_s->p2pdev->conf->wps_nfc_dh_privkey == NULL) { wpa_printf(MSG_DEBUG, "P2P: No NFC DH key known"); return -1; } @@ -1275,9 +1351,9 @@ int wpas_ap_wps_add_nfc_pw(struct wpa_supplicant *wpa_s, u16 pw_id, wpabuf_free(wps->dh_pubkey); wpabuf_free(wps->dh_privkey); wps->dh_privkey = wpabuf_dup( - wpa_s->parent->conf->wps_nfc_dh_privkey); + wpa_s->p2pdev->conf->wps_nfc_dh_privkey); wps->dh_pubkey = wpabuf_dup( - wpa_s->parent->conf->wps_nfc_dh_pubkey); + wpa_s->p2pdev->conf->wps_nfc_dh_pubkey); if (wps->dh_privkey == NULL || wps->dh_pubkey == NULL) { wps->dh_ctx = NULL; wpabuf_free(wps->dh_pubkey); @@ -1308,6 +1384,58 @@ int wpas_ap_stop_ap(struct wpa_supplicant *wpa_s) hapd = wpa_s->ap_iface->bss[0]; return hostapd_ctrl_iface_stop_ap(hapd); } + + +int wpas_ap_pmksa_cache_list(struct wpa_supplicant *wpa_s, char *buf, + size_t len) +{ + size_t reply_len = 0, i; + char ap_delimiter[] = "---- AP ----\n"; + char mesh_delimiter[] = "---- mesh ----\n"; + size_t dlen; + + if (wpa_s->ap_iface) { + dlen = os_strlen(ap_delimiter); + if (dlen > len - reply_len) + return reply_len; + os_memcpy(&buf[reply_len], ap_delimiter, dlen); + reply_len += dlen; + + for (i = 0; i < wpa_s->ap_iface->num_bss; i++) { + reply_len += hostapd_ctrl_iface_pmksa_list( + wpa_s->ap_iface->bss[i], + &buf[reply_len], len - reply_len); + } + } + + if (wpa_s->ifmsh) { + dlen = os_strlen(mesh_delimiter); + if (dlen > len - reply_len) + return reply_len; + os_memcpy(&buf[reply_len], mesh_delimiter, dlen); + reply_len += dlen; + + reply_len += hostapd_ctrl_iface_pmksa_list( + wpa_s->ifmsh->bss[0], &buf[reply_len], + len - reply_len); + } + + return reply_len; +} + + +void wpas_ap_pmksa_cache_flush(struct wpa_supplicant *wpa_s) +{ + size_t i; + + if (wpa_s->ap_iface) { + for (i = 0; i < wpa_s->ap_iface->num_bss; i++) + hostapd_ctrl_iface_pmksa_flush(wpa_s->ap_iface->bss[i]); + } + + if (wpa_s->ifmsh) + hostapd_ctrl_iface_pmksa_flush(wpa_s->ifmsh->bss[0]); +} #endif /* CONFIG_CTRL_IFACE */ diff --git a/contrib/wpa/wpa_supplicant/ap.h b/contrib/wpa/wpa_supplicant/ap.h index 594168cf1e03..5a59ddcc1c93 100644 --- a/contrib/wpa/wpa_supplicant/ap.h +++ b/contrib/wpa/wpa_supplicant/ap.h @@ -76,12 +76,16 @@ int wpas_ap_wps_add_nfc_pw(struct wpa_supplicant *wpa_s, u16 pw_id, const struct wpabuf *pw, const u8 *pubkey_hash); struct hostapd_config; -void wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid, - struct hostapd_config *conf); +int wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + struct hostapd_config *conf); int wpas_ap_stop_ap(struct wpa_supplicant *wpa_s); +int wpas_ap_pmksa_cache_list(struct wpa_supplicant *wpa_s, char *buf, + size_t len); +void wpas_ap_pmksa_cache_flush(struct wpa_supplicant *wpa_s); + void wpas_event_dfs_radar_detected(struct wpa_supplicant *wpa_s, struct dfs_event *radar); void wpas_event_dfs_cac_started(struct wpa_supplicant *wpa_s, diff --git a/contrib/wpa/wpa_supplicant/autoscan.c b/contrib/wpa/wpa_supplicant/autoscan.c index a2cf7a5ef232..072a1d5414ae 100644 --- a/contrib/wpa/wpa_supplicant/autoscan.c +++ b/contrib/wpa/wpa_supplicant/autoscan.c @@ -1,6 +1,7 @@ /* * WPA Supplicant - auto scan * Copyright (c) 2012, Intel Corporation. All rights reserved. + * Copyright 2015 Intel Deutschland GmbH * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -15,13 +16,6 @@ #include "scan.h" #include "autoscan.h" -#ifdef CONFIG_AUTOSCAN_EXPONENTIAL -extern const struct autoscan_ops autoscan_exponential_ops; -#endif /* CONFIG_AUTOSCAN_EXPONENTIAL */ - -#ifdef CONFIG_AUTOSCAN_PERIODIC -extern const struct autoscan_ops autoscan_periodic_ops; -#endif /* CONFIG_AUTOSCAN_PERIODIC */ static const struct autoscan_ops * autoscan_modules[] = { #ifdef CONFIG_AUTOSCAN_EXPONENTIAL @@ -50,6 +44,11 @@ int autoscan_init(struct wpa_supplicant *wpa_s, int req_scan) size_t nlen; int i; const struct autoscan_ops *ops = NULL; + struct sched_scan_plan *scan_plans; + + /* Give preference to scheduled scan plans if supported/configured */ + if (wpa_s->sched_scan_plans) + return 0; if (wpa_s->autoscan && wpa_s->autoscan_priv) return 0; @@ -79,11 +78,23 @@ int autoscan_init(struct wpa_supplicant *wpa_s, int req_scan) return -1; } + scan_plans = os_malloc(sizeof(*wpa_s->sched_scan_plans)); + if (!scan_plans) + return -1; + wpa_s->autoscan_params = NULL; wpa_s->autoscan_priv = ops->init(wpa_s, params); - if (wpa_s->autoscan_priv == NULL) + if (!wpa_s->autoscan_priv) { + os_free(scan_plans); return -1; + } + + scan_plans[0].interval = 5; + scan_plans[0].iterations = 0; + os_free(wpa_s->sched_scan_plans); + wpa_s->sched_scan_plans = scan_plans; + wpa_s->sched_scan_plans_num = 1; wpa_s->autoscan = ops; wpa_printf(MSG_DEBUG, "autoscan: Initialized module '%s' with " @@ -116,7 +127,10 @@ void autoscan_deinit(struct wpa_supplicant *wpa_s) wpa_s->autoscan_priv = NULL; wpa_s->scan_interval = 5; - wpa_s->sched_scan_interval = 0; + + os_free(wpa_s->sched_scan_plans); + wpa_s->sched_scan_plans = NULL; + wpa_s->sched_scan_plans_num = 0; } } @@ -134,7 +148,7 @@ int autoscan_notify_scan(struct wpa_supplicant *wpa_s, return -1; wpa_s->scan_interval = interval; - wpa_s->sched_scan_interval = interval; + wpa_s->sched_scan_plans[0].interval = interval; request_scan(wpa_s); } diff --git a/contrib/wpa/wpa_supplicant/autoscan.h b/contrib/wpa/wpa_supplicant/autoscan.h index e2a7652213be..560684fcbf77 100644 --- a/contrib/wpa/wpa_supplicant/autoscan.h +++ b/contrib/wpa/wpa_supplicant/autoscan.h @@ -27,6 +27,16 @@ void autoscan_deinit(struct wpa_supplicant *wpa_s); int autoscan_notify_scan(struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res); +/* Available autoscan modules */ + +#ifdef CONFIG_AUTOSCAN_EXPONENTIAL +extern const struct autoscan_ops autoscan_exponential_ops; +#endif /* CONFIG_AUTOSCAN_EXPONENTIAL */ + +#ifdef CONFIG_AUTOSCAN_PERIODIC +extern const struct autoscan_ops autoscan_periodic_ops; +#endif /* CONFIG_AUTOSCAN_PERIODIC */ + #else /* CONFIG_AUTOSCAN */ static inline int autoscan_init(struct wpa_supplicant *wpa_s, int req_scan) diff --git a/contrib/wpa/wpa_supplicant/bgscan.c b/contrib/wpa/wpa_supplicant/bgscan.c index f74cdbf24a45..798b43c3fdf7 100644 --- a/contrib/wpa/wpa_supplicant/bgscan.c +++ b/contrib/wpa/wpa_supplicant/bgscan.c @@ -13,12 +13,6 @@ #include "config_ssid.h" #include "bgscan.h" -#ifdef CONFIG_BGSCAN_SIMPLE -extern const struct bgscan_ops bgscan_simple_ops; -#endif /* CONFIG_BGSCAN_SIMPLE */ -#ifdef CONFIG_BGSCAN_LEARN -extern const struct bgscan_ops bgscan_learn_ops; -#endif /* CONFIG_BGSCAN_LEARN */ static const struct bgscan_ops * bgscan_modules[] = { #ifdef CONFIG_BGSCAN_SIMPLE diff --git a/contrib/wpa/wpa_supplicant/bgscan.h b/contrib/wpa/wpa_supplicant/bgscan.h index 9131e4ecddc0..3df1550a97dd 100644 --- a/contrib/wpa/wpa_supplicant/bgscan.h +++ b/contrib/wpa/wpa_supplicant/bgscan.h @@ -39,6 +39,15 @@ void bgscan_notify_signal_change(struct wpa_supplicant *wpa_s, int above, int current_signal, int current_noise, int current_txrate); +/* Available bgscan modules */ + +#ifdef CONFIG_BGSCAN_SIMPLE +extern const struct bgscan_ops bgscan_simple_ops; +#endif /* CONFIG_BGSCAN_SIMPLE */ +#ifdef CONFIG_BGSCAN_LEARN +extern const struct bgscan_ops bgscan_learn_ops; +#endif /* CONFIG_BGSCAN_LEARN */ + #else /* CONFIG_BGSCAN */ static inline int bgscan_init(struct wpa_supplicant *wpa_s, diff --git a/contrib/wpa/wpa_supplicant/binder/.clang-format b/contrib/wpa/wpa_supplicant/binder/.clang-format new file mode 100644 index 000000000000..dbfdabfc07fd --- /dev/null +++ b/contrib/wpa/wpa_supplicant/binder/.clang-format @@ -0,0 +1,9 @@ +BasedOnStyle: LLVM +IndentWidth: 8 +UseTab: Always +BreakBeforeBraces: Mozilla +AllowShortIfStatementsOnASingleLine: false +IndentCaseLabels: false +AccessModifierOffset: -8 +AlignAfterOpenBracket: AlwaysBreak +SortIncludes: false diff --git a/contrib/wpa/wpa_supplicant/binder/binder.cpp b/contrib/wpa/wpa_supplicant/binder/binder.cpp new file mode 100644 index 000000000000..750e87818b20 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/binder/binder.cpp @@ -0,0 +1,104 @@ +/* + * binder interface for wpa_supplicant daemon + * Copyright (c) 2004-2016, Jouni Malinen + * Copyright (c) 2004-2016, Roshan Pius + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include +#include +#include + +#include "binder_manager.h" + +extern "C" { +#include "binder.h" +#include "binder_i.h" +#include "utils/common.h" +#include "utils/eloop.h" +#include "utils/includes.h" +} + +void wpas_binder_sock_handler(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct wpa_global *global = (wpa_global *)eloop_ctx; + struct wpas_binder_priv *priv = (wpas_binder_priv *)sock_ctx; + + wpa_printf( + MSG_DEBUG, "Processing binder events on FD %d", priv->binder_fd); + android::IPCThreadState::self()->handlePolledCommands(); +} + +struct wpas_binder_priv *wpas_binder_init(struct wpa_global *global) +{ + struct wpas_binder_priv *priv; + wpa_supplicant_binder::BinderManager *binder_manager; + + priv = (wpas_binder_priv *)os_zalloc(sizeof(*priv)); + if (!priv) + return NULL; + priv->global = global; + + android::ProcessState::self()->setThreadPoolMaxThreadCount(0); + android::IPCThreadState::self()->disableBackgroundScheduling(true); + android::IPCThreadState::self()->setupPolling(&priv->binder_fd); + wpa_printf(MSG_INFO, "Process binder events on FD %d", priv->binder_fd); + if (priv->binder_fd < 0) + goto err; + /* Look for read events from the binder socket in the eloop. */ + if (eloop_register_read_sock( + priv->binder_fd, wpas_binder_sock_handler, global, priv) < 0) + goto err; + + binder_manager = wpa_supplicant_binder::BinderManager::getInstance(); + if (!binder_manager) + goto err; + binder_manager->registerBinderService(global); + /* We may not need to store this binder manager reference in the + * global data strucure because we've made it a singleton class. */ + priv->binder_manager = (void *)binder_manager; + + return priv; + +err: + wpas_binder_deinit(priv); + return NULL; +} + +void wpas_binder_deinit(struct wpas_binder_priv *priv) +{ + if (!priv) + return; + + wpa_supplicant_binder::BinderManager::destroyInstance(); + eloop_unregister_read_sock(priv->binder_fd); + android::IPCThreadState::shutdown(); +} + +int wpas_binder_register_interface(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->global->binder) + return 1; + + wpa_supplicant_binder::BinderManager *binder_manager = + wpa_supplicant_binder::BinderManager::getInstance(); + if (!binder_manager) + return 1; + + return binder_manager->registerInterface(wpa_s); +} + +int wpas_binder_unregister_interface(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->global->binder) + return 1; + + wpa_supplicant_binder::BinderManager *binder_manager = + wpa_supplicant_binder::BinderManager::getInstance(); + if (!binder_manager) + return 1; + + return binder_manager->unregisterInterface(wpa_s); +} diff --git a/contrib/wpa/wpa_supplicant/binder/binder.h b/contrib/wpa/wpa_supplicant/binder/binder.h new file mode 100644 index 000000000000..019e3275c5e2 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/binder/binder.h @@ -0,0 +1,46 @@ +/* + * binder interface for wpa_supplicant daemon + * Copyright (c) 2004-2016, Jouni Malinen + * Copyright (c) 2004-2016, Roshan Pius + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPA_SUPPLICANT_BINDER_BINDER_H +#define WPA_SUPPLICANT_BINDER_BINDER_H + +#ifdef _cplusplus +extern "C" { +#endif /* _cplusplus */ + +/** + * This is the binder RPC interface entry point to the wpa_supplicant core. + * This initializes the binder driver & BinderManager instance and then forwards + * all the notifcations from the supplicant core to the BinderManager. + */ +struct wpas_binder_priv; +struct wpa_global; + +struct wpas_binder_priv *wpas_binder_init(struct wpa_global *global); +void wpas_binder_deinit(struct wpas_binder_priv *priv); + +#ifdef CONFIG_CTRL_IFACE_BINDER +int wpas_binder_register_interface(struct wpa_supplicant *wpa_s); +int wpas_binder_unregister_interface(struct wpa_supplicant *wpa_s); +#else /* CONFIG_CTRL_IFACE_BINDER */ +static inline int wpas_binder_register_interface(struct wpa_supplicant *wpa_s) +{ + return 0; +} +static inline int wpas_binder_unregister_interface(struct wpa_supplicant *wpa_s) +{ + return 0; +} +#endif /* CONFIG_CTRL_IFACE_BINDER */ + +#ifdef _cplusplus +} +#endif /* _cplusplus */ + +#endif /* WPA_SUPPLICANT_BINDER_BINDER_H */ diff --git a/contrib/wpa/wpa_supplicant/binder/binder_constants.cpp b/contrib/wpa/wpa_supplicant/binder/binder_constants.cpp new file mode 100644 index 000000000000..0d452b11baec --- /dev/null +++ b/contrib/wpa/wpa_supplicant/binder/binder_constants.cpp @@ -0,0 +1,18 @@ +/* + * binder interface for wpa_supplicant daemon + * Copyright (c) 2004-2016, Jouni Malinen + * Copyright (c) 2004-2016, Roshan Pius + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "binder_constants.h" + +namespace wpa_supplicant_binder { +namespace binder_constants { + +const char kServiceName[] = "wpa_supplicant"; + +} /* namespace binder_constants */ +} /* namespace wpa_supplicant_binder */ diff --git a/contrib/wpa/wpa_supplicant/binder/binder_constants.h b/contrib/wpa/wpa_supplicant/binder/binder_constants.h new file mode 100644 index 000000000000..a4d9b558edc0 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/binder/binder_constants.h @@ -0,0 +1,21 @@ +/* + * binder interface for wpa_supplicant daemon + * Copyright (c) 2004-2016, Jouni Malinen + * Copyright (c) 2004-2016, Roshan Pius + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPA_SUPPLICANT_BINDER_BINDER_CONSTANTS_H +#define WPA_SUPPLICANT_BINDER_BINDER_CONSTANTS_H + +namespace wpa_supplicant_binder { +namespace binder_constants { + +extern const char kServiceName[]; + +} /* namespace binder_constants */ +} /* namespace wpa_supplicant_binder */ + +#endif /* WPA_SUPPLICANT_BINDER_BINDER_CONSTANTS_H */ diff --git a/contrib/wpa/wpa_supplicant/binder/binder_i.h b/contrib/wpa/wpa_supplicant/binder/binder_i.h new file mode 100644 index 000000000000..5140d6d6c01d --- /dev/null +++ b/contrib/wpa/wpa_supplicant/binder/binder_i.h @@ -0,0 +1,28 @@ +/* + * binder interface for wpa_supplicant daemon + * Copyright (c) 2004-2016, Jouni Malinen + * Copyright (c) 2004-2016, Roshan Pius + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef BINDER_I_H +#define BINDER_I_H + +#ifdef _cplusplus +extern "C" { +#endif // _cplusplus + +struct wpas_binder_priv +{ + int binder_fd; + struct wpa_global *global; + void *binder_manager; +}; + +#ifdef _cplusplus +} +#endif /* _cplusplus */ + +#endif /* BINDER_I_H */ diff --git a/contrib/wpa/wpa_supplicant/binder/binder_manager.cpp b/contrib/wpa/wpa_supplicant/binder/binder_manager.cpp new file mode 100644 index 000000000000..27e8dedca44a --- /dev/null +++ b/contrib/wpa/wpa_supplicant/binder/binder_manager.cpp @@ -0,0 +1,100 @@ +/* + * binder interface for wpa_supplicant daemon + * Copyright (c) 2004-2016, Jouni Malinen + * Copyright (c) 2004-2016, Roshan Pius + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include + +#include "binder_constants.h" +#include "binder_manager.h" + +extern "C" { +#include "utils/common.h" +#include "utils/includes.h" +} + +namespace wpa_supplicant_binder { + +BinderManager *BinderManager::instance_ = NULL; + +BinderManager *BinderManager::getInstance() +{ + if (!instance_) + instance_ = new BinderManager(); + return instance_; +} + +void BinderManager::destroyInstance() +{ + if (instance_) + delete instance_; + instance_ = NULL; +} + +int BinderManager::registerBinderService(struct wpa_global *global) +{ + /* Create the main binder service object and register with + * system service manager. */ + supplicant_object_ = new Supplicant(global); + android::String16 service_name(binder_constants::kServiceName); + android::defaultServiceManager()->addService( + service_name, android::IInterface::asBinder(supplicant_object_)); + return 0; +} + +int BinderManager::registerInterface(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s) + return 1; + + /* Using the corresponding wpa_supplicant pointer as key to our + * object map. */ + const void *iface_key = wpa_s; + + /* Return failure if we already have an object for that iface_key. */ + if (iface_object_map_.find(iface_key) != iface_object_map_.end()) + return 1; + + iface_object_map_[iface_key] = new Iface(wpa_s); + if (!iface_object_map_[iface_key].get()) + return 1; + + wpa_s->binder_object_key = iface_key; + + return 0; +} + +int BinderManager::unregisterInterface(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s || !wpa_s->binder_object_key) + return 1; + + const void *iface_key = wpa_s; + if (iface_object_map_.find(iface_key) == iface_object_map_.end()) + return 1; + + /* Delete the corresponding iface object from our map. */ + iface_object_map_.erase(iface_key); + wpa_s->binder_object_key = NULL; + return 0; +} + +int BinderManager::getIfaceBinderObjectByKey( + const void *iface_object_key, + android::sp *iface_object) +{ + if (!iface_object_key || !iface_object) + return 1; + + if (iface_object_map_.find(iface_object_key) == iface_object_map_.end()) + return 1; + + *iface_object = iface_object_map_[iface_object_key]; + return 0; +} + +} /* namespace wpa_supplicant_binder */ diff --git a/contrib/wpa/wpa_supplicant/binder/binder_manager.h b/contrib/wpa/wpa_supplicant/binder/binder_manager.h new file mode 100644 index 000000000000..d8b7dd0f8726 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/binder/binder_manager.h @@ -0,0 +1,58 @@ +/* + * binder interface for wpa_supplicant daemon + * Copyright (c) 2004-2016, Jouni Malinen + * Copyright (c) 2004-2016, Roshan Pius + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPA_SUPPLICANT_BINDER_BINDER_MANAGER_H +#define WPA_SUPPLICANT_BINDER_BINDER_MANAGER_H + +#include +#include + +#include "iface.h" +#include "supplicant.h" + +struct wpa_global; +struct wpa_supplicant; + +namespace wpa_supplicant_binder { + +/** + * BinderManager is responsible for managing the lifetime of all + * binder objects created by wpa_supplicant. This is a singleton + * class which is created by the supplicant core and can be used + * to get references to the binder objects. + */ +class BinderManager +{ +public: + static BinderManager *getInstance(); + static void destroyInstance(); + int registerBinderService(struct wpa_global *global); + int registerInterface(struct wpa_supplicant *wpa_s); + int unregisterInterface(struct wpa_supplicant *wpa_s); + int getIfaceBinderObjectByKey( + const void *iface_object_key, + android::sp *iface_object); + +private: + BinderManager() = default; + ~BinderManager() = default; + + /* Singleton instance of this class. */ + static BinderManager *instance_; + /* The main binder service object. */ + android::sp supplicant_object_; + /* Map of all the interface specific binder objects controlled by + * wpa_supplicant. This map is keyed in by the corresponding + * wpa_supplicant structure pointer. */ + std::map> iface_object_map_; +}; + +} /* namespace wpa_supplicant_binder */ + +#endif /* WPA_SUPPLICANT_BINDER_BINDER_MANAGER_H */ diff --git a/contrib/wpa/wpa_supplicant/binder/fi/w1/wpa_supplicant/IIface.aidl b/contrib/wpa/wpa_supplicant/binder/fi/w1/wpa_supplicant/IIface.aidl new file mode 100644 index 000000000000..ea11d426df1f --- /dev/null +++ b/contrib/wpa/wpa_supplicant/binder/fi/w1/wpa_supplicant/IIface.aidl @@ -0,0 +1,16 @@ +/* + * binder interface for wpa_supplicant daemon + * Copyright (c) 2004-2016, Jouni Malinen + * Copyright (c) 2004-2016, Roshan Pius + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +package fi.w1.wpa_supplicant; + +/** + * Interface exposed by wpa_supplicant for each network interface it controls. + */ +interface IIface { +} diff --git a/contrib/wpa/wpa_supplicant/binder/fi/w1/wpa_supplicant/ISupplicant.aidl b/contrib/wpa/wpa_supplicant/binder/fi/w1/wpa_supplicant/ISupplicant.aidl new file mode 100644 index 000000000000..1cbee20a620f --- /dev/null +++ b/contrib/wpa/wpa_supplicant/binder/fi/w1/wpa_supplicant/ISupplicant.aidl @@ -0,0 +1,59 @@ +/* + * WPA Supplicant - binder interface for wpa_supplicant daemon + * Copyright (c) 2004-2016, Jouni Malinen + * Copyright (c) 2004-2016, Roshan Pius + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +package fi.w1.wpa_supplicant; + +import android.os.PersistableBundle; +import fi.w1.wpa_supplicant.IIface; + +/** + * Interface exposed by the wpa_supplicant binder service registered + * with the service manager with name: fi.w1.wpa_supplicant. + */ +interface ISupplicant { + /* Error values returned by the service to RPC method calls. */ + const int ERROR_INVALID_ARGS = 1; + const int ERROR_UNKNOWN = 2; + const int ERROR_IFACE_EXISTS = 3; + const int ERROR_IFACE_UNKNOWN = 4; + + /** + * Registers a wireless interface in wpa_supplicant. + * + * @param args A dictionary with arguments used to add the interface to + * wpa_supplicant. + * The dictionary may contain the following entries: + * Ifname(String) Name of the network interface to control, e.g., + * wlan0. + * BridgeIfname(String) Name of the bridge interface to control, e.g., + * br0. + * Driver(String) Driver name which the interface uses, e.g., nl80211. + * ConfigFile(String) Configuration file path. + * + * @return Binder object representing the interface. + */ + IIface CreateInterface(in PersistableBundle args); + + /** + * Deregisters a wireless interface from wpa_supplicant. + * + * @param ifname Name of the network interface, e.g., wlan0 + */ + void RemoveInterface(in @utf8InCpp String ifname); + + /** + * Gets a binder object for the interface corresponding to ifname + * which wpa_supplicant already controls. + * + * @param ifname Name of the network interface, e.g., wlan0 + * + * @return Binder object representing the interface. + */ + IIface GetInterface(in @utf8InCpp String ifname); +} diff --git a/contrib/wpa/wpa_supplicant/binder/fi/w1/wpa_supplicant/ISupplicantCallbacks.aidl b/contrib/wpa/wpa_supplicant/binder/fi/w1/wpa_supplicant/ISupplicantCallbacks.aidl new file mode 100644 index 000000000000..d624d9133603 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/binder/fi/w1/wpa_supplicant/ISupplicantCallbacks.aidl @@ -0,0 +1,20 @@ +/* + * binder interface for wpa_supplicant daemon + * Copyright (c) 2004-2016, Jouni Malinen + * Copyright (c) 2004-2016, Roshan Pius + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +package fi.w1.wpa_supplicant; + +import android.os.PersistableBundle; + +/** + * Callback Interface exposed by the wpa_supplicant service. Clients need + * to host an instance of this binder object and pass a reference of the object + * to wpa_supplicant via the registerCallbacksObject method. + */ +interface ISupplicantCallbacks { +} diff --git a/contrib/wpa/wpa_supplicant/binder/iface.cpp b/contrib/wpa/wpa_supplicant/binder/iface.cpp new file mode 100644 index 000000000000..c61b3b006427 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/binder/iface.cpp @@ -0,0 +1,16 @@ +/* + * binder interface for wpa_supplicant daemon + * Copyright (c) 2004-2016, Jouni Malinen + * Copyright (c) 2004-2016, Roshan Pius + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "iface.h" + +namespace wpa_supplicant_binder { + +Iface::Iface(struct wpa_supplicant *wpa_s) : wpa_s_(wpa_s) {} + +} /* namespace wpa_supplicant_binder */ diff --git a/contrib/wpa/wpa_supplicant/binder/iface.h b/contrib/wpa/wpa_supplicant/binder/iface.h new file mode 100644 index 000000000000..c0ee12c65fa5 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/binder/iface.h @@ -0,0 +1,42 @@ +/* + * binder interface for wpa_supplicant daemon + * Copyright (c) 2004-2016, Jouni Malinen + * Copyright (c) 2004-2016, Roshan Pius + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPA_SUPPLICANT_BINDER_IFACE_H +#define WPA_SUPPLICANT_BINDER_IFACE_H + +#include "fi/w1/wpa_supplicant/BnIface.h" + +extern "C" { +#include "utils/common.h" +#include "utils/includes.h" +#include "../wpa_supplicant_i.h" +} + +namespace wpa_supplicant_binder { + +/** + * Implementation of Iface binder object. Each unique binder + * object is used for control operations on a specific interface + * controlled by wpa_supplicant. + */ +class Iface : public fi::w1::wpa_supplicant::BnIface +{ +public: + Iface(struct wpa_supplicant *wpa_s); + virtual ~Iface() = default; + +private: + /* Raw pointer to the structure maintained by the core for this + * interface. */ + struct wpa_supplicant *wpa_s_; +}; + +} /* namespace wpa_supplicant_binder */ + +#endif /* WPA_SUPPLICANT_BINDER_IFACE_H */ diff --git a/contrib/wpa/wpa_supplicant/binder/supplicant.cpp b/contrib/wpa/wpa_supplicant/binder/supplicant.cpp new file mode 100644 index 000000000000..76569b1471fb --- /dev/null +++ b/contrib/wpa/wpa_supplicant/binder/supplicant.cpp @@ -0,0 +1,127 @@ +/* + * binder interface for wpa_supplicant daemon + * Copyright (c) 2004-2016, Jouni Malinen + * Copyright (c) 2004-2016, Roshan Pius + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "supplicant.h" +#include "binder_manager.h" + +namespace wpa_supplicant_binder { + +Supplicant::Supplicant(struct wpa_global *global) : wpa_global_(global) {} + +android::binder::Status Supplicant::CreateInterface( + const android::os::PersistableBundle ¶ms, + android::sp *aidl_return) +{ + android::String16 driver, ifname, confname, bridge_ifname; + + /* Check if required Ifname argument is missing */ + if (!params.getString(android::String16("Ifname"), &ifname)) + return android::binder::Status::fromServiceSpecificError( + ERROR_INVALID_ARGS, + android::String8("Ifname missing in params.")); + /* Retrieve the remaining params from the dictionary */ + params.getString(android::String16("Driver"), &driver); + params.getString(android::String16("ConfigFile"), &confname); + params.getString(android::String16("BridgeIfname"), &bridge_ifname); + + /* + * Try to get the wpa_supplicant record for this iface, return + * an error if we already control it. + */ + if (wpa_supplicant_get_iface( + wpa_global_, android::String8(ifname).string()) != NULL) + return android::binder::Status::fromServiceSpecificError( + ERROR_IFACE_EXISTS, + android::String8("wpa_supplicant already controls this " + "interface.")); + + android::binder::Status status; + struct wpa_supplicant *wpa_s = NULL; + struct wpa_interface iface; + + os_memset(&iface, 0, sizeof(iface)); + iface.driver = os_strdup(android::String8(driver).string()); + iface.ifname = os_strdup(android::String8(ifname).string()); + iface.confname = os_strdup(android::String8(confname).string()); + iface.bridge_ifname = + os_strdup(android::String8(bridge_ifname).string()); + /* Otherwise, have wpa_supplicant attach to it. */ + wpa_s = wpa_supplicant_add_iface(wpa_global_, &iface, NULL); + /* The supplicant core creates a corresponding binder object via + * BinderManager when |wpa_supplicant_add_iface| is called. */ + if (!wpa_s || !wpa_s->binder_object_key) { + status = android::binder::Status::fromServiceSpecificError( + ERROR_UNKNOWN, + android::String8( + "wpa_supplicant couldn't grab this interface.")); + } else { + BinderManager *binder_manager = BinderManager::getInstance(); + + if (!binder_manager || + binder_manager->getIfaceBinderObjectByKey( + wpa_s->binder_object_key, aidl_return)) + status = + android::binder::Status::fromServiceSpecificError( + ERROR_UNKNOWN, + android::String8("wpa_supplicant encountered a " + "binder error.")); + else + status = android::binder::Status::ok(); + } + os_free((void *)iface.driver); + os_free((void *)iface.ifname); + os_free((void *)iface.confname); + os_free((void *)iface.bridge_ifname); + return status; +} + +android::binder::Status Supplicant::RemoveInterface(const std::string &ifname) +{ + struct wpa_supplicant *wpa_s; + + wpa_s = wpa_supplicant_get_iface(wpa_global_, ifname.c_str()); + if (!wpa_s || !wpa_s->binder_object_key) + return android::binder::Status::fromServiceSpecificError( + ERROR_IFACE_UNKNOWN, + android::String8("wpa_supplicant does not control this " + "interface.")); + if (wpa_supplicant_remove_iface(wpa_global_, wpa_s, 0)) + return android::binder::Status::fromServiceSpecificError( + ERROR_UNKNOWN, + android::String8( + "wpa_supplicant couldn't remove this interface.")); + return android::binder::Status::ok(); +} + +android::binder::Status Supplicant::GetInterface( + const std::string &ifname, + android::sp *aidl_return) +{ + struct wpa_supplicant *wpa_s; + + wpa_s = wpa_supplicant_get_iface(wpa_global_, ifname.c_str()); + if (!wpa_s || !wpa_s->binder_object_key) + return android::binder::Status::fromServiceSpecificError( + ERROR_IFACE_UNKNOWN, + android::String8( + "wpa_supplicant does not control this interface.")); + + BinderManager *binder_manager = BinderManager::getInstance(); + if (!binder_manager || + binder_manager->getIfaceBinderObjectByKey( + wpa_s->binder_object_key, aidl_return)) + return android::binder::Status::fromServiceSpecificError( + ERROR_UNKNOWN, + android::String8( + "wpa_supplicant encountered a binder error.")); + + return android::binder::Status::ok(); +} + +} /* namespace wpa_supplicant_binder */ diff --git a/contrib/wpa/wpa_supplicant/binder/supplicant.h b/contrib/wpa/wpa_supplicant/binder/supplicant.h new file mode 100644 index 000000000000..136b99b14327 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/binder/supplicant.h @@ -0,0 +1,55 @@ +/* + * binder interface for wpa_supplicant daemon + * Copyright (c) 2004-2016, Jouni Malinen + * Copyright (c) 2004-2016, Roshan Pius + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPA_SUPPLICANT_BINDER_SUPPLICANT_H +#define WPA_SUPPLICANT_BINDER_SUPPLICANT_H + +#include "fi/w1/wpa_supplicant/BnSupplicant.h" +#include "fi/w1/wpa_supplicant/IIface.h" +#include "fi/w1/wpa_supplicant/ISupplicantCallbacks.h" + +extern "C" { +#include "utils/common.h" +#include "utils/includes.h" +#include "../wpa_supplicant_i.h" +} + +namespace wpa_supplicant_binder { + +/** + * Implementation of the supplicant binder object. This binder + * object is used core for global control operations on + * wpa_supplicant. + */ +class Supplicant : public fi::w1::wpa_supplicant::BnSupplicant +{ +public: + Supplicant(struct wpa_global *global); + virtual ~Supplicant() = default; + + android::binder::Status CreateInterface( + const android::os::PersistableBundle ¶ms, + android::sp *aidl_return) override; + android::binder::Status + RemoveInterface(const std::string &ifname) override; + android::binder::Status GetInterface( + const std::string &ifname, + android::sp *aidl_return) override; + +private: + /* Raw pointer to the global structure maintained by the core. */ + struct wpa_global *wpa_global_; + /* All the callback objects registered by the clients. */ + std::vector> + callbacks_; +}; + +} /* namespace wpa_supplicant_binder */ + +#endif /* WPA_SUPPLICANT_BINDER_SUPPLICANT_H */ diff --git a/contrib/wpa/wpa_supplicant/bss.c b/contrib/wpa/wpa_supplicant/bss.c index 1051ee3a4c55..3a8778db9058 100644 --- a/contrib/wpa/wpa_supplicant/bss.c +++ b/contrib/wpa/wpa_supplicant/bss.c @@ -12,6 +12,7 @@ #include "utils/eloop.h" #include "common/ieee802_11_defs.h" #include "drivers/driver.h" +#include "eap_peer/eap.h" #include "wpa_supplicant_i.h" #include "config.h" #include "notify.h" @@ -60,6 +61,9 @@ struct wpa_bss_anqp * wpa_bss_anqp_alloc(void) anqp = os_zalloc(sizeof(*anqp)); if (anqp == NULL) return NULL; +#ifdef CONFIG_INTERWORKING + dl_list_init(&anqp->anqp_elems); +#endif /* CONFIG_INTERWORKING */ anqp->users = 1; return anqp; } @@ -80,6 +84,7 @@ static struct wpa_bss_anqp * wpa_bss_anqp_clone(struct wpa_bss_anqp *anqp) #define ANQP_DUP(f) if (anqp->f) n->f = wpabuf_dup(anqp->f) #ifdef CONFIG_INTERWORKING + dl_list_init(&n->anqp_elems); ANQP_DUP(capability_list); ANQP_DUP(venue_name); ANQP_DUP(network_auth_type); @@ -141,6 +146,10 @@ int wpa_bss_anqp_unshare_alloc(struct wpa_bss *bss) */ static void wpa_bss_anqp_free(struct wpa_bss_anqp *anqp) { +#ifdef CONFIG_INTERWORKING + struct wpa_bss_anqp_elem *elem; +#endif /* CONFIG_INTERWORKING */ + if (anqp == NULL) return; @@ -159,6 +168,13 @@ static void wpa_bss_anqp_free(struct wpa_bss_anqp *anqp) wpabuf_free(anqp->nai_realm); wpabuf_free(anqp->anqp_3gpp); wpabuf_free(anqp->domain_name); + + while ((elem = dl_list_first(&anqp->anqp_elems, + struct wpa_bss_anqp_elem, list))) { + dl_list_del(&elem->list); + wpabuf_free(elem->payload); + os_free(elem); + } #endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_HS20 wpabuf_free(anqp->hs20_capability_list); @@ -198,8 +214,8 @@ static void wpa_bss_update_pending_connect(struct wpa_supplicant *wpa_s, } -static void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, - const char *reason) +void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, + const char *reason) { if (wpa_s->last_scan_res) { unsigned int i; @@ -288,6 +304,47 @@ static void wpa_bss_copy_res(struct wpa_bss *dst, struct wpa_scan_res *src, } +static int wpa_bss_is_wps_candidate(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss) +{ +#ifdef CONFIG_WPS + struct wpa_ssid *ssid; + struct wpabuf *wps_ie; + int pbc = 0, ret; + + wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); + if (!wps_ie) + return 0; + + if (wps_is_selected_pbc_registrar(wps_ie)) { + pbc = 1; + } else if (!wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1)) { + wpabuf_free(wps_ie); + return 0; + } + + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS)) + continue; + if (ssid->ssid_len && + (ssid->ssid_len != bss->ssid_len || + os_memcmp(ssid->ssid, bss->ssid, ssid->ssid_len) != 0)) + continue; + + if (pbc) + ret = eap_is_wps_pbc_enrollee(&ssid->eap); + else + ret = eap_is_wps_pin_enrollee(&ssid->eap); + wpabuf_free(wps_ie); + return ret; + } + wpabuf_free(wps_ie); +#endif /* CONFIG_WPS */ + + return 0; +} + + static int wpa_bss_known(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) { struct wpa_ssid *ssid; @@ -326,7 +383,8 @@ static int wpa_bss_remove_oldest_unknown(struct wpa_supplicant *wpa_s) struct wpa_bss *bss; dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { - if (!wpa_bss_known(wpa_s, bss)) { + if (!wpa_bss_known(wpa_s, bss) && + !wpa_bss_is_wps_candidate(wpa_s, bss)) { wpa_bss_remove(wpa_s, bss, __func__); return 0; } @@ -784,7 +842,7 @@ void wpa_bss_update_end(struct wpa_supplicant *wpa_s, struct scan_info *info, struct wpa_bss *bss, *n; os_get_reltime(&wpa_s->last_scan); - if (!new_scan) + if ((info && info->aborted) || !new_scan) return; /* do not expire entries without new scan */ dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) { @@ -1004,20 +1062,7 @@ struct wpa_bss * wpa_bss_get_id_range(struct wpa_supplicant *wpa_s, */ const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie) { - const u8 *end, *pos; - - pos = (const u8 *) (bss + 1); - end = pos + bss->ie_len; - - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) - break; - if (pos[0] == ie) - return pos; - pos += 2 + pos[1]; - } - - return NULL; + return get_ie((const u8 *) (bss + 1), bss->ie_len, ie); } @@ -1037,8 +1082,8 @@ const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type) pos = (const u8 *) (bss + 1); end = pos + bss->ie_len; - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) + while (end - pos > 1) { + if (2 + pos[1] > end - pos) break; if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && vendor_type == WPA_GET_BE32(&pos[2])) @@ -1074,8 +1119,8 @@ const u8 * wpa_bss_get_vendor_ie_beacon(const struct wpa_bss *bss, pos += bss->ie_len; end = pos + bss->beacon_ie_len; - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) + while (end - pos > 1) { + if (2 + pos[1] > end - pos) break; if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && vendor_type == WPA_GET_BE32(&pos[2])) @@ -1110,8 +1155,8 @@ struct wpabuf * wpa_bss_get_vendor_ie_multi(const struct wpa_bss *bss, pos = (const u8 *) (bss + 1); end = pos + bss->ie_len; - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) + while (end - pos > 1) { + if (2 + pos[1] > end - pos) break; if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && vendor_type == WPA_GET_BE32(&pos[2])) @@ -1155,8 +1200,8 @@ struct wpabuf * wpa_bss_get_vendor_ie_multi_beacon(const struct wpa_bss *bss, pos += bss->ie_len; end = pos + bss->beacon_ie_len; - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) + while (end - pos > 1) { + if (2 + pos[1] > end - pos) break; if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && vendor_type == WPA_GET_BE32(&pos[2])) diff --git a/contrib/wpa/wpa_supplicant/bss.h b/contrib/wpa/wpa_supplicant/bss.h index b215380eeb15..84e8fb07461e 100644 --- a/contrib/wpa/wpa_supplicant/bss.h +++ b/contrib/wpa/wpa_supplicant/bss.h @@ -19,6 +19,12 @@ struct wpa_scan_res; #define WPA_BSS_ASSOCIATED BIT(5) #define WPA_BSS_ANQP_FETCH_TRIED BIT(6) +struct wpa_bss_anqp_elem { + struct dl_list list; + u16 infoid; + struct wpabuf *payload; +}; + /** * struct wpa_bss_anqp - ANQP data for a BSS entry (struct wpa_bss) */ @@ -34,6 +40,7 @@ struct wpa_bss_anqp { struct wpabuf *nai_realm; struct wpabuf *anqp_3gpp; struct wpabuf *domain_name; + struct dl_list anqp_elems; /* list of struct wpa_bss_anqp_elem */ #endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_HS20 struct wpabuf *hs20_capability_list; @@ -106,6 +113,8 @@ void wpa_bss_update_start(struct wpa_supplicant *wpa_s); void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s, struct wpa_scan_res *res, struct os_reltime *fetch_time); +void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, + const char *reason); void wpa_bss_update_end(struct wpa_supplicant *wpa_s, struct scan_info *info, int new_scan); int wpa_bss_init(struct wpa_supplicant *wpa_s); @@ -141,6 +150,17 @@ static inline int bss_is_dmg(const struct wpa_bss *bss) return bss->freq > 45000; } +/** + * Test whether a BSS is a PBSS. + * This checks whether a BSS is a DMG-band PBSS. PBSS is used for P2P DMG + * network. + */ +static inline int bss_is_pbss(struct wpa_bss *bss) +{ + return bss_is_dmg(bss) && + (bss->caps & IEEE80211_CAP_DMG_MASK) == IEEE80211_CAP_DMG_PBSS; +} + static inline void wpa_bss_update_level(struct wpa_bss *bss, int new_level) { if (bss != NULL && new_level < 0) diff --git a/contrib/wpa/wpa_supplicant/config.c b/contrib/wpa/wpa_supplicant/config.c index b1adab77bbe0..dd922caf80af 100644 --- a/contrib/wpa/wpa_supplicant/config.c +++ b/contrib/wpa/wpa_supplicant/config.c @@ -32,7 +32,11 @@ struct parse_data { /* Configuration variable name */ char *name; - /* Parser function for this variable */ + /* Parser function for this variable. The parser functions return 0 or 1 + * to indicate success. Value 0 indicates that the parameter value may + * have changed while value 1 means that the value did not change. + * Error cases (failure to parse the string) are indicated by returning + * -1. */ int (*parser)(const struct parse_data *data, struct wpa_ssid *ssid, int line, const char *value); @@ -59,7 +63,7 @@ static int wpa_config_parse_str(const struct parse_data *data, struct wpa_ssid *ssid, int line, const char *value) { - size_t res_len, *dst_len; + size_t res_len, *dst_len, prev_len; char **dst, *tmp; if (os_strcmp(value, "NULL") == 0) { @@ -105,6 +109,21 @@ static int wpa_config_parse_str(const struct parse_data *data, set: dst = (char **) (((u8 *) ssid) + (long) data->param1); dst_len = (size_t *) (((u8 *) ssid) + (long) data->param2); + + if (data->param2) + prev_len = *dst_len; + else if (*dst) + prev_len = os_strlen(*dst); + else + prev_len = 0; + if ((*dst == NULL && tmp == NULL) || + (*dst && tmp && prev_len == res_len && + os_memcmp(*dst, tmp, res_len) == 0)) { + /* No change to the previously configured value */ + os_free(tmp); + return 1; + } + os_free(*dst); *dst = tmp; if (data->param2) @@ -190,6 +209,9 @@ static int wpa_config_parse_int(const struct parse_data *data, line, value); return -1; } + + if (*dst == val) + return 1; *dst = val; wpa_printf(MSG_MSGDUMP, "%s=%d (0x%x)", data->name, *dst, *dst); @@ -456,9 +478,17 @@ static int wpa_config_parse_psk(const struct parse_data *data, } wpa_hexdump_ascii_key(MSG_MSGDUMP, "PSK (ASCII passphrase)", (u8 *) value, len); + if (has_ctrl_char((u8 *) value, len)) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid passphrase character", + line); + return -1; + } if (ssid->passphrase && os_strlen(ssid->passphrase) == len && - os_memcmp(ssid->passphrase, value, len) == 0) - return 0; + os_memcmp(ssid->passphrase, value, len) == 0) { + /* No change to the previously configured value */ + return 1; + } ssid->psk_set = 0; str_clear_free(ssid->passphrase); ssid->passphrase = dup_binstr(value, len); @@ -569,6 +599,8 @@ static int wpa_config_parse_proto(const struct parse_data *data, errors++; } + if (!errors && ssid->proto == val) + return 1; wpa_printf(MSG_MSGDUMP, "proto: 0x%x", val); ssid->proto = val; return errors ? -1 : 0; @@ -705,6 +737,8 @@ static int wpa_config_parse_key_mgmt(const struct parse_data *data, errors++; } + if (!errors && ssid->key_mgmt == val) + return 1; wpa_printf(MSG_MSGDUMP, "key_mgmt: 0x%x", val); ssid->key_mgmt = val; return errors ? -1 : 0; @@ -899,6 +933,9 @@ static char * wpa_config_write_key_mgmt(const struct parse_data *data, static int wpa_config_parse_cipher(int line, const char *value) { +#ifdef CONFIG_NO_WPA + return -1; +#else /* CONFIG_NO_WPA */ int val = wpa_parse_cipher(value); if (val < 0) { wpa_printf(MSG_ERROR, "Line %d: invalid cipher '%s'.", @@ -911,12 +948,16 @@ static int wpa_config_parse_cipher(int line, const char *value) return -1; } return val; +#endif /* CONFIG_NO_WPA */ } #ifndef NO_CONFIG_WRITE static char * wpa_config_write_cipher(int cipher) { +#ifdef CONFIG_NO_WPA + return NULL; +#else /* CONFIG_NO_WPA */ char *buf = os_zalloc(50); if (buf == NULL) return NULL; @@ -927,6 +968,7 @@ static char * wpa_config_write_cipher(int cipher) } return buf; +#endif /* CONFIG_NO_WPA */ } #endif /* NO_CONFIG_WRITE */ @@ -945,6 +987,8 @@ static int wpa_config_parse_pairwise(const struct parse_data *data, return -1; } + if (ssid->pairwise_cipher == val) + return 1; wpa_printf(MSG_MSGDUMP, "pairwise: 0x%x", val); ssid->pairwise_cipher = val; return 0; @@ -981,6 +1025,8 @@ static int wpa_config_parse_group(const struct parse_data *data, return -1; } + if (ssid->group_cipher == val) + return 1; wpa_printf(MSG_MSGDUMP, "group: 0x%x", val); ssid->group_cipher = val; return 0; @@ -1042,6 +1088,8 @@ static int wpa_config_parse_auth_alg(const struct parse_data *data, errors++; } + if (!errors && ssid->auth_alg == val) + return 1; wpa_printf(MSG_MSGDUMP, "auth_alg: 0x%x", val); ssid->auth_alg = val; return errors ? -1 : 0; @@ -1296,6 +1344,32 @@ static int wpa_config_parse_eap(const struct parse_data *data, methods[num_methods].method = EAP_TYPE_NONE; num_methods++; + if (!errors && ssid->eap.eap_methods) { + struct eap_method_type *prev_m; + size_t i, j, prev_methods, match = 0; + + prev_m = ssid->eap.eap_methods; + for (i = 0; prev_m[i].vendor != EAP_VENDOR_IETF || + prev_m[i].method != EAP_TYPE_NONE; i++) { + /* Count the methods */ + } + prev_methods = i + 1; + + for (i = 0; prev_methods == num_methods && i < prev_methods; + i++) { + for (j = 0; j < num_methods; j++) { + if (prev_m[i].vendor == methods[j].vendor && + prev_m[i].method == methods[j].method) { + match++; + break; + } + } + } + if (match == num_methods) { + os_free(methods); + return 1; + } + } wpa_hexdump(MSG_MSGDUMP, "eap methods", (u8 *) methods, num_methods * sizeof(*methods)); os_free(ssid->eap.eap_methods); @@ -1348,6 +1422,8 @@ static int wpa_config_parse_password(const struct parse_data *data, u8 *hash; if (os_strcmp(value, "NULL") == 0) { + if (!ssid->eap.password) + return 1; /* Already unset */ wpa_printf(MSG_DEBUG, "Unset configuration string 'password'"); bin_clear_free(ssid->eap.password, ssid->eap.password_len); ssid->eap.password = NULL; @@ -1411,6 +1487,12 @@ static int wpa_config_parse_password(const struct parse_data *data, wpa_hexdump_key(MSG_MSGDUMP, data->name, hash, 16); + if (ssid->eap.password && ssid->eap.password_len == 16 && + os_memcmp(ssid->eap.password, hash, 16) == 0 && + (ssid->eap.flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH)) { + bin_clear_free(hash, 16); + return 1; + } bin_clear_free(ssid->eap.password, ssid->eap.password_len); ssid->eap.password = hash; ssid->eap.password_len = 16; @@ -1837,6 +1919,8 @@ static const struct parse_data ssid_fields[] = { { FUNC(auth_alg) }, { FUNC(scan_freq) }, { FUNC(freq_list) }, + { INT_RANGE(max_oper_chwidth, VHT_CHANWIDTH_USE_HT, + VHT_CHANWIDTH_80P80MHZ) }, #ifdef IEEE8021X_EAPOL { FUNC(eap) }, { STR_LENe(identity) }, @@ -1910,6 +1994,9 @@ static const struct parse_data ssid_fields[] = { { INT_RANGE(mixed_cell, 0, 1) }, { INT_RANGE(frequency, 0, 65000) }, { INT_RANGE(fixed_freq, 0, 1) }, +#ifdef CONFIG_ACS + { INT_RANGE(acs, 0, 1) }, +#endif /* CONFIG_ACS */ #ifdef CONFIG_MESH { FUNC(mesh_basic_rates) }, { INT(dot11MeshMaxRetries) }, @@ -1918,6 +2005,7 @@ static const struct parse_data ssid_fields[] = { { INT(dot11MeshHoldingTimeout) }, #endif /* CONFIG_MESH */ { INT(wpa_ptk_rekey) }, + { INT(group_rekey) }, { STR(bgscan) }, { INT_RANGE(ignore_broadcast_ssid, 0, 2) }, #ifdef CONFIG_P2P @@ -1967,6 +2055,8 @@ static const struct parse_data ssid_fields[] = { { INT(update_identifier) }, #endif /* CONFIG_HS20 */ { INT_RANGE(mac_addr, 0, 2) }, + { INT_RANGE(pbss, 0, 2) }, + { INT_RANGE(wps_disabled, 0, 1) }, }; #undef OFFSET @@ -2271,6 +2361,11 @@ void wpa_config_free(struct wpa_config *config) os_free(config->bgscan); os_free(config->wowlan_triggers); os_free(config->fst_group_id); + os_free(config->sched_scan_plans); +#ifdef CONFIG_MBO + os_free(config->non_pref_chan); +#endif /* CONFIG_MBO */ + os_free(config); } @@ -2453,7 +2548,8 @@ void wpa_config_set_network_defaults(struct wpa_ssid *ssid) * @var: Variable name, e.g., "ssid" * @value: Variable value * @line: Line number in configuration file or 0 if not used - * Returns: 0 on success, -1 on failure + * Returns: 0 on success with possible change in the value, 1 on success with + * no change to previously configured value, or -1 on failure * * This function can be used to set network configuration variables based on * both the configuration file and management interface input. The value @@ -2474,7 +2570,8 @@ int wpa_config_set(struct wpa_ssid *ssid, const char *var, const char *value, if (os_strcmp(var, field->name) != 0) continue; - if (field->parser(field, ssid, line, value)) { + ret = field->parser(field, ssid, line, value); + if (ret < 0) { if (line) { wpa_printf(MSG_ERROR, "Line %d: failed to " "parse %s '%s'.", line, var, value); @@ -2573,9 +2670,8 @@ char ** wpa_config_get_all(struct wpa_ssid *ssid, int get_keys) return props; err: - value = *props; - while (value) - os_free(value++); + for (i = 0; props[i]; i++) + os_free(props[i]); os_free(props); return NULL; #endif /* NO_CONFIG_WRITE */ @@ -2604,8 +2700,19 @@ char * wpa_config_get(struct wpa_ssid *ssid, const char *var) for (i = 0; i < NUM_SSID_FIELDS; i++) { const struct parse_data *field = &ssid_fields[i]; - if (os_strcmp(var, field->name) == 0) - return field->writer(field, ssid); + if (os_strcmp(var, field->name) == 0) { + char *ret = field->writer(field, ssid); + + if (ret && has_newline(ret)) { + wpa_printf(MSG_ERROR, + "Found newline in value for %s; not returning it", + var); + os_free(ret); + ret = NULL; + } + + return ret; + } } return NULL; @@ -2790,6 +2897,8 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var, if (os_strcmp(var, "password") == 0 && os_strncmp(value, "ext:", 4) == 0) { + if (has_newline(value)) + return -1; str_clear_free(cred->password); cred->password = os_strdup(value); cred->ext_password = 1; @@ -2840,9 +2949,14 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var, } val = wpa_config_parse_string(value, &len); - if (val == NULL) { + if (val == NULL || + (os_strcmp(var, "excluded_ssid") != 0 && + os_strcmp(var, "roaming_consortium") != 0 && + os_strcmp(var, "required_roaming_consortium") != 0 && + has_newline(val))) { wpa_printf(MSG_ERROR, "Line %d: invalid field '%s' string " "value '%s'.", line, var, value); + os_free(val); return -1; } @@ -3540,6 +3654,11 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface, config->rand_addr_lifetime = DEFAULT_RAND_ADDR_LIFETIME; config->key_mgmt_offload = DEFAULT_KEY_MGMT_OFFLOAD; config->cert_in_cb = DEFAULT_CERT_IN_CB; + config->wpa_rsc_relaxation = DEFAULT_WPA_RSC_RELAXATION; + +#ifdef CONFIG_MBO + config->mbo_cell_capa = DEFAULT_MBO_CELL_CAPA; +#endif /* CONFIG_MBO */ if (ctrl_interface) config->ctrl_interface = os_strdup(ctrl_interface); @@ -3646,6 +3765,12 @@ static int wpa_global_config_parse_str(const struct global_parse_data *data, return -1; } + if (has_newline(pos)) { + wpa_printf(MSG_ERROR, "Line %d: invalid %s value with newline", + line, data->name); + return -1; + } + tmp = os_strdup(pos); if (tmp == NULL) return -1; @@ -3684,22 +3809,12 @@ static int wpa_global_config_parse_bin(const struct global_parse_data *data, struct wpa_config *config, int line, const char *pos) { - size_t len; struct wpabuf **dst, *tmp; - len = os_strlen(pos); - if (len & 0x01) + tmp = wpabuf_parse_bin(pos); + if (!tmp) return -1; - tmp = wpabuf_alloc(len / 2); - if (tmp == NULL) - return -1; - - if (hexstr2bin(pos, wpabuf_put(tmp, len / 2), len / 2)) { - wpabuf_free(tmp); - return -1; - } - dst = (struct wpabuf **) (((u8 *) config) + (long) data->param1); wpabuf_free(*dst); *dst = tmp; @@ -4246,6 +4361,16 @@ static const struct global_parse_data global_fields[] = { { INT_RANGE(fst_priority, 1, FST_MAX_PRIO_VALUE), 0 }, { INT_RANGE(fst_llt, 1, FST_MAX_LLT_MS), 0 }, #endif /* CONFIG_FST */ + { INT_RANGE(wpa_rsc_relaxation, 0, 1), 0 }, + { STR(sched_scan_plans), CFG_CHANGED_SCHED_SCAN_PLANS }, +#ifdef CONFIG_MBO + { STR(non_pref_chan), 0 }, + { INT_RANGE(mbo_cell_capa, MBO_CELL_CAPA_AVAILABLE, + MBO_CELL_CAPA_NOT_SUPPORTED), 0 }, +#endif /*CONFIG_MBO */ + { INT(gas_address3), 0 }, + { INT_RANGE(ftm_responder, 0, 1), 0 }, + { INT_RANGE(ftm_initiator, 0, 1), 0 }, }; #undef FUNC @@ -4304,6 +4429,23 @@ int wpa_config_get_value(const char *name, struct wpa_config *config, } +int wpa_config_get_num_global_field_names(void) +{ + return NUM_GLOBAL_FIELDS; +} + + +const char * wpa_config_get_global_field_name(unsigned int i, int *no_var) +{ + if (i >= NUM_GLOBAL_FIELDS) + return NULL; + + if (no_var) + *no_var = !global_fields[i].param1; + return global_fields[i].name; +} + + int wpa_config_process_global(struct wpa_config *config, char *pos, int line) { size_t i; diff --git a/contrib/wpa/wpa_supplicant/config.h b/contrib/wpa/wpa_supplicant/config.h index 627f38b6e005..48e64be5da1a 100644 --- a/contrib/wpa/wpa_supplicant/config.h +++ b/contrib/wpa/wpa_supplicant/config.h @@ -39,6 +39,8 @@ #define DEFAULT_KEY_MGMT_OFFLOAD 1 #define DEFAULT_CERT_IN_CB 1 #define DEFAULT_P2P_GO_CTWINDOW 0 +#define DEFAULT_WPA_RSC_RELAXATION 1 +#define DEFAULT_MBO_CELL_CAPA MBO_CELL_CAPA_NOT_SUPPORTED #include "config_ssid.h" #include "wps/wps.h" @@ -331,6 +333,7 @@ struct wpa_cred { #define CFG_CHANGED_EXT_PW_BACKEND BIT(14) #define CFG_CHANGED_NFC_PASSWORD_TOKEN BIT(15) #define CFG_CHANGED_P2P_PASSPHRASE_LEN BIT(16) +#define CFG_CHANGED_SCHED_SCAN_PLANS BIT(17) /** * struct wpa_config - wpa_supplicant configuration data @@ -761,12 +764,17 @@ struct wpa_config { * frequency list of the local device and the peer device. * * @P2P_GO_FREQ_MOVE_STAY: Prefer to stay on the current frequency. + * + * @P2P_GO_FREQ_MOVE_SCM_ECSA: Same as + * P2P_GO_FREQ_MOVE_SCM_PEER_SUPPORTS but a transition is possible only + * if all the group members advertise eCSA support. */ enum { P2P_GO_FREQ_MOVE_SCM = 0, P2P_GO_FREQ_MOVE_SCM_PEER_SUPPORTS = 1, P2P_GO_FREQ_MOVE_STAY = 2, - P2P_GO_FREQ_MOVE_MAX = P2P_GO_FREQ_MOVE_STAY, + P2P_GO_FREQ_MOVE_SCM_ECSA = 3, + P2P_GO_FREQ_MOVE_MAX = P2P_GO_FREQ_MOVE_SCM_ECSA, } p2p_go_freq_change_policy; #define DEFAULT_P2P_GO_FREQ_MOVE P2P_GO_FREQ_MOVE_STAY @@ -1031,7 +1039,8 @@ struct wpa_config { * * By default, PMF is disabled unless enabled by the per-network * ieee80211w=1 or ieee80211w=2 parameter. pmf=1/2 can be used to change - * this default behavior. + * this default behavior for RSN network (this is not applicable for + * non-RSN cases). */ enum mfp_options pmf; @@ -1247,6 +1256,78 @@ struct wpa_config { * interface. */ int fst_llt; + + /** + * wpa_rsc_relaxation - RSC relaxation on GTK installation + * + * Values: + * 0 - use the EAPOL-Key RSC value on GTK installation + * 1 - use the null RSC if a bogus RSC value is detected in message 3 + * of 4-Way Handshake or message 1 of Group Key Handshake. + */ + int wpa_rsc_relaxation; + + /** + * sched_scan_plans - Scan plans for scheduled scan + * + * Each scan plan specifies the interval between scans and the number of + * iterations. The last scan plan only specifies the scan interval and + * will be run infinitely. + * + * format: ... + */ + char *sched_scan_plans; + +#ifdef CONFIG_MBO + /** + * non_pref_chan - Non-preferred channels list, separated by spaces. + * + * format: op_class:chan:preference:reason<:detail> + * Detail is optional. + */ + char *non_pref_chan; + + /** + * mbo_cell_capa - Cellular capabilities for MBO + */ + enum mbo_cellular_capa mbo_cell_capa; +#endif /* CONFIG_MBO */ + + /** + * gas_address3 - GAS Address3 field behavior + * + * Values: + * 0 - P2P specification (Address3 = AP BSSID) + * 1 = IEEE 802.11 standard compliant (Address3 = Wildcard BSSID when + * sent to not-associated AP; if associated, AP BSSID) + */ + int gas_address3; + + /** + * ftm_responder - Publish FTM (fine timing measurement) + * responder functionality + * + * Values: + * 0 - do not publish FTM responder functionality (Default) + * 1 - publish FTM responder functionality in + * bit 70 of Extended Capabilities element + * Note, actual FTM responder operation is managed outside + * wpa_supplicant. + */ + int ftm_responder; + + /** + * ftm_initiator - Publish FTM (fine timing measurement) + * initiator functionality + * + * Values: + * 0 - do not publish FTM initiator functionality (Default) + * 1 - publish FTM initiator functionality in + * bit 71 of Extended Capabilities element + * Note, actual FTM initiator operation is managed outside + * wpa_supplicant. + */ + int ftm_initiator; }; @@ -1305,6 +1386,9 @@ void wpa_config_debug_dump_networks(struct wpa_config *config); /* Prototypes for common functions from config.c */ int wpa_config_process_global(struct wpa_config *config, char *pos, int line); +int wpa_config_get_num_global_field_names(void); + +const char * wpa_config_get_global_field_name(unsigned int i, int *no_var); /* Prototypes for backend specific functions from the selected config_*.c */ diff --git a/contrib/wpa/wpa_supplicant/config_file.c b/contrib/wpa/wpa_supplicant/config_file.c index fb438ea43e13..7ae16545bebc 100644 --- a/contrib/wpa/wpa_supplicant/config_file.c +++ b/contrib/wpa/wpa_supplicant/config_file.c @@ -747,10 +747,16 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) INT(no_auto_peer); INT(frequency); INT(fixed_freq); +#ifdef CONFIG_ACS + INT(acs); +#endif /* CONFIG_ACS */ write_int(f, "proactive_key_caching", ssid->proactive_key_caching, -1); INT(disabled); INT(peerkey); INT(mixed_cell); + INT(max_oper_chwidth); + INT(pbss); + INT(wps_disabled); #ifdef CONFIG_IEEE80211W write_int(f, "ieee80211w", ssid->ieee80211w, MGMT_FRAME_PROTECTION_DEFAULT); @@ -779,6 +785,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) INT_DEF(dot11MeshHoldingTimeout, DEFAULT_MESH_HOLDING_TIMEOUT); #endif /* CONFIG_MESH */ INT(wpa_ptk_rekey); + INT(group_rekey); INT(ignore_broadcast_ssid); #ifdef CONFIG_HT_OVERRIDES INT_DEF(disable_ht, DEFAULT_DISABLE_HT); @@ -1136,6 +1143,22 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) if (config->p2p_go_freq_change_policy != DEFAULT_P2P_GO_FREQ_MOVE) fprintf(f, "p2p_go_freq_change_policy=%u\n", config->p2p_go_freq_change_policy); + if (WPA_GET_BE32(config->ip_addr_go)) + fprintf(f, "ip_addr_go=%u.%u.%u.%u\n", + config->ip_addr_go[0], config->ip_addr_go[1], + config->ip_addr_go[2], config->ip_addr_go[3]); + if (WPA_GET_BE32(config->ip_addr_mask)) + fprintf(f, "ip_addr_mask=%u.%u.%u.%u\n", + config->ip_addr_mask[0], config->ip_addr_mask[1], + config->ip_addr_mask[2], config->ip_addr_mask[3]); + if (WPA_GET_BE32(config->ip_addr_start)) + fprintf(f, "ip_addr_start=%u.%u.%u.%u\n", + config->ip_addr_start[0], config->ip_addr_start[1], + config->ip_addr_start[2], config->ip_addr_start[3]); + if (WPA_GET_BE32(config->ip_addr_end)) + fprintf(f, "ip_addr_end=%u.%u.%u.%u\n", + config->ip_addr_end[0], config->ip_addr_end[1], + config->ip_addr_end[2], config->ip_addr_end[3]); #endif /* CONFIG_P2P */ if (config->country[0] && config->country[1]) { fprintf(f, "country=%c%c\n", @@ -1299,6 +1322,28 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) if (config->wps_priority) fprintf(f, "wps_priority=%d\n", config->wps_priority); + + if (config->wpa_rsc_relaxation != DEFAULT_WPA_RSC_RELAXATION) + fprintf(f, "wpa_rsc_relaxation=%d\n", + config->wpa_rsc_relaxation); + + if (config->sched_scan_plans) + fprintf(f, "sched_scan_plans=%s\n", config->sched_scan_plans); + +#ifdef CONFIG_MBO + if (config->non_pref_chan) + fprintf(f, "non_pref_chan=%s\n", config->non_pref_chan); + if (config->mbo_cell_capa != DEFAULT_MBO_CELL_CAPA) + fprintf(f, "mbo_cell_capa=%u\n", config->mbo_cell_capa); +#endif /* CONFIG_MBO */ + + if (config->gas_address3) + fprintf(f, "gas_address3=%d\n", config->gas_address3); + + if (config->ftm_responder) + fprintf(f, "ftm_responder=%d\n", config->ftm_responder); + if (config->ftm_initiator) + fprintf(f, "ftm_initiator=%d\n", config->ftm_initiator); } #endif /* CONFIG_NO_CONFIG_WRITE */ diff --git a/contrib/wpa/wpa_supplicant/config_ssid.h b/contrib/wpa/wpa_supplicant/config_ssid.h index 7ef326cfbed6..010b594af85e 100644 --- a/contrib/wpa/wpa_supplicant/config_ssid.h +++ b/contrib/wpa/wpa_supplicant/config_ssid.h @@ -359,6 +359,19 @@ struct wpa_ssid { WPAS_MODE_MESH = 5, } mode; + /** + * pbss - Whether to use PBSS. Relevant to DMG networks only. + * 0 = do not use PBSS + * 1 = use PBSS + * 2 = don't care (not allowed in AP mode) + * Used together with mode configuration. When mode is AP, it + * means to start a PCP instead of a regular AP. When mode is INFRA it + * means connect to a PCP instead of AP. In this mode you can also + * specify 2 (don't care) meaning connect to either AP or PCP. + * P2P_GO and P2P_GROUP_FORMATION modes must use PBSS in DMG network. + */ + int pbss; + /** * disabled - Whether this network is currently disabled * @@ -431,6 +444,18 @@ struct wpa_ssid { */ int fixed_freq; +#ifdef CONFIG_ACS + /** + * ACS - Automatic Channel Selection for AP mode + * + * If present, it will be handled together with frequency. + * frequency will be used to determine hardware mode only, when it is + * used for both hardware mode and channel when used alone. This will + * force the channel to be set to 0, thus enabling ACS. + */ + int acs; +#endif /* CONFIG_ACS */ + /** * mesh_basic_rates - BSS Basic rate set for mesh network * @@ -449,6 +474,10 @@ struct wpa_ssid { int vht; + u8 max_oper_chwidth; + + unsigned int vht_center_freq2; + /** * wpa_ptk_rekey - Maximum lifetime for PTK in seconds * @@ -457,6 +486,14 @@ struct wpa_ssid { */ int wpa_ptk_rekey; + /** + * group_rekey - Group rekeying time in seconds + * + * This value, if non-zero, is used as the dot11RSNAConfigGroupRekeyTime + * parameter when operating in Authenticator role in IBSS. + */ + int group_rekey; + /** * scan_freq - Array of frequencies to scan or %NULL for all * @@ -719,6 +756,14 @@ struct wpa_ssid { * this MBSS will trigger a peering attempt. */ int no_auto_peer; + + /** + * wps_disabled - WPS disabled in AP mode + * + * 0 = WPS enabled and configured (default) + * 1 = WPS disabled + */ + int wps_disabled; }; #endif /* CONFIG_SSID_H */ diff --git a/contrib/wpa/wpa_supplicant/ctrl_iface.c b/contrib/wpa/wpa_supplicant/ctrl_iface.c index 25dcdf7ee321..d814fdf7fd2d 100644 --- a/contrib/wpa/wpa_supplicant/ctrl_iface.c +++ b/contrib/wpa/wpa_supplicant/ctrl_iface.c @@ -15,6 +15,7 @@ #include "utils/common.h" #include "utils/eloop.h" #include "utils/uuid.h" +#include "utils/module_tests.h" #include "common/version.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" @@ -55,6 +56,7 @@ static int wpa_supplicant_global_iface_list(struct wpa_global *global, char *buf, int len); static int wpa_supplicant_global_iface_interfaces(struct wpa_global *global, + const char *input, char *buf, int len); static int * freq_range_to_channel_list(struct wpa_supplicant *wpa_s, char *val); @@ -310,6 +312,33 @@ static int wpas_ctrl_set_band(struct wpa_supplicant *wpa_s, char *band) } +static int wpas_ctrl_iface_set_lci(struct wpa_supplicant *wpa_s, + const char *cmd) +{ + struct wpabuf *lci; + + if (*cmd == '\0' || os_strcmp(cmd, "\"\"") == 0) { + wpabuf_free(wpa_s->lci); + wpa_s->lci = NULL; + return 0; + } + + lci = wpabuf_parse_bin(cmd); + if (!lci) + return -1; + + if (os_get_reltime(&wpa_s->lci_time)) { + wpabuf_free(lci); + return -1; + } + + wpabuf_free(wpa_s->lci); + wpa_s->lci = lci; + + return 0; +} + + static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, char *cmd) { @@ -371,6 +400,20 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, wps_corrupt_pkhash = atoi(value); wpa_printf(MSG_DEBUG, "WPS: Testing - wps_corrupt_pkhash=%d", wps_corrupt_pkhash); + } else if (os_strcasecmp(cmd, "wps_force_auth_types") == 0) { + if (value[0] == '\0') { + wps_force_auth_types_in_use = 0; + } else { + wps_force_auth_types = strtol(value, NULL, 0); + wps_force_auth_types_in_use = 1; + } + } else if (os_strcasecmp(cmd, "wps_force_encr_types") == 0) { + if (value[0] == '\0') { + wps_force_encr_types_in_use = 0; + } else { + wps_force_encr_types = strtol(value, NULL, 0); + wps_force_encr_types_in_use = 1; + } #endif /* CONFIG_WPS_TESTING */ } else if (os_strcasecmp(cmd, "ampdu") == 0) { if (wpa_drv_ampdu(wpa_s, atoi(value)) < 0) @@ -378,7 +421,6 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, #ifdef CONFIG_TDLS #ifdef CONFIG_TDLS_TESTING } else if (os_strcasecmp(cmd, "tdls_testing") == 0) { - extern unsigned int tdls_testing; tdls_testing = strtol(value, NULL, 0); wpa_printf(MSG_DEBUG, "TDLS: tdls_testing=0x%x", tdls_testing); #endif /* CONFIG_TDLS_TESTING */ @@ -467,6 +509,14 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, wpa_s->extra_roc_dur = atoi(value); } else if (os_strcasecmp(cmd, "test_failure") == 0) { wpa_s->test_failure = atoi(value); + } else if (os_strcasecmp(cmd, "p2p_go_csa_on_inv") == 0) { + wpa_s->p2p_go_csa_on_inv = !!atoi(value); + } else if (os_strcasecmp(cmd, "ignore_auth_resp") == 0) { + wpa_s->ignore_auth_resp = !!atoi(value); + } else if (os_strcasecmp(cmd, "ignore_assoc_disallow") == 0) { + wpa_s->ignore_assoc_disallow = !!atoi(value); + } else if (os_strcasecmp(cmd, "reject_btm_req_reason") == 0) { + wpa_s->reject_btm_req_reason = atoi(value); #endif /* CONFIG_TESTING_OPTIONS */ #ifndef CONFIG_NO_CONFIG_BLOBS } else if (os_strcmp(cmd, "blob") == 0) { @@ -474,6 +524,14 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, #endif /* CONFIG_NO_CONFIG_BLOBS */ } else if (os_strcasecmp(cmd, "setband") == 0) { ret = wpas_ctrl_set_band(wpa_s, value); +#ifdef CONFIG_MBO + } else if (os_strcasecmp(cmd, "non_pref_chan") == 0) { + ret = wpas_mbo_update_non_pref_chan(wpa_s, value); + } else if (os_strcasecmp(cmd, "mbo_cell_capa") == 0) { + wpas_mbo_update_cell_capa(wpa_s, atoi(value)); +#endif /* CONFIG_MBO */ + } else if (os_strcasecmp(cmd, "lci") == 0) { + ret = wpas_ctrl_iface_set_lci(wpa_s, value); } else { value[-1] = '='; ret = wpa_config_process_global(wpa_s->conf, cmd, -1); @@ -940,7 +998,8 @@ static int wpa_supplicant_ctrl_iface_wps_pin(struct wpa_supplicant *wpa_s, if (os_strcmp(cmd, "any") == 0) _bssid = NULL; else if (os_strcmp(cmd, "get") == 0) { - ret = wps_generate_pin(); + if (wps_generate_pin((unsigned int *) &ret) < 0) + return -1; goto done; } else if (hwaddr_aton(cmd, bssid)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PIN: invalid BSSID '%s'", @@ -1833,6 +1892,10 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, "mode=P2P GO - group " "formation\n"); break; + case WPAS_MODE_MESH: + ret = os_snprintf(pos, end - pos, + "mode=mesh\n"); + break; default: ret = 0; break; @@ -2703,6 +2766,40 @@ static int wpa_supplicant_ctrl_iface_mesh_group_remove( return 0; } + +static int wpa_supplicant_ctrl_iface_mesh_peer_remove( + struct wpa_supplicant *wpa_s, char *cmd) +{ + u8 addr[ETH_ALEN]; + + if (hwaddr_aton(cmd, addr) < 0) + return -1; + + return wpas_mesh_peer_remove(wpa_s, addr); +} + + +static int wpa_supplicant_ctrl_iface_mesh_peer_add( + struct wpa_supplicant *wpa_s, char *cmd) +{ + u8 addr[ETH_ALEN]; + int duration; + char *pos; + + pos = os_strstr(cmd, " duration="); + if (pos) { + *pos = '\0'; + duration = atoi(pos + 10); + } else { + duration = -1; + } + + if (hwaddr_aton(cmd, addr)) + return -1; + + return wpas_mesh_peer_add(wpa_s, addr, duration); +} + #endif /* CONFIG_MESH */ @@ -2832,15 +2929,10 @@ static int wpa_supplicant_ctrl_iface_add_network( wpa_printf(MSG_DEBUG, "CTRL_IFACE: ADD_NETWORK"); - ssid = wpa_config_add_network(wpa_s->conf); + ssid = wpa_supplicant_add_network(wpa_s); if (ssid == NULL) return -1; - wpas_notify_network_added(wpa_s, ssid); - - ssid->disabled = 1; - wpa_config_set_network_defaults(ssid); - ret = os_snprintf(buf, buflen, "%d\n", ssid->id); if (os_snprintf_error(buflen, ret)) return -1; @@ -2853,7 +2945,7 @@ static int wpa_supplicant_ctrl_iface_remove_network( { int id; struct wpa_ssid *ssid; - int was_disabled; + int result; /* cmd: "" or "all" */ if (os_strcmp(cmd, "all") == 0) { @@ -2889,54 +2981,17 @@ static int wpa_supplicant_ctrl_iface_remove_network( id = atoi(cmd); wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_NETWORK id=%d", id); - ssid = wpa_config_get_network(wpa_s->conf, id); - if (ssid) - wpas_notify_network_removed(wpa_s, ssid); - if (ssid == NULL) { + result = wpa_supplicant_remove_network(wpa_s, id); + if (result == -1) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network " "id=%d", id); return -1; } - - if (wpa_s->last_ssid == ssid) - wpa_s->last_ssid = NULL; - - if (ssid == wpa_s->current_ssid || wpa_s->current_ssid == NULL) { -#ifdef CONFIG_SME - wpa_s->sme.prev_bssid_set = 0; -#endif /* CONFIG_SME */ - /* - * Invalidate the EAP session cache if the current or - * previously used network is removed. - */ - eapol_sm_invalidate_cached_session(wpa_s->eapol); - } - - if (ssid == wpa_s->current_ssid) { - wpa_sm_set_config(wpa_s->wpa, NULL); - eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); - - if (wpa_s->wpa_state >= WPA_AUTHENTICATING) - wpa_s->own_disconnect_req = 1; - wpa_supplicant_deauthenticate(wpa_s, - WLAN_REASON_DEAUTH_LEAVING); - } - - was_disabled = ssid->disabled; - - if (wpa_config_remove_network(wpa_s->conf, id) < 0) { + if (result == -2) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Not able to remove the " "network id=%d", id); return -1; } - - if (!was_disabled && wpa_s->sched_scanning) { - wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to remove " - "network from filters"); - wpa_supplicant_cancel_sched_scan(wpa_s); - wpa_supplicant_req_scan(wpa_s, 0, 0); - } - return 0; } @@ -2945,22 +3000,29 @@ static int wpa_supplicant_ctrl_iface_update_network( struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, char *name, char *value) { - if (wpa_config_set(ssid, name, value, 0) < 0) { + int ret; + + ret = wpa_config_set(ssid, name, value, 0); + if (ret < 0) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to set network " "variable '%s'", name); return -1; } + if (ret == 1) + return 0; /* No change to the previously configured value */ if (os_strcmp(name, "bssid") != 0 && - os_strcmp(name, "priority") != 0) + os_strcmp(name, "priority") != 0) { wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid); - if (wpa_s->current_ssid == ssid || wpa_s->current_ssid == NULL) { - /* - * Invalidate the EAP session cache if anything in the current - * or previously used configuration changes. - */ - eapol_sm_invalidate_cached_session(wpa_s->eapol); + if (wpa_s->current_ssid == ssid || + wpa_s->current_ssid == NULL) { + /* + * Invalidate the EAP session cache if anything in the + * current or previously used configuration changes. + */ + eapol_sm_invalidate_cached_session(wpa_s->eapol); + } } if ((os_strcmp(name, "psk") == 0 && @@ -3935,6 +3997,15 @@ static int wpa_supplicant_ctrl_iface_get_capability( } #endif /* CONFIG_FIPS */ +#ifdef CONFIG_ACS + if (os_strcmp(field, "acs") == 0) { + res = os_snprintf(buf, buflen, "ACS"); + if (os_snprintf_error(buflen, res)) + return -1; + return res; + } +#endif /* CONFIG_ACS */ + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'", field); @@ -4195,9 +4266,10 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, if (mask & WPA_BSS_MASK_P2P_SCAN) { ie = (const u8 *) (bss + 1); ret = wpas_p2p_scan_result_text(ie, bss->ie_len, pos, end); - if (ret < 0 || ret >= end - pos) + if (ret >= end - pos) return 0; - pos += ret; + if (ret > 0) + pos += ret; } #endif /* CONFIG_P2P */ @@ -4231,6 +4303,8 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, #ifdef CONFIG_INTERWORKING if ((mask & WPA_BSS_MASK_INTERNETW) && bss->anqp) { struct wpa_bss_anqp *anqp = bss->anqp; + struct wpa_bss_anqp_elem *elem; + pos = anqp_add_hex(pos, end, "anqp_capability_list", anqp->capability_list); pos = anqp_add_hex(pos, end, "anqp_venue_name", @@ -4260,6 +4334,15 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, pos = anqp_add_hex(pos, end, "hs20_osu_providers_list", anqp->hs20_osu_providers_list); #endif /* CONFIG_HS20 */ + + dl_list_for_each(elem, &anqp->anqp_elems, + struct wpa_bss_anqp_elem, list) { + char title[20]; + + os_snprintf(title, sizeof(title), "anqp[%u]", + elem->infoid); + pos = anqp_add_hex(pos, end, title, elem->payload); + } } #endif /* CONFIG_INTERWORKING */ @@ -4267,9 +4350,10 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, if (mask & WPA_BSS_MASK_MESH_SCAN) { ie = (const u8 *) (bss + 1); ret = wpas_mesh_scan_result_text(ie, bss->ie_len, pos, end); - if (ret < 0 || ret >= end - pos) + if (ret >= end - pos) return 0; - pos += ret; + if (ret > 0) + pos += ret; } #endif /* CONFIG_MESH */ @@ -4676,7 +4760,7 @@ static int p2ps_ctrl_parse_cpt_priority(const char *pos, u8 *cpt) return -1; } - if (isblank(*last)) { + if (isblank((unsigned char) *last)) { i++; break; } @@ -4848,6 +4932,30 @@ static int p2p_ctrl_asp_provision(struct wpa_supplicant *wpa_s, char *cmd) } +static int parse_freq(int chwidth, int freq2) +{ + if (freq2 < 0) + return -1; + if (freq2) + return VHT_CHANWIDTH_80P80MHZ; + + switch (chwidth) { + case 0: + case 20: + case 40: + return VHT_CHANWIDTH_USE_HT; + case 80: + return VHT_CHANWIDTH_80MHZ; + case 160: + return VHT_CHANWIDTH_160MHZ; + default: + wpa_printf(MSG_DEBUG, "Unknown max oper bandwidth: %d", + chwidth); + return -1; + } +} + + static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { @@ -4864,7 +4972,9 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd, int go_intent = -1; int freq = 0; int pd; - int ht40, vht; + int ht40, vht, max_oper_chwidth, chwidth = 0, freq2 = 0; + u8 _group_ssid[SSID_MAX_LEN], *group_ssid = NULL; + size_t group_ssid_len = 0; if (!wpa_s->global->p2p_init_wpa_s) return -1; @@ -4877,7 +4987,7 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd, /* <"pbc" | "pin" | PIN> [label|display|keypad|p2ps] * [persistent|persistent=] * [join] [auth] [go_intent=<0..15>] [freq=] [provdisc] - * [ht40] [vht] [auto] */ + * [ht40] [vht] [auto] [ssid=] */ if (hwaddr_aton(cmd, addr)) return -1; @@ -4925,11 +5035,41 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd, return -1; } + pos2 = os_strstr(pos, " freq2="); + if (pos2) + freq2 = atoi(pos2 + 7); + + pos2 = os_strstr(pos, " max_oper_chwidth="); + if (pos2) + chwidth = atoi(pos2 + 18); + + max_oper_chwidth = parse_freq(chwidth, freq2); + if (max_oper_chwidth < 0) + return -1; + + pos2 = os_strstr(pos, " ssid="); + if (pos2) { + char *end; + + pos2 += 6; + end = os_strchr(pos2, ' '); + if (!end) + group_ssid_len = os_strlen(pos2) / 2; + else + group_ssid_len = (end - pos2) / 2; + if (group_ssid_len == 0 || group_ssid_len > SSID_MAX_LEN || + hexstr2bin(pos2, _group_ssid, group_ssid_len) < 0) + return -1; + group_ssid = _group_ssid; + } + if (os_strncmp(pos, "pin", 3) == 0) { /* Request random PIN (to be displayed) and enable the PIN */ wps_method = WPS_PIN_DISPLAY; } else if (os_strncmp(pos, "pbc", 3) == 0) { wps_method = WPS_PBC; + } else if (os_strstr(pos, "p2ps") != NULL) { + wps_method = WPS_P2PS; } else { pin = pos; pos = os_strchr(pin, ' '); @@ -4938,8 +5078,6 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd, *pos++ = '\0'; if (os_strncmp(pos, "display", 7) == 0) wps_method = WPS_PIN_DISPLAY; - else if (os_strncmp(pos, "p2ps", 4) == 0) - wps_method = WPS_P2PS; } if (!wps_pin_str_valid(pin)) { os_memcpy(buf, "FAIL-INVALID-PIN\n", 17); @@ -4949,8 +5087,9 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd, new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method, persistent_group, automatic, join, - auth, go_intent, freq, persistent_id, pd, - ht40, vht); + auth, go_intent, freq, freq2, persistent_id, + pd, ht40, vht, max_oper_chwidth, + group_ssid, group_ssid_len); if (new_pin == -2) { os_memcpy(buf, "FAIL-CHANNEL-UNAVAILABLE\n", 25); return 25; @@ -5505,7 +5644,7 @@ static int p2p_ctrl_invite_persistent(struct wpa_supplicant *wpa_s, char *cmd) struct wpa_ssid *ssid; u8 *_peer = NULL, peer[ETH_ALEN]; int freq = 0, pref_freq = 0; - int ht40, vht; + int ht40, vht, max_oper_chwidth, chwidth = 0, freq2 = 0; id = atoi(cmd); pos = os_strstr(cmd, " peer="); @@ -5543,8 +5682,20 @@ static int p2p_ctrl_invite_persistent(struct wpa_supplicant *wpa_s, char *cmd) ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40 || vht; - return wpas_p2p_invite(wpa_s, _peer, ssid, NULL, freq, ht40, vht, - pref_freq); + pos = os_strstr(cmd, "freq2="); + if (pos) + freq2 = atoi(pos + 6); + + pos = os_strstr(cmd, " max_oper_chwidth="); + if (pos) + chwidth = atoi(pos + 18); + + max_oper_chwidth = parse_freq(chwidth, freq2); + if (max_oper_chwidth < 0) + return -1; + + return wpas_p2p_invite(wpa_s, _peer, ssid, NULL, freq, freq2, ht40, vht, + max_oper_chwidth, pref_freq); } @@ -5591,7 +5742,8 @@ static int p2p_ctrl_invite(struct wpa_supplicant *wpa_s, char *cmd) static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s, - int id, int freq, int ht40, int vht) + int id, int freq, int vht_center_freq2, + int ht40, int vht, int vht_chwidth) { struct wpa_ssid *ssid; @@ -5603,8 +5755,9 @@ static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s, return -1; } - return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0, ht40, vht, - NULL, 0, 0); + return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, + vht_center_freq2, 0, ht40, vht, + vht_chwidth, NULL, 0, 0); } @@ -5613,11 +5766,14 @@ static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd) int freq = 0, persistent = 0, group_id = -1; int vht = wpa_s->conf->p2p_go_vht; int ht40 = wpa_s->conf->p2p_go_ht40 || vht; + int max_oper_chwidth, chwidth = 0, freq2 = 0; char *token, *context = NULL; while ((token = str_token(cmd, " ", &context))) { if (sscanf(token, "freq=%d", &freq) == 1 || - sscanf(token, "persistent=%d", &group_id) == 1) { + sscanf(token, "freq2=%d", &freq2) == 1 || + sscanf(token, "persistent=%d", &group_id) == 1 || + sscanf(token, "max_oper_chwidth=%d", &chwidth) == 1) { continue; } else if (os_strcmp(token, "ht40") == 0) { ht40 = 1; @@ -5634,11 +5790,40 @@ static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd) } } + max_oper_chwidth = parse_freq(chwidth, freq2); + if (max_oper_chwidth < 0) + return -1; + if (group_id >= 0) return p2p_ctrl_group_add_persistent(wpa_s, group_id, - freq, ht40, vht); + freq, freq2, ht40, vht, + max_oper_chwidth); - return wpas_p2p_group_add(wpa_s, persistent, freq, ht40, vht); + return wpas_p2p_group_add(wpa_s, persistent, freq, freq2, ht40, vht, + max_oper_chwidth); +} + + +static int p2p_ctrl_group_member(struct wpa_supplicant *wpa_s, const char *cmd, + char *buf, size_t buflen) +{ + u8 dev_addr[ETH_ALEN]; + struct wpa_ssid *ssid; + int res; + const u8 *iaddr; + + ssid = wpa_s->current_ssid; + if (!wpa_s->global->p2p || !ssid || ssid->mode != WPAS_MODE_P2P_GO || + hwaddr_aton(cmd, dev_addr)) + return -1; + + iaddr = p2p_group_get_client_interface_addr(wpa_s->p2p_group, dev_addr); + if (!iaddr) + return -1; + res = os_snprintf(buf, buflen, MACSTR, MAC2STR(iaddr)); + if (os_snprintf_error(buflen, res)) + return -1; + return res; } @@ -5797,8 +5982,15 @@ static int p2p_ctrl_set(struct wpa_supplicant *wpa_s, char *cmd) } if (os_strcmp(cmd, "listen_channel") == 0) { - return p2p_set_listen_channel(wpa_s->global->p2p, 81, - atoi(param), 1); + char *pos; + u8 channel, op_class; + + channel = atoi(param); + pos = os_strchr(param, ' '); + op_class = pos ? atoi(pos) : 81; + + return p2p_set_listen_channel(wpa_s->global->p2p, op_class, + channel, 1); } if (os_strcmp(cmd, "ssid_postfix") == 0) { @@ -6059,6 +6251,21 @@ static int p2p_ctrl_remove_client(struct wpa_supplicant *wpa_s, const char *cmd) return 0; } + +static int p2p_ctrl_iface_p2p_lo_start(struct wpa_supplicant *wpa_s, char *cmd) +{ + int freq = 0, period = 0, interval = 0, count = 0; + + if (sscanf(cmd, "%d %d %d %d", &freq, &period, &interval, &count) != 4) + { + wpa_printf(MSG_DEBUG, + "CTRL: Invalid P2P LO Start parameter: '%s'", cmd); + return -1; + } + + return wpas_p2p_lo_start(wpa_s, freq, period, interval, count); +} + #endif /* CONFIG_P2P */ @@ -6176,6 +6383,7 @@ static int get_anqp(struct wpa_supplicant *wpa_s, char *dst) u16 id[MAX_ANQP_INFO_ID]; size_t num_id = 0; u32 subtypes = 0; + int get_cell_pref = 0; used = hwaddr_aton2(dst, dst_addr); if (used < 0) @@ -6193,6 +6401,15 @@ static int get_anqp(struct wpa_supplicant *wpa_s, char *dst) #else /* CONFIG_HS20 */ return -1; #endif /* CONFIG_HS20 */ + } else if (os_strncmp(pos, "mbo:", 4) == 0) { +#ifdef CONFIG_MBO + int num = atoi(pos + 4); + if (num != MBO_ANQP_SUBTYPE_CELL_CONN_PREF) + return -1; + get_cell_pref = 1; +#else /* CONFIG_MBO */ + return -1; +#endif /* CONFIG_MBO */ } else { id[num_id] = atoi(pos); if (id[num_id]) @@ -6207,7 +6424,8 @@ static int get_anqp(struct wpa_supplicant *wpa_s, char *dst) if (num_id == 0) return -1; - return anqp_send_req(wpa_s, dst_addr, id, num_id, subtypes); + return anqp_send_req(wpa_s, dst_addr, id, num_id, subtypes, + get_cell_pref); } @@ -6378,7 +6596,7 @@ static int get_hs20_anqp(struct wpa_supplicant *wpa_s, char *dst) if (subtypes == 0) return -1; - return hs20_anqp_send_req(wpa_s, dst_addr, subtypes, NULL, 0); + return hs20_anqp_send_req(wpa_s, dst_addr, subtypes, NULL, 0, 0); } @@ -6401,7 +6619,7 @@ static int hs20_nai_home_realm_list(struct wpa_supplicant *wpa_s, ret = hs20_anqp_send_req(wpa_s, addr, BIT(HS20_STYPE_NAI_HOME_REALM_QUERY), - buf, len); + buf, len, 0); os_free(buf); @@ -6447,14 +6665,59 @@ static int hs20_get_nai_home_realm_list(struct wpa_supplicant *wpa_s, ret = hs20_anqp_send_req(wpa_s, dst_addr, BIT(HS20_STYPE_NAI_HOME_REALM_QUERY), - buf, len); + buf, len, 0); os_free(buf); return ret; } -static int hs20_icon_request(struct wpa_supplicant *wpa_s, char *cmd) +static int get_hs20_icon(struct wpa_supplicant *wpa_s, char *cmd, char *reply, + int buflen) +{ + u8 dst_addr[ETH_ALEN]; + int used; + char *ctx = NULL, *icon, *poffset, *psize; + + used = hwaddr_aton2(cmd, dst_addr); + if (used < 0) + return -1; + cmd += used; + + icon = str_token(cmd, " ", &ctx); + poffset = str_token(cmd, " ", &ctx); + psize = str_token(cmd, " ", &ctx); + if (!icon || !poffset || !psize) + return -1; + + wpa_s->fetch_osu_icon_in_progress = 0; + return hs20_get_icon(wpa_s, dst_addr, icon, atoi(poffset), atoi(psize), + reply, buflen); +} + + +static int del_hs20_icon(struct wpa_supplicant *wpa_s, char *cmd) +{ + u8 dst_addr[ETH_ALEN]; + int used; + char *icon; + + if (!cmd[0]) + return hs20_del_icon(wpa_s, NULL, NULL); + + used = hwaddr_aton2(cmd, dst_addr); + if (used < 0) + return -1; + + while (cmd[used] == ' ') + used++; + icon = cmd[used] ? &cmd[used] : NULL; + + return hs20_del_icon(wpa_s, dst_addr, icon); +} + + +static int hs20_icon_request(struct wpa_supplicant *wpa_s, char *cmd, int inmem) { u8 dst_addr[ETH_ALEN]; int used; @@ -6470,7 +6733,7 @@ static int hs20_icon_request(struct wpa_supplicant *wpa_s, char *cmd) wpa_s->fetch_osu_icon_in_progress = 0; return hs20_anqp_send_req(wpa_s, dst_addr, BIT(HS20_STYPE_ICON_REQUEST), - (u8 *) icon, os_strlen(icon)); + (u8 *) icon, os_strlen(icon), inmem); } #endif /* CONFIG_HS20 */ @@ -6560,14 +6823,27 @@ static int wpas_ctrl_iface_wnm_sleep(struct wpa_supplicant *wpa_s, char *cmd) static int wpas_ctrl_iface_wnm_bss_query(struct wpa_supplicant *wpa_s, char *cmd) { - int query_reason; + int query_reason, list = 0; query_reason = atoi(cmd); - wpa_printf(MSG_DEBUG, "CTRL_IFACE: WNM_BSS_QUERY query_reason=%d", - query_reason); + cmd = os_strchr(cmd, ' '); + if (cmd) { + cmd++; + if (os_strncmp(cmd, "list", 4) == 0) { + list = 1; + } else { + wpa_printf(MSG_DEBUG, "WNM Query: Invalid option %s", + cmd); + return -1; + } + } - return wnm_send_bss_transition_mgmt_query(wpa_s, query_reason); + wpa_printf(MSG_DEBUG, + "CTRL_IFACE: WNM_BSS_QUERY query_reason=%d%s", + query_reason, list ? " candidate list" : ""); + + return wnm_send_bss_transition_mgmt_query(wpa_s, query_reason, list); } #endif /* CONFIG_WNM */ @@ -6632,6 +6908,28 @@ static int wpa_supplicant_signal_poll(struct wpa_supplicant *wpa_s, char *buf, } +static int wpas_ctrl_iface_signal_monitor(struct wpa_supplicant *wpa_s, + const char *cmd) +{ + const char *pos; + int threshold = 0; + int hysteresis = 0; + + if (wpa_s->bgscan && wpa_s->bgscan_priv) { + wpa_printf(MSG_DEBUG, + "Reject SIGNAL_MONITOR command - bgscan is active"); + return -1; + } + pos = os_strstr(cmd, "THRESHOLD="); + if (pos) + threshold = atoi(pos + 10); + pos = os_strstr(cmd, "HYSTERESIS="); + if (pos) + hysteresis = atoi(pos + 11); + return wpa_drv_signal_monitor(wpa_s, threshold, hysteresis); +} + + static int wpas_ctrl_iface_get_pref_freq_list( struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { @@ -6679,6 +6977,34 @@ static int wpas_ctrl_iface_get_pref_freq_list( } +static int wpas_ctrl_iface_driver_flags(struct wpa_supplicant *wpa_s, + char *buf, size_t buflen) +{ + int ret, i; + char *pos, *end; + + ret = os_snprintf(buf, buflen, "%016llX:\n", + (long long unsigned) wpa_s->drv_flags); + if (os_snprintf_error(buflen, ret)) + return -1; + + pos = buf + ret; + end = buf + buflen; + + for (i = 0; i < 64; i++) { + if (wpa_s->drv_flags & (1LLU << i)) { + ret = os_snprintf(pos, end - pos, "%s\n", + driver_flag_to_string(1LLU << i)); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } + } + + return pos - buf; +} + + static int wpa_supplicant_pktcnt_poll(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { @@ -6736,13 +7062,13 @@ static int wpa_supplicant_vendor_cmd(struct wpa_supplicant *wpa_s, char *cmd, /* cmd: [] */ vendor_id = strtoul(cmd, &pos, 16); - if (!isblank(*pos)) + if (!isblank((unsigned char) *pos)) return -EINVAL; subcmd = strtoul(pos, &pos, 10); if (*pos != '\0') { - if (!isblank(*pos++)) + if (!isblank((unsigned char) *pos++)) return -EINVAL; data_len = os_strlen(pos); } @@ -6790,10 +7116,20 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) wpa_dbg(wpa_s, MSG_DEBUG, "Flush all wpa_supplicant state"); + wpas_abort_ongoing_scan(wpa_s); + + if (wpa_s->wpa_state >= WPA_AUTHENTICATING) { + /* + * Avoid possible auto connect re-connection on getting + * disconnected due to state flush. + */ + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); + } + #ifdef CONFIG_P2P + wpas_p2p_group_remove(p2p_wpa_s, "*"); wpas_p2p_cancel(p2p_wpa_s); p2p_ctrl_flush(p2p_wpa_s); - wpas_p2p_group_remove(p2p_wpa_s, "*"); wpas_p2p_service_flush(p2p_wpa_s); p2p_wpa_s->global->p2p_disabled = 0; p2p_wpa_s->global->p2p_per_sta_psk = 0; @@ -6803,12 +7139,15 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) p2p_wpa_s->global->p2p_go_avoid_freq.range = NULL; p2p_wpa_s->global->p2p_go_avoid_freq.num = 0; p2p_wpa_s->global->pending_p2ps_group = 0; + p2p_wpa_s->global->pending_p2ps_group_freq = 0; #endif /* CONFIG_P2P */ #ifdef CONFIG_WPS_TESTING wps_version_number = 0x20; wps_testing_dummy_cred = 0; wps_corrupt_pkhash = 0; + wps_force_auth_types_in_use = 0; + wps_force_encr_types_in_use = 0; #endif /* CONFIG_WPS_TESTING */ #ifdef CONFIG_WPS wpa_s->wps_fragment_size = 0; @@ -6820,7 +7159,6 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) #ifdef CONFIG_TDLS #ifdef CONFIG_TDLS_TESTING - extern unsigned int tdls_testing; tdls_testing = 0; #endif /* CONFIG_TDLS_TESTING */ wpa_drv_tdls_oper(wpa_s, TDLS_ENABLE, NULL); @@ -6866,7 +7204,10 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) wpa_s->next_ssid = NULL; #ifdef CONFIG_INTERWORKING +#ifdef CONFIG_HS20 hs20_cancel_fetch_osu(wpa_s); + hs20_del_icon(wpa_s, NULL, NULL); +#endif /* CONFIG_HS20 */ #endif /* CONFIG_INTERWORKING */ wpa_s->ext_mgmt_frame_handling = 0; @@ -6874,6 +7215,11 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) #ifdef CONFIG_TESTING_OPTIONS wpa_s->extra_roc_dur = 0; wpa_s->test_failure = WPAS_TEST_FAILURE_NONE; + wpa_s->p2p_go_csa_on_inv = 0; + wpa_s->ignore_auth_resp = 0; + wpa_s->ignore_assoc_disallow = 0; + wpa_s->reject_btm_req_reason = 0; + wpa_sm_set_test_assoc_ie(wpa_s->wpa, NULL); #endif /* CONFIG_TESTING_OPTIONS */ wpa_s->disconnected = 0; @@ -6892,6 +7238,10 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL); wpa_s->wnmsleep_used = 0; + +#ifdef CONFIG_SME + wpa_s->sme.last_unprot_disconnect.sec = 0; +#endif /* CONFIG_SME */ } @@ -6948,6 +7298,13 @@ static void wpas_ctrl_radio_work_cb(struct wpa_radio_work *work, int deinit) eloop_cancel_timeout(wpas_ctrl_radio_work_timeout, work, NULL); + /* + * work->type points to a buffer in ework, so need to replace + * that here with a fixed string to avoid use of freed memory + * in debug prints. + */ + work->type = "freed-ext-work"; + work->ctx = NULL; os_free(ework); return; } @@ -7397,6 +7754,76 @@ static void wpas_ctrl_iface_mgmt_tx_done(struct wpa_supplicant *wpa_s) } +static int wpas_ctrl_iface_mgmt_rx_process(struct wpa_supplicant *wpa_s, + char *cmd) +{ + char *pos, *param; + size_t len; + u8 *buf; + int freq = 0, datarate = 0, ssi_signal = 0; + union wpa_event_data event; + + if (!wpa_s->ext_mgmt_frame_handling) + return -1; + + /* freq= datarate= ssi_signal= frame= */ + + wpa_printf(MSG_DEBUG, "External MGMT RX process: %s", cmd); + + pos = cmd; + param = os_strstr(pos, "freq="); + if (param) { + param += 5; + freq = atoi(param); + } + + param = os_strstr(pos, " datarate="); + if (param) { + param += 10; + datarate = atoi(param); + } + + param = os_strstr(pos, " ssi_signal="); + if (param) { + param += 12; + ssi_signal = atoi(param); + } + + param = os_strstr(pos, " frame="); + if (param == NULL) + return -1; + param += 7; + + len = os_strlen(param); + if (len & 1) + return -1; + len /= 2; + + buf = os_malloc(len); + if (buf == NULL) + return -1; + + if (hexstr2bin(param, buf, len) < 0) { + os_free(buf); + return -1; + } + + os_memset(&event, 0, sizeof(event)); + event.rx_mgmt.freq = freq; + event.rx_mgmt.frame = buf; + event.rx_mgmt.frame_len = len; + event.rx_mgmt.ssi_signal = ssi_signal; + event.rx_mgmt.datarate = datarate; + wpa_s->ext_mgmt_frame_handling = 0; + wpa_supplicant_event(wpa_s, EVENT_RX_MGMT, &event); + wpa_s->ext_mgmt_frame_handling = 1; + + os_free(buf); + + return 0; +} + + static int wpas_ctrl_iface_driver_event(struct wpa_supplicant *wpa_s, char *cmd) { char *pos, *param; @@ -7496,7 +7923,8 @@ static u16 ipv4_hdr_checksum(const void *buf, size_t len) #define HWSIM_PACKETLEN 1500 #define HWSIM_IP_LEN (HWSIM_PACKETLEN - sizeof(struct ether_header)) -void wpas_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) +static void wpas_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) { struct wpa_supplicant *wpa_s = ctx; const struct ether_header *eth; @@ -7530,6 +7958,8 @@ static int wpas_ctrl_iface_data_test_config(struct wpa_supplicant *wpa_s, char *cmd) { int enabled = atoi(cmd); + char *pos; + const char *ifname; if (!enabled) { if (wpa_s->l2_test) { @@ -7543,7 +7973,13 @@ static int wpas_ctrl_iface_data_test_config(struct wpa_supplicant *wpa_s, if (wpa_s->l2_test) return 0; - wpa_s->l2_test = l2_packet_init(wpa_s->ifname, wpa_s->own_addr, + pos = os_strstr(cmd, " ifname="); + if (pos) + ifname = pos + 8; + else + ifname = wpa_s->ifname; + + wpa_s->l2_test = l2_packet_init(ifname, wpa_s->own_addr, ETHERTYPE_IP, wpas_data_test_rx, wpa_s, 1); if (wpa_s->l2_test == NULL) @@ -7664,8 +8100,6 @@ done: static int wpas_ctrl_test_alloc_fail(struct wpa_supplicant *wpa_s, char *cmd) { #ifdef WPA_TRACE_BFD - extern char wpa_trace_fail_func[256]; - extern unsigned int wpa_trace_fail_after; char *pos; wpa_trace_fail_after = atoi(cmd); @@ -7688,9 +8122,6 @@ static int wpas_ctrl_get_alloc_fail(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { #ifdef WPA_TRACE_BFD - extern char wpa_trace_fail_func[256]; - extern unsigned int wpa_trace_fail_after; - return os_snprintf(buf, buflen, "%u:%s", wpa_trace_fail_after, wpa_trace_fail_func); #else /* WPA_TRACE_BFD */ @@ -7702,8 +8133,6 @@ static int wpas_ctrl_get_alloc_fail(struct wpa_supplicant *wpa_s, static int wpas_ctrl_test_fail(struct wpa_supplicant *wpa_s, char *cmd) { #ifdef WPA_TRACE_BFD - extern char wpa_trace_test_fail_func[256]; - extern unsigned int wpa_trace_test_fail_after; char *pos; wpa_trace_test_fail_after = atoi(cmd); @@ -7726,9 +8155,6 @@ static int wpas_ctrl_get_fail(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { #ifdef WPA_TRACE_BFD - extern char wpa_trace_test_fail_func[256]; - extern unsigned int wpa_trace_test_fail_after; - return os_snprintf(buf, buflen, "%u:%s", wpa_trace_test_fail_after, wpa_trace_test_fail_func); #else /* WPA_TRACE_BFD */ @@ -7736,61 +8162,63 @@ static int wpas_ctrl_get_fail(struct wpa_supplicant *wpa_s, #endif /* WPA_TRACE_BFD */ } -#endif /* CONFIG_TESTING_OPTIONS */ - -static void wpas_ctrl_vendor_elem_update(struct wpa_supplicant *wpa_s) +static void wpas_ctrl_event_test_cb(void *eloop_ctx, void *timeout_ctx) { - unsigned int i; - char buf[30]; + struct wpa_supplicant *wpa_s = eloop_ctx; + int i, count = (intptr_t) timeout_ctx; - wpa_printf(MSG_DEBUG, "Update vendor elements"); + wpa_printf(MSG_DEBUG, "TEST: Send %d control interface event messages", + count); + for (i = 0; i < count; i++) { + wpa_msg_ctrl(wpa_s, MSG_INFO, "TEST-EVENT-MESSAGE %d/%d", + i + 1, count); + } +} - for (i = 0; i < NUM_VENDOR_ELEM_FRAMES; i++) { - if (wpa_s->vendor_elem[i]) { - int res; - res = os_snprintf(buf, sizeof(buf), "frame[%u]", i); - if (!os_snprintf_error(sizeof(buf), res)) { - wpa_hexdump_buf(MSG_DEBUG, buf, - wpa_s->vendor_elem[i]); - } +static int wpas_ctrl_event_test(struct wpa_supplicant *wpa_s, const char *cmd) +{ + int count; + + count = atoi(cmd); + if (count <= 0) + return -1; + + return eloop_register_timeout(0, 0, wpas_ctrl_event_test_cb, wpa_s, + (void *) (intptr_t) count); +} + + +static int wpas_ctrl_test_assoc_ie(struct wpa_supplicant *wpa_s, + const char *cmd) +{ + struct wpabuf *buf; + size_t len; + + len = os_strlen(cmd); + if (len & 1) + return -1; + len /= 2; + + if (len == 0) { + buf = NULL; + } else { + buf = wpabuf_alloc(len); + if (buf == NULL) + return -1; + + if (hexstr2bin(cmd, wpabuf_put(buf, len), len) < 0) { + wpabuf_free(buf); + return -1; } } -#ifdef CONFIG_P2P - if (wpa_s->parent == wpa_s && - wpa_s->global->p2p && - !wpa_s->global->p2p_disabled) - p2p_set_vendor_elems(wpa_s->global->p2p, wpa_s->vendor_elem); -#endif /* CONFIG_P2P */ + wpa_sm_set_test_assoc_ie(wpa_s->wpa, buf); + return 0; } - -static struct wpa_supplicant * -wpas_ctrl_vendor_elem_iface(struct wpa_supplicant *wpa_s, - enum wpa_vendor_elem_frame frame) -{ - switch (frame) { -#ifdef CONFIG_P2P - case VENDOR_ELEM_PROBE_REQ_P2P: - case VENDOR_ELEM_PROBE_RESP_P2P: - case VENDOR_ELEM_PROBE_RESP_P2P_GO: - case VENDOR_ELEM_BEACON_P2P_GO: - case VENDOR_ELEM_P2P_PD_REQ: - case VENDOR_ELEM_P2P_PD_RESP: - case VENDOR_ELEM_P2P_GO_NEG_REQ: - case VENDOR_ELEM_P2P_GO_NEG_RESP: - case VENDOR_ELEM_P2P_GO_NEG_CONF: - case VENDOR_ELEM_P2P_INV_REQ: - case VENDOR_ELEM_P2P_INV_RESP: - case VENDOR_ELEM_P2P_ASSOC_REQ: - return wpa_s->parent; -#endif /* CONFIG_P2P */ - default: - return wpa_s; - } -} +#endif /* CONFIG_TESTING_OPTIONS */ static int wpas_ctrl_vendor_elem_add(struct wpa_supplicant *wpa_s, char *cmd) @@ -7804,7 +8232,7 @@ static int wpas_ctrl_vendor_elem_add(struct wpa_supplicant *wpa_s, char *cmd) frame = atoi(pos); if (frame < 0 || frame >= NUM_VENDOR_ELEM_FRAMES) return -1; - wpa_s = wpas_ctrl_vendor_elem_iface(wpa_s, frame); + wpa_s = wpas_vendor_elem(wpa_s, frame); pos = os_strchr(pos, ' '); if (pos == NULL) @@ -7835,7 +8263,7 @@ static int wpas_ctrl_vendor_elem_add(struct wpa_supplicant *wpa_s, char *cmd) if (wpa_s->vendor_elem[frame] == NULL) { wpa_s->vendor_elem[frame] = buf; - wpas_ctrl_vendor_elem_update(wpa_s); + wpas_vendor_elem_update(wpa_s); return 0; } @@ -7846,7 +8274,7 @@ static int wpas_ctrl_vendor_elem_add(struct wpa_supplicant *wpa_s, char *cmd) wpabuf_put_buf(wpa_s->vendor_elem[frame], buf); wpabuf_free(buf); - wpas_ctrl_vendor_elem_update(wpa_s); + wpas_vendor_elem_update(wpa_s); return 0; } @@ -7859,7 +8287,7 @@ static int wpas_ctrl_vendor_elem_get(struct wpa_supplicant *wpa_s, char *cmd, if (frame < 0 || frame >= NUM_VENDOR_ELEM_FRAMES) return -1; - wpa_s = wpas_ctrl_vendor_elem_iface(wpa_s, frame); + wpa_s = wpas_vendor_elem(wpa_s, frame); if (wpa_s->vendor_elem[frame] == NULL) return 0; @@ -7877,12 +8305,12 @@ static int wpas_ctrl_vendor_elem_remove(struct wpa_supplicant *wpa_s, char *cmd) size_t len; u8 *buf; struct ieee802_11_elems elems; - u8 *ie, *end; + int res; frame = atoi(pos); if (frame < 0 || frame >= NUM_VENDOR_ELEM_FRAMES) return -1; - wpa_s = wpas_ctrl_vendor_elem_iface(wpa_s, frame); + wpa_s = wpas_vendor_elem(wpa_s, frame); pos = os_strchr(pos, ' '); if (pos == NULL) @@ -7892,7 +8320,7 @@ static int wpas_ctrl_vendor_elem_remove(struct wpa_supplicant *wpa_s, char *cmd) if (*pos == '*') { wpabuf_free(wpa_s->vendor_elem[frame]); wpa_s->vendor_elem[frame] = NULL; - wpas_ctrl_vendor_elem_update(wpa_s); + wpas_vendor_elem_update(wpa_s); return 0; } @@ -7920,65 +8348,149 @@ static int wpas_ctrl_vendor_elem_remove(struct wpa_supplicant *wpa_s, char *cmd) return -1; } - ie = wpabuf_mhead_u8(wpa_s->vendor_elem[frame]); - end = ie + wpabuf_len(wpa_s->vendor_elem[frame]); - - for (; ie + 1 < end; ie += 2 + ie[1]) { - if (ie + len > end) - break; - if (os_memcmp(ie, buf, len) != 0) - continue; - - if (wpabuf_len(wpa_s->vendor_elem[frame]) == len) { - wpabuf_free(wpa_s->vendor_elem[frame]); - wpa_s->vendor_elem[frame] = NULL; - } else { - os_memmove(ie, ie + len, - end - (ie + len)); - wpa_s->vendor_elem[frame]->used -= len; - } - os_free(buf); - wpas_ctrl_vendor_elem_update(wpa_s); - return 0; - } - + res = wpas_vendor_elem_remove(wpa_s, frame, buf, len); os_free(buf); - - return -1; + return res; } static void wpas_ctrl_neighbor_rep_cb(void *ctx, struct wpabuf *neighbor_rep) { struct wpa_supplicant *wpa_s = ctx; + size_t len; + const u8 *data; - if (neighbor_rep) { - wpa_msg_ctrl(wpa_s, MSG_INFO, RRM_EVENT_NEIGHBOR_REP_RXED - "length=%u", - (unsigned int) wpabuf_len(neighbor_rep)); - wpabuf_free(neighbor_rep); - } else { + /* + * Neighbor Report element (IEEE P802.11-REVmc/D5.0) + * BSSID[6] + * BSSID Information[4] + * Operating Class[1] + * Channel Number[1] + * PHY Type[1] + * Optional Subelements[variable] + */ +#define NR_IE_MIN_LEN (ETH_ALEN + 4 + 1 + 1 + 1) + + if (!neighbor_rep || wpabuf_len(neighbor_rep) == 0) { wpa_msg_ctrl(wpa_s, MSG_INFO, RRM_EVENT_NEIGHBOR_REP_FAILED); + goto out; } + + data = wpabuf_head_u8(neighbor_rep); + len = wpabuf_len(neighbor_rep); + + while (len >= 2 + NR_IE_MIN_LEN) { + const u8 *nr; + char lci[256 * 2 + 1]; + char civic[256 * 2 + 1]; + u8 nr_len = data[1]; + const u8 *pos = data, *end; + + if (pos[0] != WLAN_EID_NEIGHBOR_REPORT || + nr_len < NR_IE_MIN_LEN) { + wpa_printf(MSG_DEBUG, + "CTRL: Invalid Neighbor Report element: id=%u len=%u", + data[0], nr_len); + goto out; + } + + if (2U + nr_len > len) { + wpa_printf(MSG_DEBUG, + "CTRL: Invalid Neighbor Report element: id=%u len=%zu nr_len=%u", + data[0], len, nr_len); + goto out; + } + pos += 2; + end = pos + nr_len; + + nr = pos; + pos += NR_IE_MIN_LEN; + + lci[0] = '\0'; + civic[0] = '\0'; + while (end - pos > 2) { + u8 s_id, s_len; + + s_id = *pos++; + s_len = *pos++; + if (s_len > end - pos) + goto out; + if (s_id == WLAN_EID_MEASURE_REPORT && s_len > 3) { + /* Measurement Token[1] */ + /* Measurement Report Mode[1] */ + /* Measurement Type[1] */ + /* Measurement Report[variable] */ + switch (pos[2]) { + case MEASURE_TYPE_LCI: + if (lci[0]) + break; + wpa_snprintf_hex(lci, sizeof(lci), + pos, s_len); + break; + case MEASURE_TYPE_LOCATION_CIVIC: + if (civic[0]) + break; + wpa_snprintf_hex(civic, sizeof(civic), + pos, s_len); + break; + } + } + + pos += s_len; + } + + wpa_msg(wpa_s, MSG_INFO, RRM_EVENT_NEIGHBOR_REP_RXED + "bssid=" MACSTR + " info=0x%x op_class=%u chan=%u phy_type=%u%s%s%s%s", + MAC2STR(nr), WPA_GET_LE32(nr + ETH_ALEN), + nr[ETH_ALEN + 4], nr[ETH_ALEN + 5], + nr[ETH_ALEN + 6], + lci[0] ? " lci=" : "", lci, + civic[0] ? " civic=" : "", civic); + + data = end; + len -= 2 + nr_len; + } + +out: + wpabuf_free(neighbor_rep); } -static int wpas_ctrl_iface_send_neigbor_rep(struct wpa_supplicant *wpa_s, - char *cmd) +static int wpas_ctrl_iface_send_neighbor_rep(struct wpa_supplicant *wpa_s, + char *cmd) { - struct wpa_ssid ssid; - struct wpa_ssid *ssid_p = NULL; - int ret = 0; + struct wpa_ssid_value ssid, *ssid_p = NULL; + int ret, lci = 0, civic = 0; + char *ssid_s; - if (os_strncmp(cmd, " ssid=", 6) == 0) { - ssid.ssid_len = os_strlen(cmd + 6); - if (ssid.ssid_len > SSID_MAX_LEN) + ssid_s = os_strstr(cmd, "ssid="); + if (ssid_s) { + if (ssid_parse(ssid_s + 5, &ssid)) { + wpa_printf(MSG_ERROR, + "CTRL: Send Neighbor Report: bad SSID"); return -1; - ssid.ssid = (u8 *) (cmd + 6); + } + ssid_p = &ssid; + + /* + * Move cmd after the SSID text that may include "lci" or + * "civic". + */ + cmd = os_strchr(ssid_s + 6, ssid_s[5] == '"' ? '"' : ' '); + if (cmd) + cmd++; + } - ret = wpas_rrm_send_neighbor_rep_request(wpa_s, ssid_p, + if (cmd && os_strstr(cmd, "lci")) + lci = 1; + + if (cmd && os_strstr(cmd, "civic")) + civic = 1; + + ret = wpas_rrm_send_neighbor_rep_request(wpa_s, ssid_p, lci, civic, wpas_ctrl_neighbor_rep_cb, wpa_s); @@ -8063,10 +8575,7 @@ static int wpas_ctrl_iface_mac_rand_scan(struct wpa_supplicant *wpa_s, } } else if (wpa_s->sched_scanning && (type & MAC_ADDR_RAND_SCHED_SCAN)) { - /* simulate timeout to restart the sched scan */ - wpa_s->sched_scan_timed_out = 1; - wpa_s->prev_sched_ssid = NULL; - wpa_supplicant_cancel_sched_scan(wpa_s); + wpas_scan_restart_sched_scan(wpa_s); } return 0; } @@ -8092,12 +8601,8 @@ static int wpas_ctrl_iface_mac_rand_scan(struct wpa_supplicant *wpa_s, wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_SCHED_SCAN, addr, mask); - if (wpa_s->sched_scanning && !wpa_s->pno) { - /* simulate timeout to restart the sched scan */ - wpa_s->sched_scan_timed_out = 1; - wpa_s->prev_sched_ssid = NULL; - wpa_supplicant_cancel_sched_scan(wpa_s); - } + if (wpa_s->sched_scanning && !wpa_s->pno) + wpas_scan_restart_sched_scan(wpa_s); } if (type & MAC_ADDR_RAND_PNO) { @@ -8113,6 +8618,29 @@ static int wpas_ctrl_iface_mac_rand_scan(struct wpa_supplicant *wpa_s, } +static int wpas_ctrl_iface_pmksa(struct wpa_supplicant *wpa_s, + char *buf, size_t buflen) +{ + size_t reply_len; + + reply_len = wpa_sm_pmksa_cache_list(wpa_s->wpa, buf, buflen); +#ifdef CONFIG_AP + reply_len += wpas_ap_pmksa_cache_list(wpa_s, &buf[reply_len], + buflen - reply_len); +#endif /* CONFIG_AP */ + return reply_len; +} + + +static void wpas_ctrl_iface_pmksa_flush(struct wpa_supplicant *wpa_s) +{ + wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL); +#ifdef CONFIG_AP + wpas_ap_pmksa_cache_flush(wpa_s); +#endif /* CONFIG_AP */ +} + + static int wpas_ctrl_cmd_debug_level(const char *cmd) { if (os_strcmp(cmd, "PING") == 0 || @@ -8184,10 +8712,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, reply_len = wpa_supplicant_ctrl_iface_status( wpa_s, buf + 6, reply, reply_size); } else if (os_strcmp(buf, "PMKSA") == 0) { - reply_len = wpa_sm_pmksa_cache_list(wpa_s->wpa, reply, - reply_size); + reply_len = wpas_ctrl_iface_pmksa(wpa_s, reply, reply_size); } else if (os_strcmp(buf, "PMKSA_FLUSH") == 0) { - wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL); + wpas_ctrl_iface_pmksa_flush(wpa_s); } else if (os_strncmp(buf, "SET ", 4) == 0) { if (wpa_supplicant_ctrl_iface_set(wpa_s, buf + 4)) reply_len = -1; @@ -8355,6 +8882,12 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, if (wpa_supplicant_ctrl_iface_mesh_group_remove(wpa_s, buf + 18)) reply_len = -1; + } else if (os_strncmp(buf, "MESH_PEER_REMOVE ", 17) == 0) { + if (wpa_supplicant_ctrl_iface_mesh_peer_remove(wpa_s, buf + 17)) + reply_len = -1; + } else if (os_strncmp(buf, "MESH_PEER_ADD ", 14) == 0) { + if (wpa_supplicant_ctrl_iface_mesh_peer_add(wpa_s, buf + 14)) + reply_len = -1; #endif /* CONFIG_MESH */ #ifdef CONFIG_P2P } else if (os_strncmp(buf, "P2P_FIND ", 9) == 0) { @@ -8389,6 +8922,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "P2P_GROUP_ADD ", 14) == 0) { if (p2p_ctrl_group_add(wpa_s, buf + 14)) reply_len = -1; + } else if (os_strncmp(buf, "P2P_GROUP_MEMBER ", 17) == 0) { + reply_len = p2p_ctrl_group_member(wpa_s, buf + 17, reply, + reply_size); } else if (os_strncmp(buf, "P2P_PROV_DISC ", 14) == 0) { if (p2p_ctrl_prov_disc(wpa_s, buf + 14)) reply_len = -1; @@ -8454,6 +8990,12 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "P2P_REMOVE_CLIENT ", 18) == 0) { if (p2p_ctrl_remove_client(wpa_s, buf + 18) < 0) reply_len = -1; + } else if (os_strncmp(buf, "P2P_LO_START ", 13) == 0) { + if (p2p_ctrl_iface_p2p_lo_start(wpa_s, buf + 13)) + reply_len = -1; + } else if (os_strcmp(buf, "P2P_LO_STOP") == 0) { + if (wpas_p2p_lo_stop(wpa_s)) + reply_len = -1; #endif /* CONFIG_P2P */ #ifdef CONFIG_WIFI_DISPLAY } else if (os_strncmp(buf, "WFD_SUBELEM_SET ", 16) == 0) { @@ -8507,10 +9049,21 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, if (hs20_get_nai_home_realm_list(wpa_s, buf + 29) < 0) reply_len = -1; } else if (os_strncmp(buf, "HS20_ICON_REQUEST ", 18) == 0) { - if (hs20_icon_request(wpa_s, buf + 18) < 0) + if (hs20_icon_request(wpa_s, buf + 18, 0) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "REQ_HS20_ICON ", 14) == 0) { + if (hs20_icon_request(wpa_s, buf + 14, 1) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "GET_HS20_ICON ", 14) == 0) { + reply_len = get_hs20_icon(wpa_s, buf + 14, reply, reply_size); + } else if (os_strncmp(buf, "DEL_HS20_ICON ", 14) == 0) { + if (del_hs20_icon(wpa_s, buf + 14) < 0) reply_len = -1; } else if (os_strcmp(buf, "FETCH_OSU") == 0) { - if (hs20_fetch_osu(wpa_s) < 0) + if (hs20_fetch_osu(wpa_s, 0) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "FETCH_OSU no-scan") == 0) { + if (hs20_fetch_osu(wpa_s, 1) < 0) reply_len = -1; } else if (os_strcmp(buf, "CANCEL_FETCH_OSU") == 0) { hs20_cancel_fetch_osu(wpa_s); @@ -8549,16 +9102,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, reply_len = wpa_supplicant_ctrl_iface_list_networks( wpa_s, NULL, reply, reply_size); } else if (os_strcmp(buf, "DISCONNECT") == 0) { -#ifdef CONFIG_SME - wpa_s->sme.prev_bssid_set = 0; -#endif /* CONFIG_SME */ - wpa_s->reassociate = 0; - wpa_s->disconnected = 1; - wpa_supplicant_cancel_sched_scan(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); + wpas_request_disconnection(wpa_s); } else if (os_strcmp(buf, "SCAN") == 0) { wpas_ctrl_scan(wpa_s, NULL, reply, reply_size, &reply_len); } else if (os_strncmp(buf, "SCAN ", 5) == 0) { @@ -8566,6 +9110,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strcmp(buf, "SCAN_RESULTS") == 0) { reply_len = wpa_supplicant_ctrl_iface_scan_results( wpa_s, reply, reply_size); + } else if (os_strcmp(buf, "ABORT_SCAN") == 0) { + if (wpas_abort_ongoing_scan(wpa_s) < 0) + reply_len = -1; } else if (os_strncmp(buf, "SELECT_NETWORK ", 15) == 0) { if (wpa_supplicant_ctrl_iface_select_network(wpa_s, buf + 15)) reply_len = -1; @@ -8624,9 +9171,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strcmp(buf, "INTERFACE_LIST") == 0) { reply_len = wpa_supplicant_global_iface_list( wpa_s->global, reply, reply_size); - } else if (os_strcmp(buf, "INTERFACES") == 0) { + } else if (os_strncmp(buf, "INTERFACES", 10) == 0) { reply_len = wpa_supplicant_global_iface_interfaces( - wpa_s->global, reply, reply_size); + wpa_s->global, buf + 10, reply, reply_size); } else if (os_strncmp(buf, "BSS ", 4) == 0) { reply_len = wpa_supplicant_ctrl_iface_bss( wpa_s, buf + 4, reply, reply_size); @@ -8707,6 +9254,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "SIGNAL_POLL", 11) == 0) { reply_len = wpa_supplicant_signal_poll(wpa_s, reply, reply_size); + } else if (os_strncmp(buf, "SIGNAL_MONITOR", 14) == 0) { + if (wpas_ctrl_iface_signal_monitor(wpa_s, buf + 14)) + reply_len = -1; } else if (os_strncmp(buf, "PKTCNT_POLL", 11) == 0) { reply_len = wpa_supplicant_pktcnt_poll(wpa_s, reply, reply_size); @@ -8715,6 +9265,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, if (wpa_supplicant_ctrl_iface_autoscan(wpa_s, buf + 9)) reply_len = -1; #endif /* CONFIG_AUTOSCAN */ + } else if (os_strcmp(buf, "DRIVER_FLAGS") == 0) { + reply_len = wpas_ctrl_iface_driver_flags(wpa_s, reply, + reply_size); #ifdef ANDROID } else if (os_strncmp(buf, "DRIVER ", 7) == 0) { reply_len = wpa_supplicant_driver_cmd(wpa_s, buf + 7, reply, @@ -8745,6 +9298,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, reply_len = -1; } else if (os_strcmp(buf, "MGMT_TX_DONE") == 0) { wpas_ctrl_iface_mgmt_tx_done(wpa_s); + } else if (os_strncmp(buf, "MGMT_RX_PROCESS ", 16) == 0) { + if (wpas_ctrl_iface_mgmt_rx_process(wpa_s, buf + 16) < 0) + reply_len = -1; } else if (os_strncmp(buf, "DRIVER_EVENT ", 13) == 0) { if (wpas_ctrl_iface_driver_event(wpa_s, buf + 13) < 0) reply_len = -1; @@ -8770,6 +9326,12 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, reply_len = -1; } else if (os_strcmp(buf, "GET_FAIL") == 0) { reply_len = wpas_ctrl_get_fail(wpa_s, reply, reply_size); + } else if (os_strncmp(buf, "EVENT_TEST ", 11) == 0) { + if (wpas_ctrl_event_test(wpa_s, buf + 11) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "TEST_ASSOC_IE ", 14) == 0) { + if (wpas_ctrl_test_assoc_ie(wpa_s, buf + 14) < 0) + reply_len = -1; #endif /* CONFIG_TESTING_OPTIONS */ } else if (os_strncmp(buf, "VENDOR_ELEM_ADD ", 16) == 0) { if (wpas_ctrl_vendor_elem_add(wpa_s, buf + 16) < 0) @@ -8781,7 +9343,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, if (wpas_ctrl_vendor_elem_remove(wpa_s, buf + 19) < 0) reply_len = -1; } else if (os_strncmp(buf, "NEIGHBOR_REP_REQUEST", 20) == 0) { - if (wpas_ctrl_iface_send_neigbor_rep(wpa_s, buf + 20)) + if (wpas_ctrl_iface_send_neighbor_rep(wpa_s, buf + 20)) reply_len = -1; } else if (os_strcmp(buf, "ERP_FLUSH") == 0) { wpas_ctrl_iface_erp_flush(wpa_s); @@ -8814,10 +9376,11 @@ static int wpa_supplicant_global_iface_add(struct wpa_global *global, struct wpa_supplicant *wpa_s; unsigned int create_iface = 0; u8 mac_addr[ETH_ALEN]; + enum wpa_driver_if_type type = WPA_IF_STATION; /* * TABTABTABTAB - * TAB[TAB] + * TAB[TAB[TAB]] */ wpa_printf(MSG_DEBUG, "CTRL_IFACE GLOBAL INTERFACE_ADD '%s'", cmd); @@ -8885,9 +9448,22 @@ static int wpa_supplicant_global_iface_add(struct wpa_global *global, if (!extra[0]) break; - if (os_strcmp(extra, "create") == 0) + if (os_strcmp(extra, "create") == 0) { create_iface = 1; - else { + if (!pos) + break; + + if (os_strcmp(pos, "sta") == 0) { + type = WPA_IF_STATION; + } else if (os_strcmp(pos, "ap") == 0) { + type = WPA_IF_AP_BSS; + } else { + wpa_printf(MSG_DEBUG, + "INTERFACE_ADD unsupported interface type: '%s'", + pos); + return -1; + } + } else { wpa_printf(MSG_DEBUG, "INTERFACE_ADD unsupported extra parameter: '%s'", extra); @@ -8900,7 +9476,7 @@ static int wpa_supplicant_global_iface_add(struct wpa_global *global, iface.ifname); if (!global->ifaces) return -1; - if (wpa_drv_if_add(global->ifaces, WPA_IF_STATION, iface.ifname, + if (wpa_drv_if_add(global->ifaces, type, iface.ifname, NULL, NULL, NULL, mac_addr, NULL) < 0) { wpa_printf(MSG_ERROR, "CTRL_IFACE interface creation failed"); @@ -9009,18 +9585,31 @@ static int wpa_supplicant_global_iface_list(struct wpa_global *global, static int wpa_supplicant_global_iface_interfaces(struct wpa_global *global, + const char *input, char *buf, int len) { int res; char *pos, *end; struct wpa_supplicant *wpa_s; + int show_ctrl = 0; + + if (input) + show_ctrl = !!os_strstr(input, "ctrl"); wpa_s = global->ifaces; pos = buf; end = buf + len; while (wpa_s) { - res = os_snprintf(pos, end - pos, "%s\n", wpa_s->ifname); + if (show_ctrl) + res = os_snprintf(pos, end - pos, "%s ctrl_iface=%s\n", + wpa_s->ifname, + wpa_s->conf->ctrl_interface ? + wpa_s->conf->ctrl_interface : "N/A"); + else + res = os_snprintf(pos, end - pos, "%s\n", + wpa_s->ifname); + if (os_snprintf_error(end - pos, res)) { *pos = '\0'; break; @@ -9086,6 +9675,7 @@ static char * wpas_global_ctrl_iface_redir_p2p(struct wpa_global *global, "P2P_LISTEN ", "P2P_GROUP_REMOVE ", "P2P_GROUP_ADD ", + "P2P_GROUP_MEMBER ", "P2P_PROV_DISC ", "P2P_SERV_DISC_REQ ", "P2P_SERV_DISC_CANCEL_REQ ", @@ -9409,9 +9999,9 @@ char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global, } else if (os_strcmp(buf, "INTERFACE_LIST") == 0) { reply_len = wpa_supplicant_global_iface_list( global, reply, reply_size); - } else if (os_strcmp(buf, "INTERFACES") == 0) { + } else if (os_strncmp(buf, "INTERFACES", 10) == 0) { reply_len = wpa_supplicant_global_iface_interfaces( - global, reply, reply_size); + global, buf + 10, reply, reply_size); #ifdef CONFIG_FST } else if (os_strncmp(buf, "FST-ATTACH ", 11) == 0) { reply_len = wpas_global_ctrl_iface_fst_attach(global, buf + 11, @@ -9457,7 +10047,6 @@ char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global, reply_size); #ifdef CONFIG_MODULE_TESTS } else if (os_strcmp(buf, "MODULE_TESTS") == 0) { - int wpas_module_tests(void); if (wpas_module_tests() < 0) reply_len = -1; #endif /* CONFIG_MODULE_TESTS */ diff --git a/contrib/wpa/wpa_supplicant/ctrl_iface_udp.c b/contrib/wpa/wpa_supplicant/ctrl_iface_udp.c index 76f69f2b57bb..0dc0937ff0aa 100644 --- a/contrib/wpa/wpa_supplicant/ctrl_iface_udp.c +++ b/contrib/wpa/wpa_supplicant/ctrl_iface_udp.c @@ -1,6 +1,6 @@ /* * WPA Supplicant / UDP socket -based control interface - * Copyright (c) 2004-2005, Jouni Malinen + * Copyright (c) 2004-2016, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -48,13 +48,33 @@ struct ctrl_iface_priv { u8 cookie[COOKIE_LEN]; }; +struct ctrl_iface_global_priv { + int sock; + struct wpa_ctrl_dst *ctrl_dst; + u8 cookie[COOKIE_LEN]; +}; -static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, + +static void wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s, + const char *ifname, int sock, + struct wpa_ctrl_dst **head, int level, const char *buf, size_t len); -static int wpa_supplicant_ctrl_iface_attach(struct ctrl_iface_priv *priv, +static void wpas_ctrl_iface_free_dst(struct wpa_ctrl_dst *dst) +{ + struct wpa_ctrl_dst *prev; + + while (dst) { + prev = dst; + dst = dst->next; + os_free(prev); + } +} + + +static int wpa_supplicant_ctrl_iface_attach(struct wpa_ctrl_dst **head, #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 struct sockaddr_in6 *from, #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ @@ -73,8 +93,8 @@ static int wpa_supplicant_ctrl_iface_attach(struct ctrl_iface_priv *priv, os_memcpy(&dst->addr, from, sizeof(*from)); dst->addrlen = fromlen; dst->debug_level = MSG_INFO; - dst->next = priv->ctrl_dst; - priv->ctrl_dst = dst; + dst->next = *head; + *head = dst; #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached %s:%d", inet_ntop(AF_INET6, &from->sin6_addr, addr, sizeof(*from)), @@ -87,7 +107,7 @@ static int wpa_supplicant_ctrl_iface_attach(struct ctrl_iface_priv *priv, } -static int wpa_supplicant_ctrl_iface_detach(struct ctrl_iface_priv *priv, +static int wpa_supplicant_ctrl_iface_detach(struct wpa_ctrl_dst **head, #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 struct sockaddr_in6 *from, #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ @@ -100,7 +120,7 @@ static int wpa_supplicant_ctrl_iface_detach(struct ctrl_iface_priv *priv, char addr[INET6_ADDRSTRLEN]; #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ - dst = priv->ctrl_dst; + dst = *head; while (dst) { #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 if (from->sin6_port == dst->addr.sin6_port && @@ -118,7 +138,7 @@ static int wpa_supplicant_ctrl_iface_detach(struct ctrl_iface_priv *priv, ntohs(from->sin_port)); #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ if (prev == NULL) - priv->ctrl_dst = dst->next; + *head = dst->next; else prev->next = dst->next; os_free(dst); @@ -282,14 +302,16 @@ static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, pos++; if (os_strcmp(pos, "ATTACH") == 0) { - if (wpa_supplicant_ctrl_iface_attach(priv, &from, fromlen)) + if (wpa_supplicant_ctrl_iface_attach(&priv->ctrl_dst, + &from, fromlen)) reply_len = 1; else { new_attached = 1; reply_len = 2; } } else if (os_strcmp(pos, "DETACH") == 0) { - if (wpa_supplicant_ctrl_iface_detach(priv, &from, fromlen)) + if (wpa_supplicant_ctrl_iface_detach(&priv->ctrl_dst, + &from, fromlen)) reply_len = 1; else reply_len = 2; @@ -327,9 +349,28 @@ static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, const char *txt, size_t len) { struct wpa_supplicant *wpa_s = ctx; - if (wpa_s == NULL || wpa_s->ctrl_iface == NULL) + + if (!wpa_s) return; - wpa_supplicant_ctrl_iface_send(wpa_s->ctrl_iface, level, txt, len); + + if (type != WPA_MSG_NO_GLOBAL && wpa_s->global->ctrl_iface) { + struct ctrl_iface_global_priv *priv = wpa_s->global->ctrl_iface; + + if (priv->ctrl_dst) { + wpa_supplicant_ctrl_iface_send( + wpa_s, + type != WPA_MSG_PER_INTERFACE ? + NULL : wpa_s->ifname, + priv->sock, &priv->ctrl_dst, level, txt, len); + } + } + + if (type == WPA_MSG_ONLY_GLOBAL || !wpa_s->ctrl_iface) + return; + + wpa_supplicant_ctrl_iface_send(wpa_s, NULL, wpa_s->ctrl_iface->sock, + &wpa_s->ctrl_iface->ctrl_dst, + level, txt, len); } @@ -337,7 +378,9 @@ struct ctrl_iface_priv * wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) { struct ctrl_iface_priv *priv; + char port_str[40]; int port = WPA_CTRL_IFACE_PORT; + char *pos; #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 struct sockaddr_in6 addr; int domain = PF_INET6; @@ -356,6 +399,17 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) if (wpa_s->conf->ctrl_interface == NULL) return priv; + pos = os_strstr(wpa_s->conf->ctrl_interface, "udp:"); + if (pos) { + pos += 4; + port = atoi(pos); + if (port <= 0) { + wpa_printf(MSG_ERROR, "Invalid ctrl_iface UDP port: %s", + wpa_s->conf->ctrl_interface); + goto fail; + } + } + priv->sock = socket(domain, SOCK_DGRAM, 0); if (priv->sock < 0) { wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno)); @@ -392,6 +446,15 @@ try_again: goto fail; } + /* Update the ctrl_interface value to match the selected port */ + os_snprintf(port_str, sizeof(port_str), "udp:%d", port); + os_free(wpa_s->conf->ctrl_interface); + wpa_s->conf->ctrl_interface = os_strdup(port_str); + if (!wpa_s->conf->ctrl_interface) { + wpa_msg(wpa_s, MSG_ERROR, "Failed to malloc ctrl_interface"); + goto fail; + } + #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE wpa_msg(wpa_s, MSG_DEBUG, "ctrl_iface_init UDP port: %d", port); #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ @@ -412,8 +475,6 @@ fail: void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv) { - struct wpa_ctrl_dst *dst, *prev; - if (priv->sock > -1) { eloop_unregister_read_sock(priv->sock); if (priv->ctrl_dst) { @@ -430,22 +491,19 @@ void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv) priv->sock = -1; } - dst = priv->ctrl_dst; - while (dst) { - prev = dst; - dst = dst->next; - os_free(prev); - } + wpas_ctrl_iface_free_dst(priv->ctrl_dst); os_free(priv); } -static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, +static void wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s, + const char *ifname, int sock, + struct wpa_ctrl_dst **head, int level, const char *buf, size_t len) { struct wpa_ctrl_dst *dst, *next; - char levelstr[10]; + char levelstr[64]; int idx; char *sbuf; int llen; @@ -453,11 +511,15 @@ static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, char addr[INET6_ADDRSTRLEN]; #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ - dst = priv->ctrl_dst; - if (priv->sock < 0 || dst == NULL) + dst = *head; + if (sock < 0 || dst == NULL) return; - os_snprintf(levelstr, sizeof(levelstr), "<%d>", level); + if (ifname) + os_snprintf(levelstr, sizeof(levelstr), "IFACE=%s <%d>", + ifname, level); + else + os_snprintf(levelstr, sizeof(levelstr), "<%d>", level); llen = os_strlen(levelstr); sbuf = os_malloc(llen + len); @@ -481,7 +543,7 @@ static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, inet_ntoa(dst->addr.sin_addr), ntohs(dst->addr.sin_port)); #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ - if (sendto(priv->sock, sbuf, llen + len, 0, + if (sendto(sock, sbuf, llen + len, 0, (struct sockaddr *) &dst->addr, sizeof(dst->addr)) < 0) { wpa_printf(MSG_ERROR, @@ -490,7 +552,7 @@ static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, dst->errors++; if (dst->errors > 10) { wpa_supplicant_ctrl_iface_detach( - priv, &dst->addr, + head, &dst->addr, dst->addrlen); } } else @@ -513,12 +575,6 @@ void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv) /* Global ctrl_iface */ -struct ctrl_iface_global_priv { - int sock; - u8 cookie[COOKIE_LEN]; -}; - - static char * wpa_supplicant_global_get_cookie(struct ctrl_iface_global_priv *priv, size_t *reply_len) @@ -546,9 +602,13 @@ static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx, struct ctrl_iface_global_priv *priv = sock_ctx; char buf[256], *pos; int res; +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + struct sockaddr_in6 from; +#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ struct sockaddr_in from; +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ socklen_t fromlen = sizeof(from); - char *reply; + char *reply = NULL; size_t reply_len; u8 cookie[COOKIE_LEN]; @@ -561,6 +621,7 @@ static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx, } #ifndef CONFIG_CTRL_IFACE_UDP_REMOTE +#ifndef CONFIG_CTRL_IFACE_UDP_IPV6 if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) { /* * The OS networking stack is expected to drop this kind of @@ -572,6 +633,7 @@ static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx, "source %s", inet_ntoa(from.sin_addr)); return; } +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ buf[res] = '\0'; @@ -603,17 +665,34 @@ static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx, while (*pos == ' ') pos++; - reply = wpa_supplicant_global_ctrl_iface_process(global, pos, - &reply_len); + if (os_strcmp(pos, "ATTACH") == 0) { + if (wpa_supplicant_ctrl_iface_attach(&priv->ctrl_dst, + &from, fromlen)) + reply_len = 1; + else + reply_len = 2; + } else if (os_strcmp(pos, "DETACH") == 0) { + if (wpa_supplicant_ctrl_iface_detach(&priv->ctrl_dst, + &from, fromlen)) + reply_len = 1; + else + reply_len = 2; + } else { + reply = wpa_supplicant_global_ctrl_iface_process(global, pos, + &reply_len); + } done: if (reply) { sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen); os_free(reply); - } else if (reply_len) { + } else if (reply_len == 1) { sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, fromlen); + } else if (reply_len == 2) { + sendto(sock, "OK\n", 3, 0, (struct sockaddr *) &from, + fromlen); } } @@ -623,6 +702,7 @@ wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global) { struct ctrl_iface_global_priv *priv; struct sockaddr_in addr; + char *pos; int port = WPA_GLOBAL_CTRL_IFACE_PORT; priv = os_zalloc(sizeof(*priv)); @@ -637,6 +717,17 @@ wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global) wpa_printf(MSG_DEBUG, "Global control interface '%s'", global->params.ctrl_interface); + pos = os_strstr(global->params.ctrl_interface, "udp:"); + if (pos) { + pos += 4; + port = atoi(pos); + if (port <= 0) { + wpa_printf(MSG_ERROR, "Invalid global ctrl UDP port %s", + global->params.ctrl_interface); + goto fail; + } + } + priv->sock = socket(PF_INET, SOCK_DGRAM, 0); if (priv->sock < 0) { wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno)); @@ -655,7 +746,7 @@ try_again: if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { port++; if ((port - WPA_GLOBAL_CTRL_IFACE_PORT) < - WPA_GLOBAL_CTRL_IFACE_PORT_LIMIT) + WPA_GLOBAL_CTRL_IFACE_PORT_LIMIT && !pos) goto try_again; wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno)); goto fail; @@ -668,6 +759,7 @@ try_again: eloop_register_read_sock(priv->sock, wpa_supplicant_global_ctrl_iface_receive, global, priv); + wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb); return priv; @@ -686,5 +778,7 @@ wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv) eloop_unregister_read_sock(priv->sock); close(priv->sock); } + + wpas_ctrl_iface_free_dst(priv->ctrl_dst); os_free(priv); } diff --git a/contrib/wpa/wpa_supplicant/ctrl_iface_unix.c b/contrib/wpa/wpa_supplicant/ctrl_iface_unix.c index 11f281473130..4db712fff7bb 100644 --- a/contrib/wpa/wpa_supplicant/ctrl_iface_unix.c +++ b/contrib/wpa/wpa_supplicant/ctrl_iface_unix.c @@ -15,7 +15,6 @@ #include #ifdef __linux__ #include -#include #endif /* __linux__ */ #ifdef ANDROID #include @@ -24,6 +23,7 @@ #include "utils/common.h" #include "utils/eloop.h" #include "utils/list.h" +#include "common/ctrl_iface_common.h" #include "eapol_supp/eapol_supp_sm.h" #include "config.h" #include "wpa_supplicant_i.h" @@ -31,27 +31,13 @@ /* Per-interface ctrl_iface */ -/** - * struct wpa_ctrl_dst - Internal data structure of control interface monitors - * - * This structure is used to store information about registered control - * interface monitors into struct wpa_supplicant. This data is private to - * ctrl_iface_unix.c and should not be touched directly from other files. - */ -struct wpa_ctrl_dst { - struct dl_list list; - struct sockaddr_un addr; - socklen_t addrlen; - int debug_level; - int errors; -}; - - struct ctrl_iface_priv { struct wpa_supplicant *wpa_s; int sock; struct dl_list ctrl_dst; int android_control_socket; + struct dl_list msg_queue; + unsigned int throttle_count; }; @@ -60,6 +46,17 @@ struct ctrl_iface_global_priv { int sock; struct dl_list ctrl_dst; int android_control_socket; + struct dl_list msg_queue; + unsigned int throttle_count; +}; + +struct ctrl_iface_msg { + struct dl_list list; + struct wpa_supplicant *wpa_s; + int level; + enum wpa_msg_type type; + const char *txt; + size_t len; }; @@ -92,7 +89,7 @@ static void wpas_ctrl_sock_debug(const char *title, int sock, const char *buf, if (getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sndbuf, &optlen) < 0) sndbuf = -1; - if (ioctl(sock, SIOCOUTQ, &outq) < 0) + if (ioctl(sock, TIOCOUTQ, &outq) < 0) outq = -1; wpa_printf(level, @@ -103,81 +100,29 @@ static void wpas_ctrl_sock_debug(const char *title, int sock, const char *buf, static int wpa_supplicant_ctrl_iface_attach(struct dl_list *ctrl_dst, - struct sockaddr_un *from, + struct sockaddr_storage *from, socklen_t fromlen, int global) { - struct wpa_ctrl_dst *dst; - char addr_txt[200]; - - dst = os_zalloc(sizeof(*dst)); - if (dst == NULL) - return -1; - os_memcpy(&dst->addr, from, sizeof(struct sockaddr_un)); - dst->addrlen = fromlen; - dst->debug_level = MSG_INFO; - dl_list_add(ctrl_dst, &dst->list); - printf_encode(addr_txt, sizeof(addr_txt), - (u8 *) from->sun_path, - fromlen - offsetof(struct sockaddr_un, sun_path)); - wpa_printf(MSG_DEBUG, "CTRL_IFACE %smonitor attached %s", - global ? "global " : "", addr_txt); - return 0; + return ctrl_iface_attach(ctrl_dst, from, fromlen); } static int wpa_supplicant_ctrl_iface_detach(struct dl_list *ctrl_dst, - struct sockaddr_un *from, + struct sockaddr_storage *from, socklen_t fromlen) { - struct wpa_ctrl_dst *dst; - - dl_list_for_each(dst, ctrl_dst, struct wpa_ctrl_dst, list) { - if (fromlen == dst->addrlen && - os_memcmp(from->sun_path, dst->addr.sun_path, - fromlen - offsetof(struct sockaddr_un, sun_path)) - == 0) { - char addr_txt[200]; - printf_encode(addr_txt, sizeof(addr_txt), - (u8 *) from->sun_path, - fromlen - - offsetof(struct sockaddr_un, sun_path)); - wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached %s", - addr_txt); - dl_list_del(&dst->list); - os_free(dst); - return 0; - } - } - return -1; + return ctrl_iface_detach(ctrl_dst, from, fromlen); } static int wpa_supplicant_ctrl_iface_level(struct ctrl_iface_priv *priv, - struct sockaddr_un *from, + struct sockaddr_storage *from, socklen_t fromlen, char *level) { - struct wpa_ctrl_dst *dst; - wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level); - dl_list_for_each(dst, &priv->ctrl_dst, struct wpa_ctrl_dst, list) { - if (fromlen == dst->addrlen && - os_memcmp(from->sun_path, dst->addr.sun_path, - fromlen - offsetof(struct sockaddr_un, sun_path)) - == 0) { - char addr_txt[200]; - dst->debug_level = atoi(level); - printf_encode(addr_txt, sizeof(addr_txt), - (u8 *) from->sun_path, fromlen - - offsetof(struct sockaddr_un, sun_path)); - wpa_printf(MSG_DEBUG, "CTRL_IFACE changed monitor level to %d for %s", - dst->debug_level, addr_txt); - return 0; - } - } - - return -1; + return ctrl_iface_level(&priv->ctrl_dst, from, fromlen, level); } @@ -188,7 +133,7 @@ static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, struct ctrl_iface_priv *priv = sock_ctx; char buf[4096]; int res; - struct sockaddr_un from; + struct sockaddr_storage from; socklen_t fromlen = sizeof(from); char *reply = NULL, *reply_buf = NULL; size_t reply_len = 0; @@ -334,33 +279,209 @@ static char * wpa_supplicant_ctrl_iface_path(struct wpa_supplicant *wpa_s) } +static int wpas_ctrl_iface_throttle(int sock) +{ +#ifdef __linux__ + socklen_t optlen; + int sndbuf, outq; + + optlen = sizeof(sndbuf); + sndbuf = 0; + if (getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sndbuf, &optlen) < 0 || + ioctl(sock, TIOCOUTQ, &outq) < 0 || + sndbuf <= 0 || outq < 0) + return 0; + return outq > sndbuf / 2; +#else /* __linux__ */ + return 0; +#endif /* __linux__ */ +} + + +static void wpas_ctrl_msg_send_pending_global(struct wpa_global *global) +{ + struct ctrl_iface_global_priv *gpriv; + struct ctrl_iface_msg *msg; + + gpriv = global->ctrl_iface; + while (gpriv && !dl_list_empty(&gpriv->msg_queue) && + !wpas_ctrl_iface_throttle(gpriv->sock)) { + msg = dl_list_first(&gpriv->msg_queue, struct ctrl_iface_msg, + list); + if (!msg) + break; + dl_list_del(&msg->list); + wpa_supplicant_ctrl_iface_send( + msg->wpa_s, + msg->type != WPA_MSG_PER_INTERFACE ? + NULL : msg->wpa_s->ifname, + gpriv->sock, &gpriv->ctrl_dst, msg->level, + msg->txt, msg->len, NULL, gpriv); + os_free(msg); + } +} + + +static void wpas_ctrl_msg_send_pending_iface(struct wpa_supplicant *wpa_s) +{ + struct ctrl_iface_priv *priv; + struct ctrl_iface_msg *msg; + + priv = wpa_s->ctrl_iface; + while (priv && !dl_list_empty(&priv->msg_queue) && + !wpas_ctrl_iface_throttle(priv->sock)) { + msg = dl_list_first(&priv->msg_queue, struct ctrl_iface_msg, + list); + if (!msg) + break; + dl_list_del(&msg->list); + wpa_supplicant_ctrl_iface_send(wpa_s, NULL, priv->sock, + &priv->ctrl_dst, msg->level, + msg->txt, msg->len, priv, NULL); + os_free(msg); + } +} + + +static void wpas_ctrl_msg_queue_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct ctrl_iface_priv *priv; + struct ctrl_iface_global_priv *gpriv; + int sock = -1, gsock = -1; + + wpas_ctrl_msg_send_pending_global(wpa_s->global); + wpas_ctrl_msg_send_pending_iface(wpa_s); + + priv = wpa_s->ctrl_iface; + if (priv && !dl_list_empty(&priv->msg_queue)) + sock = priv->sock; + + gpriv = wpa_s->global->ctrl_iface; + if (gpriv && !dl_list_empty(&gpriv->msg_queue)) + gsock = gpriv->sock; + + if (sock > -1 || gsock > -1) { + /* Continue pending message transmission from a timeout */ + wpa_printf(MSG_MSGDUMP, + "CTRL: Had to throttle pending event message transmission for (sock %d gsock %d)", + sock, gsock); + eloop_register_timeout(0, 20000, wpas_ctrl_msg_queue_timeout, + wpa_s, NULL); + } +} + + +static void wpas_ctrl_msg_queue(struct dl_list *queue, + struct wpa_supplicant *wpa_s, int level, + enum wpa_msg_type type, + const char *txt, size_t len) +{ + struct ctrl_iface_msg *msg; + + msg = os_zalloc(sizeof(*msg) + len); + if (!msg) + return; + + msg->wpa_s = wpa_s; + msg->level = level; + msg->type = type; + os_memcpy(msg + 1, txt, len); + msg->txt = (const char *) (msg + 1); + msg->len = len; + dl_list_add_tail(queue, &msg->list); + eloop_cancel_timeout(wpas_ctrl_msg_queue_timeout, wpa_s, NULL); + eloop_register_timeout(0, 0, wpas_ctrl_msg_queue_timeout, wpa_s, NULL); +} + + +static void wpas_ctrl_msg_queue_limit(unsigned int throttle_count, + struct dl_list *queue) +{ + struct ctrl_iface_msg *msg; + + if (throttle_count < 2000) + return; + + msg = dl_list_first(queue, struct ctrl_iface_msg, list); + if (msg) { + wpa_printf(MSG_DEBUG, "CTRL: Dropped oldest pending message"); + dl_list_del(&msg->list); + os_free(msg); + } +} + + static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, enum wpa_msg_type type, const char *txt, size_t len) { struct wpa_supplicant *wpa_s = ctx; + struct ctrl_iface_priv *priv; + struct ctrl_iface_global_priv *gpriv; if (wpa_s == NULL) return; - if (type != WPA_MSG_NO_GLOBAL && wpa_s->global->ctrl_iface) { - struct ctrl_iface_global_priv *priv = wpa_s->global->ctrl_iface; - if (!dl_list_empty(&priv->ctrl_dst)) { + gpriv = wpa_s->global->ctrl_iface; + + if (type != WPA_MSG_NO_GLOBAL && gpriv && + !dl_list_empty(&gpriv->ctrl_dst)) { + if (!dl_list_empty(&gpriv->msg_queue) || + wpas_ctrl_iface_throttle(gpriv->sock)) { + if (gpriv->throttle_count == 0) { + wpa_printf(MSG_MSGDUMP, + "CTRL: Had to throttle global event message for sock %d", + gpriv->sock); + } + gpriv->throttle_count++; + wpas_ctrl_msg_queue_limit(gpriv->throttle_count, + &gpriv->msg_queue); + wpas_ctrl_msg_queue(&gpriv->msg_queue, wpa_s, level, + type, txt, len); + } else { + if (gpriv->throttle_count) { + wpa_printf(MSG_MSGDUMP, + "CTRL: Had to throttle %u global event message(s) for sock %d", + gpriv->throttle_count, gpriv->sock); + } + gpriv->throttle_count = 0; wpa_supplicant_ctrl_iface_send( wpa_s, type != WPA_MSG_PER_INTERFACE ? NULL : wpa_s->ifname, - priv->sock, &priv->ctrl_dst, level, txt, len, - NULL, priv); + gpriv->sock, &gpriv->ctrl_dst, level, + txt, len, NULL, gpriv); } } - if (type == WPA_MSG_ONLY_GLOBAL || wpa_s->ctrl_iface == NULL) - return; - wpa_supplicant_ctrl_iface_send(wpa_s, NULL, wpa_s->ctrl_iface->sock, - &wpa_s->ctrl_iface->ctrl_dst, - level, txt, len, wpa_s->ctrl_iface, - NULL); + priv = wpa_s->ctrl_iface; + + if (type != WPA_MSG_ONLY_GLOBAL && priv) { + if (!dl_list_empty(&priv->msg_queue) || + wpas_ctrl_iface_throttle(priv->sock)) { + if (priv->throttle_count == 0) { + wpa_printf(MSG_MSGDUMP, + "CTRL: Had to throttle event message for sock %d", + priv->sock); + } + priv->throttle_count++; + wpas_ctrl_msg_queue_limit(priv->throttle_count, + &priv->msg_queue); + wpas_ctrl_msg_queue(&priv->msg_queue, wpa_s, level, + type, txt, len); + } else { + if (priv->throttle_count) { + wpa_printf(MSG_MSGDUMP, + "CTRL: Had to throttle %u event message(s) for sock %d", + priv->throttle_count, priv->sock); + } + priv->throttle_count = 0; + wpa_supplicant_ctrl_iface_send(wpa_s, NULL, priv->sock, + &priv->ctrl_dst, level, + txt, len, priv, NULL); + } + } } @@ -578,6 +699,7 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) if (priv == NULL) return NULL; dl_list_init(&priv->ctrl_dst); + dl_list_init(&priv->msg_queue); priv->wpa_s = wpa_s; priv->sock = -1; @@ -671,6 +793,8 @@ static int wpas_ctrl_iface_reinit(struct wpa_supplicant *wpa_s, void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv) { struct wpa_ctrl_dst *dst, *prev; + struct ctrl_iface_msg *msg, *prev_msg; + struct ctrl_iface_global_priv *gpriv; if (priv->sock > -1) { char *fname; @@ -724,8 +848,26 @@ void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv) free_dst: dl_list_for_each_safe(dst, prev, &priv->ctrl_dst, struct wpa_ctrl_dst, - list) + list) { + dl_list_del(&dst->list); os_free(dst); + } + dl_list_for_each_safe(msg, prev_msg, &priv->msg_queue, + struct ctrl_iface_msg, list) { + dl_list_del(&msg->list); + os_free(msg); + } + gpriv = priv->wpa_s->global->ctrl_iface; + if (gpriv) { + dl_list_for_each_safe(msg, prev_msg, &gpriv->msg_queue, + struct ctrl_iface_msg, list) { + if (msg->wpa_s == priv->wpa_s) { + dl_list_del(&msg->list); + os_free(msg); + } + } + } + eloop_cancel_timeout(wpas_ctrl_msg_queue_timeout, priv->wpa_s, NULL); os_free(priv); } @@ -785,33 +927,31 @@ static void wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s, dl_list_for_each_safe(dst, next, ctrl_dst, struct wpa_ctrl_dst, list) { int _errno; - char addr_txt[200]; + char txt[200]; if (level < dst->debug_level) continue; - printf_encode(addr_txt, sizeof(addr_txt), - (u8 *) dst->addr.sun_path, dst->addrlen - - offsetof(struct sockaddr_un, sun_path)); msg.msg_name = (void *) &dst->addr; msg.msg_namelen = dst->addrlen; wpas_ctrl_sock_debug("ctrl_sock-sendmsg", sock, buf, len); if (sendmsg(sock, &msg, MSG_DONTWAIT) >= 0) { - wpa_printf(MSG_MSGDUMP, - "CTRL_IFACE monitor sent successfully to %s", - addr_txt); + sockaddr_print(MSG_MSGDUMP, + "CTRL_IFACE monitor sent successfully to", + &dst->addr, dst->addrlen); dst->errors = 0; continue; } _errno = errno; - wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor[%s]: %d - %s", - addr_txt, errno, strerror(errno)); + os_snprintf(txt, sizeof(txt), "CTRL_IFACE monitor: %d (%s) for", + _errno, strerror(_errno)); + sockaddr_print(MSG_DEBUG, txt, &dst->addr, dst->addrlen); dst->errors++; if (dst->errors > 10 || _errno == ENOENT || _errno == EPERM) { - wpa_printf(MSG_INFO, "CTRL_IFACE: Detach monitor %s that cannot receive messages", - addr_txt); + sockaddr_print(MSG_INFO, "CTRL_IFACE: Detach monitor that cannot receive messages:", + &dst->addr, dst->addrlen); wpa_supplicant_ctrl_iface_detach(ctrl_dst, &dst->addr, dst->addrlen); } @@ -845,9 +985,12 @@ void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv) { char buf[256]; int res; - struct sockaddr_un from; + struct sockaddr_storage from; socklen_t fromlen = sizeof(from); + if (priv->sock == -1) + return; + for (;;) { wpa_printf(MSG_DEBUG, "CTRL_IFACE - %s - wait for monitor to " "attach", priv->wpa_s->ifname); @@ -905,7 +1048,7 @@ static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx, struct ctrl_iface_global_priv *priv = sock_ctx; char buf[4096]; int res; - struct sockaddr_un from; + struct sockaddr_storage from; socklen_t fromlen = sizeof(from); char *reply = NULL, *reply_buf = NULL; size_t reply_len; @@ -1155,6 +1298,7 @@ wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global) if (priv == NULL) return NULL; dl_list_init(&priv->ctrl_dst); + dl_list_init(&priv->msg_queue); priv->global = global; priv->sock = -1; @@ -1204,6 +1348,7 @@ void wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv) { struct wpa_ctrl_dst *dst, *prev; + struct ctrl_iface_msg *msg, *prev_msg; if (priv->sock >= 0) { eloop_unregister_read_sock(priv->sock); @@ -1212,7 +1357,14 @@ wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv) if (priv->global->params.ctrl_interface) unlink(priv->global->params.ctrl_interface); dl_list_for_each_safe(dst, prev, &priv->ctrl_dst, struct wpa_ctrl_dst, - list) + list) { + dl_list_del(&dst->list); os_free(dst); + } + dl_list_for_each_safe(msg, prev_msg, &priv->msg_queue, + struct ctrl_iface_msg, list) { + dl_list_del(&msg->list); + os_free(msg); + } os_free(priv); } diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus-wpa_supplicant.conf b/contrib/wpa/wpa_supplicant/dbus/dbus-wpa_supplicant.conf index c091234f7691..382dcb34318c 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus-wpa_supplicant.conf +++ b/contrib/wpa/wpa_supplicant/dbus/dbus-wpa_supplicant.conf @@ -17,11 +17,9 @@ - - diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_common_i.h b/contrib/wpa/wpa_supplicant/dbus/dbus_common_i.h index a551ccd554b1..95eb4bcb50ca 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_common_i.h +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_common_i.h @@ -13,6 +13,8 @@ #include +struct wpa_dbus_property_desc; + struct wpas_dbus_priv { DBusConnection *con; int should_dispatch; @@ -20,9 +22,13 @@ struct wpas_dbus_priv { u32 next_objid; int dbus_new_initialized; -#if defined(CONFIG_CTRL_IFACE_DBUS_NEW) && defined(CONFIG_AP) +#if defined(CONFIG_CTRL_IFACE_DBUS_NEW) + struct wpa_dbus_property_desc *all_interface_properties; + int globals_start; +#if defined(CONFIG_AP) int dbus_noc_refcnt; -#endif /* CONFIG_CTRL_IFACE_DBUS_NEW && CONFIG_AP */ +#endif /* CONFIG_AP */ +#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ }; #endif /* DBUS_COMMON_I_H */ diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_dict_helpers.c b/contrib/wpa/wpa_supplicant/dbus/dbus_dict_helpers.c index a0c44ebfa41d..e4e9b8da96b7 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_dict_helpers.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_dict_helpers.c @@ -204,24 +204,6 @@ dbus_bool_t wpa_dbus_dict_append_string(DBusMessageIter *iter_dict, } -/** - * Add a byte entry to the dict. - * - * @param iter_dict A valid DBusMessageIter returned from - * wpa_dbus_dict_open_write() - * @param key The key of the dict item - * @param value The byte value - * @return TRUE on success, FALSE on failure - * - */ -dbus_bool_t wpa_dbus_dict_append_byte(DBusMessageIter *iter_dict, - const char *key, const char value) -{ - return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_BYTE, - &value); -} - - /** * Add a boolean entry to the dict. * @@ -316,62 +298,6 @@ dbus_bool_t wpa_dbus_dict_append_uint32(DBusMessageIter *iter_dict, } -/** - * Add a 64-bit integer entry to the dict. - * - * @param iter_dict A valid DBusMessageIter returned from - * wpa_dbus_dict_open_write() - * @param key The key of the dict item - * @param value The 64-bit integer value - * @return TRUE on success, FALSE on failure - * - */ -dbus_bool_t wpa_dbus_dict_append_int64(DBusMessageIter *iter_dict, - const char *key, - const dbus_int64_t value) -{ - return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_INT64, - &value); -} - - -/** - * Add a 64-bit unsigned integer entry to the dict. - * - * @param iter_dict A valid DBusMessageIter returned from - * wpa_dbus_dict_open_write() - * @param key The key of the dict item - * @param value The 64-bit unsigned integer value - * @return TRUE on success, FALSE on failure - * - */ -dbus_bool_t wpa_dbus_dict_append_uint64(DBusMessageIter *iter_dict, - const char *key, - const dbus_uint64_t value) -{ - return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_UINT64, - &value); -} - - -/** - * Add a double-precision floating point entry to the dict. - * - * @param iter_dict A valid DBusMessageIter returned from - * wpa_dbus_dict_open_write() - * @param key The key of the dict item - * @param value The double-precision floating point value - * @return TRUE on success, FALSE on failure - * - */ -dbus_bool_t wpa_dbus_dict_append_double(DBusMessageIter *iter_dict, - const char *key, const double value) -{ - return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_DOUBLE, - &value); -} - - /** * Add a DBus object path entry to the dict. * diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_dict_helpers.h b/contrib/wpa/wpa_supplicant/dbus/dbus_dict_helpers.h index b068431a74cc..94a0efdbeb1f 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_dict_helpers.h +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_dict_helpers.h @@ -26,9 +26,6 @@ const char * wpa_dbus_type_as_string(const int type); dbus_bool_t wpa_dbus_dict_append_string(DBusMessageIter *iter_dict, const char *key, const char *value); -dbus_bool_t wpa_dbus_dict_append_byte(DBusMessageIter *iter_dict, - const char *key, const char value); - dbus_bool_t wpa_dbus_dict_append_bool(DBusMessageIter *iter_dict, const char *key, const dbus_bool_t value); @@ -49,18 +46,6 @@ dbus_bool_t wpa_dbus_dict_append_uint32(DBusMessageIter *iter_dict, const char *key, const dbus_uint32_t value); -dbus_bool_t wpa_dbus_dict_append_int64(DBusMessageIter *iter_dict, - const char *key, - const dbus_int64_t value); - -dbus_bool_t wpa_dbus_dict_append_uint64(DBusMessageIter *iter_dict, - const char *key, - const dbus_uint64_t value); - -dbus_bool_t wpa_dbus_dict_append_double(DBusMessageIter *iter_dict, - const char *key, - const double value); - dbus_bool_t wpa_dbus_dict_append_object_path(DBusMessageIter *iter_dict, const char *key, const char *value); diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new.c b/contrib/wpa/wpa_supplicant/dbus/dbus_new.c index 67d0e2877a47..27b3012aede8 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new.c @@ -1207,7 +1207,7 @@ static int match_group_where_peer_is_client(struct p2p_group *group, cfg->ssid_len); if (wpa_s_go != NULL && wpa_s_go == data->wpa_s) { wpas_dbus_signal_peer_groups_changed( - data->wpa_s->parent, data->info->p2p_device_addr); + data->wpa_s->p2pdev, data->info->p2p_device_addr); return 0; } @@ -1224,7 +1224,7 @@ static void signal_peer_groups_changed(struct p2p_peer_info *info, wpa_s_go = wpas_get_p2p_client_iface(data->wpa_s, info->p2p_device_addr); if (wpa_s_go != NULL && wpa_s_go == data->wpa_s) { - wpas_dbus_signal_peer_groups_changed(data->wpa_s->parent, + wpas_dbus_signal_peer_groups_changed(data->wpa_s->p2pdev, info->p2p_device_addr); return; } @@ -1254,14 +1254,11 @@ static void peer_groups_changed(struct wpa_supplicant *wpa_s) * irrespective of the role (client/GO) of the current device * * @wpa_s: %wpa_supplicant network interface data - * @ssid: SSID object * @client: this device is P2P client - * @network_id: network id of the group started, use instead of ssid->id - * to account for persistent groups + * @persistent: 0 - non persistent group, 1 - persistent group */ void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s, - const struct wpa_ssid *ssid, - int client, int network_id) + int client, int persistent) { DBusMessage *msg; DBusMessageIter iter, dict_iter; @@ -1300,6 +1297,7 @@ void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s, wpa_s->dbus_new_path) || !wpa_dbus_dict_append_string(&dict_iter, "role", client ? "client" : "GO") || + !wpa_dbus_dict_append_bool(&dict_iter, "persistent", persistent) || !wpa_dbus_dict_append_object_path(&dict_iter, "group_object", wpa_s->dbus_groupobj_path) || !wpa_dbus_dict_close_write(&iter, &dict_iter)) { @@ -1950,6 +1948,7 @@ void wpas_dbus_signal_p2p_invitation_received(struct wpa_supplicant *wpa_s, } dbus_connection_send(iface->con, msg, NULL); + dbus_message_unref(msg); } @@ -2000,6 +1999,10 @@ void wpas_dbus_signal_prop_changed(struct wpa_supplicant *wpa_s, prop = "DisconnectReason"; flush = TRUE; break; + case WPAS_DBUS_PROP_ASSOC_STATUS_CODE: + prop = "AssocStatusCode"; + flush = TRUE; + break; default: wpa_printf(MSG_ERROR, "dbus: %s: Unknown Property value %d", __func__, property); @@ -2172,41 +2175,54 @@ static const struct wpa_dbus_method_desc wpas_dbus_global_methods[] = { END_ARGS } }, + { "ExpectDisconnect", WPAS_DBUS_NEW_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_expect_disconnect, + { + END_ARGS + } + }, { NULL, NULL, NULL, { END_ARGS } } }; static const struct wpa_dbus_property_desc wpas_dbus_global_properties[] = { { "DebugLevel", WPAS_DBUS_NEW_INTERFACE, "s", wpas_dbus_getter_debug_level, - wpas_dbus_setter_debug_level + wpas_dbus_setter_debug_level, + NULL }, { "DebugTimestamp", WPAS_DBUS_NEW_INTERFACE, "b", wpas_dbus_getter_debug_timestamp, - wpas_dbus_setter_debug_timestamp + wpas_dbus_setter_debug_timestamp, + NULL }, { "DebugShowKeys", WPAS_DBUS_NEW_INTERFACE, "b", wpas_dbus_getter_debug_show_keys, - wpas_dbus_setter_debug_show_keys + wpas_dbus_setter_debug_show_keys, + NULL }, { "Interfaces", WPAS_DBUS_NEW_INTERFACE, "ao", wpas_dbus_getter_interfaces, + NULL, NULL }, { "EapMethods", WPAS_DBUS_NEW_INTERFACE, "as", wpas_dbus_getter_eap_methods, + NULL, NULL }, { "Capabilities", WPAS_DBUS_NEW_INTERFACE, "as", wpas_dbus_getter_global_capabilities, + NULL, NULL }, #ifdef CONFIG_WIFI_DISPLAY { "WFDIEs", WPAS_DBUS_NEW_INTERFACE, "ay", wpas_dbus_getter_global_wfd_ies, - wpas_dbus_setter_global_wfd_ies + wpas_dbus_setter_global_wfd_ies, + NULL }, #endif /* CONFIG_WIFI_DISPLAY */ - { NULL, NULL, NULL, NULL, NULL } + { NULL, NULL, NULL, NULL, NULL, NULL } }; static const struct wpa_dbus_signal_desc wpas_dbus_global_signals[] = { @@ -2234,12 +2250,50 @@ static const struct wpa_dbus_signal_desc wpas_dbus_global_signals[] = { }; +static char * uscore_to_dbus(const char *uscore) +{ + const char *p = uscore; + char *str, *s; + dbus_bool_t last_was_uscore = TRUE; + + s = str = os_zalloc(os_strlen(uscore) + 1); + if (!str) + return NULL; + while (p && *p) { + if (*p == '_') { + last_was_uscore = TRUE; + } else { + *s++ = last_was_uscore ? toupper(*p) : *p; + last_was_uscore = FALSE; + } + p++; + } + + return str; +} + + +static int wpa_dbus_ctrl_iface_props_init(struct wpas_dbus_priv *priv); + + +static void wpa_dbus_ctrl_iface_props_deinit(struct wpas_dbus_priv *priv) +{ + int idx = priv->globals_start; + + /* Free all allocated property values */ + while (priv->all_interface_properties[idx].dbus_property) + os_free((char *) + priv->all_interface_properties[idx++].dbus_property); + os_free((char *) priv->all_interface_properties); +} + + /** * wpas_dbus_ctrl_iface_init - Initialize dbus control interface * @global: Pointer to global data from wpa_supplicant_init() * Returns: 0 on success or -1 on failure * - * Initialize the dbus control interface for wpa_supplicantand and start + * Initialize the dbus control interface for wpa_supplicant and start * receiving commands from external programs over the bus. */ int wpas_dbus_ctrl_iface_init(struct wpas_dbus_priv *priv) @@ -2247,11 +2301,18 @@ int wpas_dbus_ctrl_iface_init(struct wpas_dbus_priv *priv) struct wpa_dbus_object_desc *obj_desc; int ret; + ret = wpa_dbus_ctrl_iface_props_init(priv); + if (ret < 0) { + wpa_printf(MSG_ERROR, + "dbus: Not enough memory to init interface properties"); + return -1; + } + obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc)); if (!obj_desc) { wpa_printf(MSG_ERROR, "Not enough memory to create object description"); - return -1; + goto error; } wpas_dbus_register(obj_desc, priv->global, NULL, @@ -2264,31 +2325,36 @@ int wpas_dbus_ctrl_iface_init(struct wpas_dbus_priv *priv) ret = wpa_dbus_ctrl_iface_init(priv, WPAS_DBUS_NEW_PATH, WPAS_DBUS_NEW_SERVICE, obj_desc); - if (ret < 0) + if (ret < 0) { free_dbus_object_desc(obj_desc); - else - priv->dbus_new_initialized = 1; + goto error; + } - return ret; + priv->dbus_new_initialized = 1; + return 0; + +error: + wpa_dbus_ctrl_iface_props_deinit(priv); + return -1; } /** * wpas_dbus_ctrl_iface_deinit - Deinitialize dbus ctrl interface for * wpa_supplicant - * @iface: Pointer to dbus private data from wpas_dbus_init() + * @priv: Pointer to dbus private data from wpas_dbus_init() * * Deinitialize the dbus control interface that was initialized with * wpas_dbus_ctrl_iface_init(). */ -void wpas_dbus_ctrl_iface_deinit(struct wpas_dbus_priv *iface) +void wpas_dbus_ctrl_iface_deinit(struct wpas_dbus_priv *priv) { - if (!iface->dbus_new_initialized) + if (!priv->dbus_new_initialized) return; wpa_printf(MSG_DEBUG, "dbus: Unregister D-Bus object '%s'", WPAS_DBUS_NEW_PATH); - dbus_connection_unregister_object_path(iface->con, - WPAS_DBUS_NEW_PATH); + dbus_connection_unregister_object_path(priv->con, WPAS_DBUS_NEW_PATH); + wpa_dbus_ctrl_iface_props_deinit(priv); } @@ -2301,13 +2367,15 @@ static void wpa_dbus_free(void *ptr) static const struct wpa_dbus_property_desc wpas_dbus_network_properties[] = { { "Properties", WPAS_DBUS_NEW_IFACE_NETWORK, "a{sv}", wpas_dbus_getter_network_properties, - wpas_dbus_setter_network_properties + wpas_dbus_setter_network_properties, + NULL }, { "Enabled", WPAS_DBUS_NEW_IFACE_NETWORK, "b", wpas_dbus_getter_enabled, - wpas_dbus_setter_enabled + wpas_dbus_setter_enabled, + NULL }, - { NULL, NULL, NULL, NULL, NULL } + { NULL, NULL, NULL, NULL, NULL, NULL } }; @@ -2446,53 +2514,65 @@ int wpas_dbus_unregister_network(struct wpa_supplicant *wpa_s, int nid) static const struct wpa_dbus_property_desc wpas_dbus_bss_properties[] = { { "SSID", WPAS_DBUS_NEW_IFACE_BSS, "ay", wpas_dbus_getter_bss_ssid, + NULL, NULL }, { "BSSID", WPAS_DBUS_NEW_IFACE_BSS, "ay", wpas_dbus_getter_bss_bssid, + NULL, NULL }, { "Privacy", WPAS_DBUS_NEW_IFACE_BSS, "b", wpas_dbus_getter_bss_privacy, + NULL, NULL }, { "Mode", WPAS_DBUS_NEW_IFACE_BSS, "s", wpas_dbus_getter_bss_mode, + NULL, NULL }, { "Signal", WPAS_DBUS_NEW_IFACE_BSS, "n", wpas_dbus_getter_bss_signal, + NULL, NULL }, { "Frequency", WPAS_DBUS_NEW_IFACE_BSS, "q", wpas_dbus_getter_bss_frequency, + NULL, NULL }, { "Rates", WPAS_DBUS_NEW_IFACE_BSS, "au", wpas_dbus_getter_bss_rates, + NULL, NULL }, { "WPA", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}", wpas_dbus_getter_bss_wpa, + NULL, NULL }, { "RSN", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}", wpas_dbus_getter_bss_rsn, + NULL, NULL }, { "WPS", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}", wpas_dbus_getter_bss_wps, + NULL, NULL }, { "IEs", WPAS_DBUS_NEW_IFACE_BSS, "ay", wpas_dbus_getter_bss_ies, + NULL, NULL }, { "Age", WPAS_DBUS_NEW_IFACE_BSS, "u", wpas_dbus_getter_bss_age, + NULL, NULL }, - { NULL, NULL, NULL, NULL, NULL } + { NULL, NULL, NULL, NULL, NULL, NULL } }; @@ -2992,131 +3072,202 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { } }, #endif /* CONFIG_TDLS */ + { "VendorElemAdd", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_vendor_elem_add, + { + { "frame_id", "i", ARG_IN }, + { "ielems", "ay", ARG_IN }, + END_ARGS + } + }, + { "VendorElemGet", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_vendor_elem_get, + { + { "frame_id", "i", ARG_IN }, + { "ielems", "ay", ARG_OUT }, + END_ARGS + } + }, + { "VendorElemRem", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_vendor_elem_remove, + { + { "frame_id", "i", ARG_IN }, + { "ielems", "ay", ARG_IN }, + END_ARGS + } + }, +#ifndef CONFIG_NO_CONFIG_WRITE + { "SaveConfig", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_save_config, + { + END_ARGS + } + }, +#endif /* CONFIG_NO_CONFIG_WRITE */ { NULL, NULL, NULL, { END_ARGS } } }; static const struct wpa_dbus_property_desc wpas_dbus_interface_properties[] = { { "Capabilities", WPAS_DBUS_NEW_IFACE_INTERFACE, "a{sv}", wpas_dbus_getter_capabilities, + NULL, NULL }, { "State", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", wpas_dbus_getter_state, + NULL, NULL }, { "Scanning", WPAS_DBUS_NEW_IFACE_INTERFACE, "b", wpas_dbus_getter_scanning, + NULL, NULL }, { "ApScan", WPAS_DBUS_NEW_IFACE_INTERFACE, "u", wpas_dbus_getter_ap_scan, - wpas_dbus_setter_ap_scan + wpas_dbus_setter_ap_scan, + NULL }, { "BSSExpireAge", WPAS_DBUS_NEW_IFACE_INTERFACE, "u", wpas_dbus_getter_bss_expire_age, - wpas_dbus_setter_bss_expire_age + wpas_dbus_setter_bss_expire_age, + NULL }, { "BSSExpireCount", WPAS_DBUS_NEW_IFACE_INTERFACE, "u", wpas_dbus_getter_bss_expire_count, - wpas_dbus_setter_bss_expire_count + wpas_dbus_setter_bss_expire_count, + NULL }, { "Country", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", wpas_dbus_getter_country, - wpas_dbus_setter_country + wpas_dbus_setter_country, + NULL }, { "Ifname", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", wpas_dbus_getter_ifname, + NULL, NULL }, { "Driver", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", wpas_dbus_getter_driver, + NULL, NULL }, { "BridgeIfname", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", wpas_dbus_getter_bridge_ifname, + NULL, + NULL + }, + { "ConfigFile", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", + wpas_dbus_getter_config_file, + NULL, NULL }, { "CurrentBSS", WPAS_DBUS_NEW_IFACE_INTERFACE, "o", wpas_dbus_getter_current_bss, + NULL, NULL }, { "CurrentNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE, "o", wpas_dbus_getter_current_network, + NULL, NULL }, { "CurrentAuthMode", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", wpas_dbus_getter_current_auth_mode, + NULL, NULL }, { "Blobs", WPAS_DBUS_NEW_IFACE_INTERFACE, "a{say}", wpas_dbus_getter_blobs, + NULL, NULL }, { "BSSs", WPAS_DBUS_NEW_IFACE_INTERFACE, "ao", wpas_dbus_getter_bsss, + NULL, NULL }, { "Networks", WPAS_DBUS_NEW_IFACE_INTERFACE, "ao", wpas_dbus_getter_networks, + NULL, NULL }, { "FastReauth", WPAS_DBUS_NEW_IFACE_INTERFACE, "b", wpas_dbus_getter_fast_reauth, - wpas_dbus_setter_fast_reauth + wpas_dbus_setter_fast_reauth, + NULL }, { "ScanInterval", WPAS_DBUS_NEW_IFACE_INTERFACE, "i", wpas_dbus_getter_scan_interval, - wpas_dbus_setter_scan_interval + wpas_dbus_setter_scan_interval, + NULL }, { "PKCS11EnginePath", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", wpas_dbus_getter_pkcs11_engine_path, + NULL, NULL }, { "PKCS11ModulePath", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", wpas_dbus_getter_pkcs11_module_path, + NULL, NULL }, #ifdef CONFIG_WPS { "ProcessCredentials", WPAS_DBUS_NEW_IFACE_WPS, "b", wpas_dbus_getter_process_credentials, - wpas_dbus_setter_process_credentials + wpas_dbus_setter_process_credentials, + NULL }, { "ConfigMethods", WPAS_DBUS_NEW_IFACE_WPS, "s", wpas_dbus_getter_config_methods, - wpas_dbus_setter_config_methods + wpas_dbus_setter_config_methods, + NULL }, #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P { "P2PDeviceConfig", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "a{sv}", wpas_dbus_getter_p2p_device_config, - wpas_dbus_setter_p2p_device_config + wpas_dbus_setter_p2p_device_config, + NULL }, { "Peers", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "ao", wpas_dbus_getter_p2p_peers, + NULL, NULL }, { "Role", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "s", wpas_dbus_getter_p2p_role, + NULL, NULL }, { "Group", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "o", wpas_dbus_getter_p2p_group, + NULL, NULL }, { "PeerGO", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "o", wpas_dbus_getter_p2p_peergo, + NULL, NULL }, { "PersistentGroups", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "ao", wpas_dbus_getter_persistent_groups, + NULL, NULL }, #endif /* CONFIG_P2P */ { "DisconnectReason", WPAS_DBUS_NEW_IFACE_INTERFACE, "i", wpas_dbus_getter_disconnect_reason, + NULL, NULL }, - { NULL, NULL, NULL, NULL, NULL } + { "AssocStatusCode", WPAS_DBUS_NEW_IFACE_INTERFACE, "i", + wpas_dbus_getter_assoc_status_code, + NULL, + NULL + }, + { NULL, NULL, NULL, NULL, NULL, NULL } }; static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = { @@ -3206,6 +3357,13 @@ static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = { END_ARGS } }, + { "DeviceFoundProperties", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + { "path", "o", ARG_OUT }, + { "properties", "a{sv}", ARG_OUT }, + END_ARGS + } + }, { "DeviceLost", WPAS_DBUS_NEW_IFACE_P2PDEVICE, { { "path", "o", ARG_OUT }, @@ -3390,6 +3548,77 @@ static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = { }; +static int wpa_dbus_ctrl_iface_props_init(struct wpas_dbus_priv *priv) +{ + size_t all_size; + unsigned int i, j, count, num_const, num_globals; + const char *global_name; + static const char * const ignored_globals[] = { + "bss_expiration_age", "bss_expiration_scan_count", + "ap_scan", "country", "fast_reauth", + "pkcs11_engine_path", "pkcs11_module_path" + }; + + /* wpas_dbus_interface_properties terminates with a NULL element */ + num_const = ARRAY_SIZE(wpas_dbus_interface_properties) - 1; + + num_globals = wpa_config_get_num_global_field_names(); + priv->globals_start = num_const; + + /* allocate enough for all properties + terminating NULL element */ + all_size = (num_globals + num_const + 1) * + sizeof(wpas_dbus_interface_properties[0]); + priv->all_interface_properties = os_zalloc(all_size); + if (!priv->all_interface_properties) { + wpa_printf(MSG_ERROR, + "dbus: Not enough memory for interface properties"); + return -1; + } + + /* Copy constant interface properties to the start of the array */ + os_memcpy(priv->all_interface_properties, + wpas_dbus_interface_properties, + sizeof(wpas_dbus_interface_properties)); + + /* Dynamically construct interface global properties */ + for (i = 0, count = num_const; i < num_globals; i++) { + struct wpa_dbus_property_desc *desc; + int no_var = 0; + + /* ignore globals that are actually just methods */ + global_name = wpa_config_get_global_field_name(i, &no_var); + if (no_var) + continue; + /* Ignore fields already explicitly exposed */ + for (j = 0; j < ARRAY_SIZE(ignored_globals); j++) { + if (os_strcmp(global_name, ignored_globals[j]) == 0) + break; + } + if (j < ARRAY_SIZE(ignored_globals)) + continue; + + desc = &priv->all_interface_properties[count++]; + desc->dbus_property = uscore_to_dbus(global_name); + if (!desc->dbus_property) { + wpa_printf(MSG_ERROR, + "dbus: Not enough memory for D-Bus property name"); + goto error; + } + desc->dbus_interface = WPAS_DBUS_NEW_IFACE_INTERFACE; + desc->type = "s"; + desc->getter = wpas_dbus_getter_iface_global; + desc->setter = wpas_dbus_setter_iface_global; + desc->data = global_name; + } + + return 0; + +error: + wpa_dbus_ctrl_iface_props_deinit(priv); + return -1; +} + + /** * wpas_dbus_register_interface - Register an interface with D-Bus * @wpa_s: wpa_supplicant interface structure @@ -3397,7 +3626,6 @@ static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = { */ int wpas_dbus_register_interface(struct wpa_supplicant *wpa_s) { - struct wpa_dbus_object_desc *obj_desc = NULL; struct wpas_dbus_priv *ctrl_iface = wpa_s->global->dbus; int next; @@ -3423,7 +3651,7 @@ int wpas_dbus_register_interface(struct wpa_supplicant *wpa_s) } wpas_dbus_register(obj_desc, wpa_s, NULL, wpas_dbus_interface_methods, - wpas_dbus_interface_properties, + ctrl_iface->all_interface_properties, wpas_dbus_interface_signals); wpa_printf(MSG_DEBUG, "dbus: Register interface object '%s'", @@ -3489,65 +3717,80 @@ int wpas_dbus_unregister_interface(struct wpa_supplicant *wpa_s) static const struct wpa_dbus_property_desc wpas_dbus_p2p_peer_properties[] = { { "DeviceName", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s", wpas_dbus_getter_p2p_peer_device_name, + NULL, NULL }, { "Manufacturer", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s", wpas_dbus_getter_p2p_peer_manufacturer, + NULL, NULL }, { "ModelName", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s", wpas_dbus_getter_p2p_peer_modelname, + NULL, NULL }, { "ModelNumber", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s", wpas_dbus_getter_p2p_peer_modelnumber, + NULL, NULL }, { "SerialNumber", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s", wpas_dbus_getter_p2p_peer_serialnumber, + NULL, NULL }, { "PrimaryDeviceType", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay", wpas_dbus_getter_p2p_peer_primary_device_type, + NULL, NULL }, { "config_method", WPAS_DBUS_NEW_IFACE_P2P_PEER, "q", wpas_dbus_getter_p2p_peer_config_method, + NULL, NULL }, { "level", WPAS_DBUS_NEW_IFACE_P2P_PEER, "i", wpas_dbus_getter_p2p_peer_level, + NULL, NULL }, { "devicecapability", WPAS_DBUS_NEW_IFACE_P2P_PEER, "y", wpas_dbus_getter_p2p_peer_device_capability, + NULL, NULL }, { "groupcapability", WPAS_DBUS_NEW_IFACE_P2P_PEER, "y", wpas_dbus_getter_p2p_peer_group_capability, + NULL, NULL }, { "SecondaryDeviceTypes", WPAS_DBUS_NEW_IFACE_P2P_PEER, "aay", wpas_dbus_getter_p2p_peer_secondary_device_types, + NULL, NULL }, { "VendorExtension", WPAS_DBUS_NEW_IFACE_P2P_PEER, "aay", wpas_dbus_getter_p2p_peer_vendor_extension, + NULL, NULL }, { "IEs", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay", wpas_dbus_getter_p2p_peer_ies, + NULL, NULL }, { "DeviceAddress", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay", wpas_dbus_getter_p2p_peer_device_address, + NULL, NULL }, { "Groups", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ao", wpas_dbus_getter_p2p_peer_groups, + NULL, NULL }, - { NULL, NULL, NULL, NULL, NULL } + { NULL, NULL, NULL, NULL, NULL, NULL } }; static const struct wpa_dbus_signal_desc wpas_dbus_p2p_peer_signals[] = { @@ -3569,12 +3812,13 @@ static const struct wpa_dbus_signal_desc wpas_dbus_p2p_peer_signals[] = { * In case of peer objects, it would be emitted by either * the "interface object" or by "peer objects" * @sig_name: signal name - DeviceFound + * @properties: Whether to add a second argument with object properties * - * Notify listeners about event related with newly found p2p peer device + * Notify listeners about event related with p2p peer device */ static void wpas_dbus_signal_peer(struct wpa_supplicant *wpa_s, const u8 *dev_addr, const char *interface, - const char *sig_name) + const char *sig_name, int properties) { struct wpas_dbus_priv *iface; DBusMessage *msg; @@ -3602,7 +3846,10 @@ static void wpas_dbus_signal_peer(struct wpa_supplicant *wpa_s, dbus_message_iter_init_append(msg, &iter); path = peer_obj_path; if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, - &path)) + &path) || + (properties && !wpa_dbus_get_object_properties( + iface, peer_obj_path, WPAS_DBUS_NEW_IFACE_P2P_PEER, + &iter))) wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); else dbus_connection_send(iface->con, msg, NULL); @@ -3623,7 +3870,11 @@ void wpas_dbus_signal_peer_device_found(struct wpa_supplicant *wpa_s, { wpas_dbus_signal_peer(wpa_s, dev_addr, WPAS_DBUS_NEW_IFACE_P2PDEVICE, - "DeviceFound"); + "DeviceFound", FALSE); + + wpas_dbus_signal_peer(wpa_s, dev_addr, + WPAS_DBUS_NEW_IFACE_P2PDEVICE, + "DeviceFoundProperties", TRUE); } /** @@ -3638,7 +3889,7 @@ void wpas_dbus_signal_peer_device_lost(struct wpa_supplicant *wpa_s, { wpas_dbus_signal_peer(wpa_s, dev_addr, WPAS_DBUS_NEW_IFACE_P2PDEVICE, - "DeviceLost"); + "DeviceLost", FALSE); } /** @@ -3805,41 +4056,50 @@ void wpas_dbus_signal_peer_groups_changed(struct wpa_supplicant *wpa_s, static const struct wpa_dbus_property_desc wpas_dbus_p2p_group_properties[] = { { "Members", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ao", wpas_dbus_getter_p2p_group_members, + NULL, NULL }, { "Group", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "o", wpas_dbus_getter_p2p_group, + NULL, NULL }, { "Role", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "s", wpas_dbus_getter_p2p_role, + NULL, NULL }, { "SSID", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ay", wpas_dbus_getter_p2p_group_ssid, + NULL, NULL }, { "BSSID", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ay", wpas_dbus_getter_p2p_group_bssid, + NULL, NULL }, { "Frequency", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "q", wpas_dbus_getter_p2p_group_frequency, + NULL, NULL }, { "Passphrase", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "s", wpas_dbus_getter_p2p_group_passphrase, + NULL, NULL }, { "PSK", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ay", wpas_dbus_getter_p2p_group_psk, + NULL, NULL }, { "WPSVendorExtensions", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "aay", wpas_dbus_getter_p2p_group_vendor_ext, - wpas_dbus_setter_p2p_group_vendor_ext + wpas_dbus_setter_p2p_group_vendor_ext, + NULL }, - { NULL, NULL, NULL, NULL, NULL } + { NULL, NULL, NULL, NULL, NULL, NULL } }; static const struct wpa_dbus_signal_desc wpas_dbus_p2p_group_signals[] = { @@ -3966,9 +4226,10 @@ static const struct wpa_dbus_property_desc wpas_dbus_persistent_group_properties[] = { { "Properties", WPAS_DBUS_NEW_IFACE_PERSISTENT_GROUP, "a{sv}", wpas_dbus_getter_persistent_group_properties, - wpas_dbus_setter_persistent_group_properties + wpas_dbus_setter_persistent_group_properties, + NULL }, - { NULL, NULL, NULL, NULL, NULL } + { NULL, NULL, NULL, NULL, NULL, NULL } }; /* No signals intended for persistent group objects */ diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new.h b/contrib/wpa/wpa_supplicant/dbus/dbus_new.h index 6d240fffce78..d64fceef718c 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new.h +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new.h @@ -29,6 +29,7 @@ enum wpas_dbus_prop { WPAS_DBUS_PROP_CURRENT_AUTH_MODE, WPAS_DBUS_PROP_BSSS, WPAS_DBUS_PROP_DISCONNECT_REASON, + WPAS_DBUS_PROP_ASSOC_STATUS_CODE, }; enum wpas_dbus_bss_prop { @@ -189,8 +190,7 @@ void wpas_dbus_signal_p2p_go_neg_req(struct wpa_supplicant *wpa_s, const u8 *src, u16 dev_passwd_id, u8 go_intent); void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s, - const struct wpa_ssid *ssid, - int client, int network_id); + int client, int persistent); void wpas_dbus_signal_p2p_group_formation_failure(struct wpa_supplicant *wpa_s, const char *reason); void wpas_dbus_register_p2p_group(struct wpa_supplicant *wpa_s, @@ -400,8 +400,7 @@ static inline void wpas_dbus_signal_p2p_go_neg_req(struct wpa_supplicant *wpa_s, static inline void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s, - const struct wpa_ssid *ssid, - int client, int network_id) + int client, int persistent) { } diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.c b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.c index 67562a547172..e11dd36ca23c 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.c @@ -435,7 +435,8 @@ dbus_bool_t wpas_dbus_simple_array_property_getter(DBusMessageIter *iter, for (i = 0; i < array_len; i++) { if (!dbus_message_iter_append_basic(&array_iter, type, - array + i * element_size)) { + (const char *) array + + i * element_size)) { dbus_set_error(error, DBUS_ERROR_FAILED, "%s: failed to construct message 2.5", __func__); @@ -711,9 +712,9 @@ DBusMessage * wpas_dbus_handler_get_interface(DBusMessage *message, * * Getter for "DebugLevel" property. */ -dbus_bool_t wpas_dbus_getter_debug_level(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_debug_level( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { const char *str; int idx = wpa_debug_level; @@ -737,9 +738,9 @@ dbus_bool_t wpas_dbus_getter_debug_level(DBusMessageIter *iter, * * Getter for "DebugTimestamp" property. */ -dbus_bool_t wpas_dbus_getter_debug_timestamp(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_debug_timestamp( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN, &wpa_debug_timestamp, error); @@ -756,9 +757,9 @@ dbus_bool_t wpas_dbus_getter_debug_timestamp(DBusMessageIter *iter, * * Getter for "DebugShowKeys" property. */ -dbus_bool_t wpas_dbus_getter_debug_show_keys(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_debug_show_keys( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN, &wpa_debug_show_keys, error); @@ -774,8 +775,9 @@ dbus_bool_t wpas_dbus_getter_debug_show_keys(DBusMessageIter *iter, * * Setter for "DebugLevel" property. */ -dbus_bool_t wpas_dbus_setter_debug_level(DBusMessageIter *iter, - DBusError *error, void *user_data) +dbus_bool_t wpas_dbus_setter_debug_level( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_global *global = user_data; const char *str = NULL; @@ -812,9 +814,9 @@ dbus_bool_t wpas_dbus_setter_debug_level(DBusMessageIter *iter, * * Setter for "DebugTimestamp" property. */ -dbus_bool_t wpas_dbus_setter_debug_timestamp(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_setter_debug_timestamp( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_global *global = user_data; dbus_bool_t val; @@ -838,9 +840,9 @@ dbus_bool_t wpas_dbus_setter_debug_timestamp(DBusMessageIter *iter, * * Setter for "DebugShowKeys" property. */ -dbus_bool_t wpas_dbus_setter_debug_show_keys(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_setter_debug_show_keys( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_global *global = user_data; dbus_bool_t val; @@ -867,9 +869,9 @@ dbus_bool_t wpas_dbus_setter_debug_show_keys(DBusMessageIter *iter, * by dbus clients to return list of registered interfaces objects * paths */ -dbus_bool_t wpas_dbus_getter_interfaces(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_interfaces( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_global *global = user_data; struct wpa_supplicant *wpa_s; @@ -912,8 +914,9 @@ dbus_bool_t wpas_dbus_getter_interfaces(DBusMessageIter *iter, * Getter for "EapMethods" property. Handles requests * by dbus clients to return list of strings with supported EAP methods */ -dbus_bool_t wpas_dbus_getter_eap_methods(DBusMessageIter *iter, - DBusError *error, void *user_data) +dbus_bool_t wpas_dbus_getter_eap_methods( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { char **eap_methods; size_t num_items = 0; @@ -948,9 +951,9 @@ dbus_bool_t wpas_dbus_getter_eap_methods(DBusMessageIter *iter, * return a list of strings with supported capabilities like AP, RSN IBSS, * and P2P that are determined at compile time. */ -dbus_bool_t wpas_dbus_getter_global_capabilities(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_global_capabilities( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { const char *capabilities[5] = { NULL, NULL, NULL, NULL, NULL }; size_t num_items = 0; @@ -1472,10 +1475,7 @@ DBusMessage * wpas_dbus_handler_disconnect(DBusMessage *message, struct wpa_supplicant *wpa_s) { if (wpa_s->current_ssid != NULL) { - wpa_s->disconnected = 1; - wpa_supplicant_deauthenticate(wpa_s, - WLAN_REASON_DEAUTH_LEAVING); - + wpas_request_disconnection(wpa_s); return NULL; } @@ -1504,7 +1504,7 @@ DBusMessage * wpas_dbus_handler_add_network(DBusMessage *message, dbus_message_iter_init(message, &iter); if (wpa_s->dbus_new_path) - ssid = wpa_config_add_network(wpa_s->conf); + ssid = wpa_supplicant_add_network(wpa_s); if (ssid == NULL) { wpa_printf(MSG_ERROR, "%s[dbus]: can't add new interface.", __func__); @@ -1513,9 +1513,6 @@ DBusMessage * wpas_dbus_handler_add_network(DBusMessage *message, "wpa_supplicant could not add a network on this interface."); goto err; } - wpas_notify_network_added(wpa_s, ssid); - ssid->disabled = 1; - wpa_config_set_network_defaults(ssid); dbus_error_init(&error); if (!set_network_properties(wpa_s, ssid, &iter, &error)) { @@ -1579,6 +1576,27 @@ DBusMessage * wpas_dbus_handler_reassociate(DBusMessage *message, } +/** + * wpas_dbus_handler_expect_disconnect - ExpectDisconnect + * @message: Pointer to incoming dbus message + * @global: %wpa_supplicant global data structure + * Returns: NULL + * + * Handler function for notifying system there will be a expected disconnect. + * This will prevent wpa_supplicant from adding blacklists upon next disconnect.. + */ +DBusMessage * wpas_dbus_handler_expect_disconnect(DBusMessage *message, + struct wpa_global *global) +{ + struct wpa_supplicant *wpa_s = global->ifaces; + + for (; wpa_s; wpa_s = wpa_s->next) + if (wpa_s->wpa_state >= WPA_ASSOCIATED) + wpa_s->own_disconnect_req = 1; + return NULL; +} + + /** * wpas_dbus_handler_reattach - Reattach to current AP * @message: Pointer to incoming dbus message @@ -1641,8 +1659,7 @@ DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message, const char *op; char *iface, *net_id; int id; - struct wpa_ssid *ssid; - int was_disabled; + int result; dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &op, DBUS_TYPE_INVALID); @@ -1665,27 +1682,12 @@ DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message, goto out; } - ssid = wpa_config_get_network(wpa_s->conf, id); - if (ssid == NULL) { + result = wpa_supplicant_remove_network(wpa_s, id); + if (result == -1) { reply = wpas_dbus_error_network_unknown(message); goto out; } - - was_disabled = ssid->disabled; - - wpas_notify_network_removed(wpa_s, ssid); - - if (ssid == wpa_s->current_ssid) - wpa_supplicant_deauthenticate(wpa_s, - WLAN_REASON_DEAUTH_LEAVING); - else if (!was_disabled && wpa_s->sched_scanning) { - wpa_printf(MSG_DEBUG, - "Stop ongoing sched_scan to remove network from filters"); - wpa_supplicant_cancel_sched_scan(wpa_s); - wpa_supplicant_req_scan(wpa_s, 0, 0); - } - - if (wpa_config_remove_network(wpa_s->conf, id) < 0) { + if (result == -2) { wpa_printf(MSG_ERROR, "%s[dbus]: error occurred when removing network %d", __func__, id); @@ -1854,7 +1856,7 @@ out: os_free(iface); return reply; #else /* IEEE8021X_EAPOL */ - wpa_printf(MSG_DEBUG, "CTRL_IFACE: 802.1X not included"); + wpa_printf(MSG_DEBUG, "dbus: 802.1X not included"); return wpas_dbus_error_unknown_error(message, "802.1X not included"); #endif /* IEEE8021X_EAPOL */ } @@ -2271,6 +2273,35 @@ DBusMessage * wpas_dbus_handler_tdls_teardown(DBusMessage *message, #endif /* CONFIG_TDLS */ +#ifndef CONFIG_NO_CONFIG_WRITE +/** + * wpas_dbus_handler_save_config - Save configuration to configuration file + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NULL on Success, Otherwise errror message + * + * Handler function for "SaveConfig" method call of network interface. + */ +DBusMessage * wpas_dbus_handler_save_config(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + int ret; + + if (!wpa_s->conf->update_config) { + return wpas_dbus_error_unknown_error( + message, + "Not allowed to update configuration (update_config=0)"); + } + + ret = wpa_config_write(wpa_s->confname, wpa_s->conf); + if (ret) + return wpas_dbus_error_unknown_error( + message, "Failed to update configuration"); + return NULL; +} +#endif /* CONFIG_NO_CONFIG_WRITE */ + + /** * wpas_dbus_handler_set_pkcs11_engine_and_module_path - Set PKCS #11 engine and module path * @message: Pointer to incoming dbus message @@ -2338,8 +2369,9 @@ DBusMessage * wpas_dbus_handler_set_pkcs11_engine_and_module_path( * * Getter for "Capabilities" property of an interface. */ -dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter, - DBusError *error, void *user_data) +dbus_bool_t wpas_dbus_getter_capabilities( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; struct wpa_driver_capa capa; @@ -2585,12 +2617,14 @@ dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter, &iter_array) || !wpa_dbus_dict_string_array_add_element( &iter_array, "infrastructure") || - !wpa_dbus_dict_string_array_add_element( - &iter_array, "ad-hoc") || + (res >= 0 && (capa.flags & WPA_DRIVER_FLAGS_IBSS) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "ad-hoc")) || (res >= 0 && (capa.flags & WPA_DRIVER_FLAGS_AP) && !wpa_dbus_dict_string_array_add_element( &iter_array, "ap")) || (res >= 0 && (capa.flags & WPA_DRIVER_FLAGS_P2P_CAPABLE) && + !wpa_s->conf->p2p_disabled && !wpa_dbus_dict_string_array_add_element( &iter_array, "p2p")) || !wpa_dbus_dict_end_string_array(&iter_dict, @@ -2629,8 +2663,9 @@ nomem: * * Getter for "State" property. */ -dbus_bool_t wpas_dbus_getter_state(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_state( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; const char *str_state; @@ -2669,8 +2704,9 @@ dbus_bool_t wpas_dbus_getter_state(DBusMessageIter *iter, DBusError *error, * * Getter for "scanning" property. */ -dbus_bool_t wpas_dbus_getter_scanning(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_scanning( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; dbus_bool_t scanning = wpa_s->scanning ? TRUE : FALSE; @@ -2689,8 +2725,9 @@ dbus_bool_t wpas_dbus_getter_scanning(DBusMessageIter *iter, DBusError *error, * * Getter function for "ApScan" property. */ -dbus_bool_t wpas_dbus_getter_ap_scan(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_ap_scan( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; dbus_uint32_t ap_scan = wpa_s->conf->ap_scan; @@ -2709,8 +2746,9 @@ dbus_bool_t wpas_dbus_getter_ap_scan(DBusMessageIter *iter, DBusError *error, * * Setter function for "ApScan" property. */ -dbus_bool_t wpas_dbus_setter_ap_scan(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_setter_ap_scan( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; dbus_uint32_t ap_scan; @@ -2738,9 +2776,9 @@ dbus_bool_t wpas_dbus_setter_ap_scan(DBusMessageIter *iter, DBusError *error, * * Getter function for "FastReauth" property. */ -dbus_bool_t wpas_dbus_getter_fast_reauth(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_fast_reauth( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; dbus_bool_t fast_reauth = wpa_s->conf->fast_reauth ? TRUE : FALSE; @@ -2760,9 +2798,9 @@ dbus_bool_t wpas_dbus_getter_fast_reauth(DBusMessageIter *iter, * * Setter function for "FastReauth" property. */ -dbus_bool_t wpas_dbus_setter_fast_reauth(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_setter_fast_reauth( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; dbus_bool_t fast_reauth; @@ -2786,9 +2824,9 @@ dbus_bool_t wpas_dbus_setter_fast_reauth(DBusMessageIter *iter, * Getter for "DisconnectReason" property. The reason is negative if it is * locally generated. */ -dbus_bool_t wpas_dbus_getter_disconnect_reason(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_disconnect_reason( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; dbus_int32_t reason = wpa_s->disconnect_reason; @@ -2798,6 +2836,27 @@ dbus_bool_t wpas_dbus_getter_disconnect_reason(DBusMessageIter *iter, } +/** + * wpas_dbus_getter_assoc_status_code - Get most recent failed assoc status code + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter for "AssocStatusCode" property. + */ +dbus_bool_t wpas_dbus_getter_assoc_status_code( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + dbus_int32_t status_code = wpa_s->assoc_status_code; + + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_INT32, + &status_code, error); +} + + /** * wpas_dbus_getter_bss_expire_age - Get BSS entry expiration age * @iter: Pointer to incoming dbus message iter @@ -2807,9 +2866,9 @@ dbus_bool_t wpas_dbus_getter_disconnect_reason(DBusMessageIter *iter, * * Getter function for "BSSExpireAge" property. */ -dbus_bool_t wpas_dbus_getter_bss_expire_age(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_bss_expire_age( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; dbus_uint32_t expire_age = wpa_s->conf->bss_expiration_age; @@ -2828,9 +2887,9 @@ dbus_bool_t wpas_dbus_getter_bss_expire_age(DBusMessageIter *iter, * * Setter function for "BSSExpireAge" property. */ -dbus_bool_t wpas_dbus_setter_bss_expire_age(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_setter_bss_expire_age( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; dbus_uint32_t expire_age; @@ -2857,9 +2916,9 @@ dbus_bool_t wpas_dbus_setter_bss_expire_age(DBusMessageIter *iter, * * Getter function for "BSSExpireCount" property. */ -dbus_bool_t wpas_dbus_getter_bss_expire_count(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_bss_expire_count( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; dbus_uint32_t expire_count = wpa_s->conf->bss_expiration_scan_count; @@ -2878,9 +2937,9 @@ dbus_bool_t wpas_dbus_getter_bss_expire_count(DBusMessageIter *iter, * * Setter function for "BSSExpireCount" property. */ -dbus_bool_t wpas_dbus_setter_bss_expire_count(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_setter_bss_expire_count( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; dbus_uint32_t expire_count; @@ -2907,8 +2966,9 @@ dbus_bool_t wpas_dbus_setter_bss_expire_count(DBusMessageIter *iter, * * Getter function for "Country" property. */ -dbus_bool_t wpas_dbus_getter_country(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_country( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; char country[3]; @@ -2932,8 +2992,9 @@ dbus_bool_t wpas_dbus_getter_country(DBusMessageIter *iter, DBusError *error, * * Setter function for "Country" property. */ -dbus_bool_t wpas_dbus_setter_country(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_setter_country( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; const char *country; @@ -2970,9 +3031,9 @@ dbus_bool_t wpas_dbus_setter_country(DBusMessageIter *iter, DBusError *error, * * Getter function for "ScanInterval" property. */ -dbus_bool_t wpas_dbus_getter_scan_interval(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_scan_interval( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; dbus_int32_t scan_interval = wpa_s->scan_interval; @@ -2991,9 +3052,9 @@ dbus_bool_t wpas_dbus_getter_scan_interval(DBusMessageIter *iter, * * Setter function for "ScanInterval" property. */ -dbus_bool_t wpas_dbus_setter_scan_interval(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_setter_scan_interval( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; dbus_int32_t scan_interval; @@ -3020,8 +3081,9 @@ dbus_bool_t wpas_dbus_setter_scan_interval(DBusMessageIter *iter, * * Getter for "Ifname" property. */ -dbus_bool_t wpas_dbus_getter_ifname(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_ifname( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; const char *ifname = wpa_s->ifname; @@ -3040,8 +3102,9 @@ dbus_bool_t wpas_dbus_getter_ifname(DBusMessageIter *iter, DBusError *error, * * Getter for "Driver" property. */ -dbus_bool_t wpas_dbus_getter_driver(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_driver( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; const char *driver; @@ -3069,9 +3132,9 @@ dbus_bool_t wpas_dbus_getter_driver(DBusMessageIter *iter, DBusError *error, * * Getter for "CurrentBSS" property. */ -dbus_bool_t wpas_dbus_getter_current_bss(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_current_bss( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *bss_obj_path = path_buf; @@ -3097,9 +3160,9 @@ dbus_bool_t wpas_dbus_getter_current_bss(DBusMessageIter *iter, * * Getter for "CurrentNetwork" property. */ -dbus_bool_t wpas_dbus_getter_current_network(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_current_network( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *net_obj_path = path_buf; @@ -3125,9 +3188,9 @@ dbus_bool_t wpas_dbus_getter_current_network(DBusMessageIter *iter, * * Getter for "CurrentAuthMode" property. */ -dbus_bool_t wpas_dbus_getter_current_auth_mode(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_current_auth_mode( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; const char *eap_mode; @@ -3143,9 +3206,11 @@ dbus_bool_t wpas_dbus_getter_current_auth_mode(DBusMessageIter *iter, "EAP-%s", eap_mode); auth_mode = eap_mode_buf; - } else { + } else if (wpa_s->current_ssid) { auth_mode = wpa_key_mgmt_txt(wpa_s->key_mgmt, wpa_s->current_ssid->proto); + } else { + auth_mode = "UNKNOWN"; } return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, @@ -3162,9 +3227,9 @@ dbus_bool_t wpas_dbus_getter_current_auth_mode(DBusMessageIter *iter, * * Getter for "BridgeIfname" property. */ -dbus_bool_t wpas_dbus_getter_bridge_ifname(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_bridge_ifname( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; const char *bridge_ifname = wpa_s->bridge_ifname; @@ -3174,6 +3239,30 @@ dbus_bool_t wpas_dbus_getter_bridge_ifname(DBusMessageIter *iter, } +/** + * wpas_dbus_getter_config_file - Get interface configuration file path + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter for "ConfigFile" property. + */ +dbus_bool_t wpas_dbus_getter_config_file( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + char *confname = ""; + + if (wpa_s->confname) + confname = wpa_s->confname; + + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, + &confname, error); +} + + /** * wpas_dbus_getter_bsss - Get array of BSSs objects * @iter: Pointer to incoming dbus message iter @@ -3183,8 +3272,9 @@ dbus_bool_t wpas_dbus_getter_bridge_ifname(DBusMessageIter *iter, * * Getter for "BSSs" property. */ -dbus_bool_t wpas_dbus_getter_bsss(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_bsss( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; struct wpa_bss *bss; @@ -3240,8 +3330,9 @@ out: * * Getter for "Networks" property. */ -dbus_bool_t wpas_dbus_getter_networks(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_networks( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; struct wpa_ssid *ssid; @@ -3303,9 +3394,9 @@ out: * * Getter for "PKCS11EnginePath" property. */ -dbus_bool_t wpas_dbus_getter_pkcs11_engine_path(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_pkcs11_engine_path( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; const char *pkcs11_engine_path; @@ -3328,9 +3419,9 @@ dbus_bool_t wpas_dbus_getter_pkcs11_engine_path(DBusMessageIter *iter, * * Getter for "PKCS11ModulePath" property. */ -dbus_bool_t wpas_dbus_getter_pkcs11_module_path(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_pkcs11_module_path( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; const char *pkcs11_module_path; @@ -3353,8 +3444,9 @@ dbus_bool_t wpas_dbus_getter_pkcs11_module_path(DBusMessageIter *iter, * * Getter for "Blobs" property. */ -dbus_bool_t wpas_dbus_getter_blobs(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_blobs( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; DBusMessageIter variant_iter, dict_iter, entry_iter, array_iter; @@ -3406,6 +3498,79 @@ dbus_bool_t wpas_dbus_getter_blobs(DBusMessageIter *iter, DBusError *error, } +dbus_bool_t wpas_dbus_getter_iface_global( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + int ret; + char buf[250]; + char *p = buf; + + if (!property_desc->data) { + dbus_set_error(error, DBUS_ERROR_INVALID_ARGS, + "Unhandled interface property %s", + property_desc->dbus_property); + return FALSE; + } + + ret = wpa_config_get_value(property_desc->data, wpa_s->conf, buf, + sizeof(buf)); + if (ret < 0) + *p = '\0'; + + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &p, + error); +} + + +dbus_bool_t wpas_dbus_setter_iface_global( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + const char *new_value = NULL; + char buf[250]; + size_t combined_len; + int ret; + + if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_STRING, + &new_value)) + return FALSE; + + combined_len = os_strlen(property_desc->data) + os_strlen(new_value) + + 3; + if (combined_len >= sizeof(buf)) { + dbus_set_error(error, DBUS_ERROR_INVALID_ARGS, + "Interface property %s value too large", + property_desc->dbus_property); + return FALSE; + } + + if (!new_value[0]) + new_value = "NULL"; + + ret = os_snprintf(buf, combined_len, "%s=%s", property_desc->data, + new_value); + if (os_snprintf_error(combined_len, ret)) { + dbus_set_error(error, WPAS_DBUS_ERROR_UNKNOWN_ERROR, + "Failed to construct new interface property %s", + property_desc->dbus_property); + return FALSE; + } + + if (wpa_config_process_global(wpa_s->conf, buf, -1)) { + dbus_set_error(error, DBUS_ERROR_INVALID_ARGS, + "Failed to set interface property %s", + property_desc->dbus_property); + return FALSE; + } + + wpa_supplicant_update_config(wpa_s); + return TRUE; +} + + static struct wpa_bss * get_bss_helper(struct bss_handler_args *args, DBusError *error, const char *func_name) { @@ -3432,8 +3597,9 @@ static struct wpa_bss * get_bss_helper(struct bss_handler_args *args, * * Getter for "BSSID" property. */ -dbus_bool_t wpas_dbus_getter_bss_bssid(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_bss_bssid( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct bss_handler_args *args = user_data; struct wpa_bss *res; @@ -3457,8 +3623,9 @@ dbus_bool_t wpas_dbus_getter_bss_bssid(DBusMessageIter *iter, DBusError *error, * * Getter for "SSID" property. */ -dbus_bool_t wpas_dbus_getter_bss_ssid(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_bss_ssid( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct bss_handler_args *args = user_data; struct wpa_bss *res; @@ -3482,8 +3649,9 @@ dbus_bool_t wpas_dbus_getter_bss_ssid(DBusMessageIter *iter, DBusError *error, * * Getter for "Privacy" property. */ -dbus_bool_t wpas_dbus_getter_bss_privacy(DBusMessageIter *iter, - DBusError *error, void *user_data) +dbus_bool_t wpas_dbus_getter_bss_privacy( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct bss_handler_args *args = user_data; struct wpa_bss *res; @@ -3508,8 +3676,9 @@ dbus_bool_t wpas_dbus_getter_bss_privacy(DBusMessageIter *iter, * * Getter for "Mode" property. */ -dbus_bool_t wpas_dbus_getter_bss_mode(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_bss_mode( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct bss_handler_args *args = user_data; struct wpa_bss *res; @@ -3549,8 +3718,9 @@ dbus_bool_t wpas_dbus_getter_bss_mode(DBusMessageIter *iter, DBusError *error, * * Getter for "Level" property. */ -dbus_bool_t wpas_dbus_getter_bss_signal(DBusMessageIter *iter, - DBusError *error, void *user_data) +dbus_bool_t wpas_dbus_getter_bss_signal( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct bss_handler_args *args = user_data; struct wpa_bss *res; @@ -3575,8 +3745,9 @@ dbus_bool_t wpas_dbus_getter_bss_signal(DBusMessageIter *iter, * * Getter for "Frequency" property. */ -dbus_bool_t wpas_dbus_getter_bss_frequency(DBusMessageIter *iter, - DBusError *error, void *user_data) +dbus_bool_t wpas_dbus_getter_bss_frequency( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct bss_handler_args *args = user_data; struct wpa_bss *res; @@ -3607,8 +3778,9 @@ static int cmp_u8s_desc(const void *a, const void *b) * * Getter for "Rates" property. */ -dbus_bool_t wpas_dbus_getter_bss_rates(DBusMessageIter *iter, - DBusError *error, void *user_data) +dbus_bool_t wpas_dbus_getter_bss_rates( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct bss_handler_args *args = user_data; struct wpa_bss *res; @@ -3647,9 +3819,9 @@ dbus_bool_t wpas_dbus_getter_bss_rates(DBusMessageIter *iter, } -static dbus_bool_t wpas_dbus_get_bss_security_prop(DBusMessageIter *iter, - struct wpa_ie_data *ie_data, - DBusError *error) +static dbus_bool_t wpas_dbus_get_bss_security_prop( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, struct wpa_ie_data *ie_data, DBusError *error) { DBusMessageIter iter_dict, variant_iter; const char *group; @@ -3780,8 +3952,9 @@ nomem: * * Getter for "WPA" property. */ -dbus_bool_t wpas_dbus_getter_bss_wpa(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_bss_wpa( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct bss_handler_args *args = user_data; struct wpa_bss *res; @@ -3800,7 +3973,7 @@ dbus_bool_t wpas_dbus_getter_bss_wpa(DBusMessageIter *iter, DBusError *error, return FALSE; } - return wpas_dbus_get_bss_security_prop(iter, &wpa_data, error); + return wpas_dbus_get_bss_security_prop(property_desc, iter, &wpa_data, error); } @@ -3813,8 +3986,9 @@ dbus_bool_t wpas_dbus_getter_bss_wpa(DBusMessageIter *iter, DBusError *error, * * Getter for "RSN" property. */ -dbus_bool_t wpas_dbus_getter_bss_rsn(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_bss_rsn( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct bss_handler_args *args = user_data; struct wpa_bss *res; @@ -3833,7 +4007,7 @@ dbus_bool_t wpas_dbus_getter_bss_rsn(DBusMessageIter *iter, DBusError *error, return FALSE; } - return wpas_dbus_get_bss_security_prop(iter, &wpa_data, error); + return wpas_dbus_get_bss_security_prop(property_desc, iter, &wpa_data, error); } @@ -3846,8 +4020,9 @@ dbus_bool_t wpas_dbus_getter_bss_rsn(DBusMessageIter *iter, DBusError *error, * * Getter for "WPS" property. */ -dbus_bool_t wpas_dbus_getter_bss_wps(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_bss_wps( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct bss_handler_args *args = user_data; struct wpa_bss *res; @@ -3902,8 +4077,9 @@ nomem: * * Getter for "IEs" property. */ -dbus_bool_t wpas_dbus_getter_bss_ies(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_bss_ies( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct bss_handler_args *args = user_data; struct wpa_bss *res; @@ -3927,8 +4103,9 @@ dbus_bool_t wpas_dbus_getter_bss_ies(DBusMessageIter *iter, DBusError *error, * * Getter for BSS age */ -dbus_bool_t wpas_dbus_getter_bss_age(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_bss_age( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct bss_handler_args *args = user_data; struct wpa_bss *res; @@ -3956,8 +4133,9 @@ dbus_bool_t wpas_dbus_getter_bss_age(DBusMessageIter *iter, DBusError *error, * * Getter for "enabled" property of a configured network. */ -dbus_bool_t wpas_dbus_getter_enabled(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_enabled( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct network_handler_args *net = user_data; dbus_bool_t enabled = net->ssid->disabled ? FALSE : TRUE; @@ -3976,8 +4154,9 @@ dbus_bool_t wpas_dbus_getter_enabled(DBusMessageIter *iter, DBusError *error, * * Setter for "Enabled" property of a configured network. */ -dbus_bool_t wpas_dbus_setter_enabled(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_setter_enabled( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct network_handler_args *net = user_data; struct wpa_supplicant *wpa_s; @@ -4009,9 +4188,9 @@ dbus_bool_t wpas_dbus_setter_enabled(DBusMessageIter *iter, DBusError *error, * * Getter for "Properties" property of a configured network. */ -dbus_bool_t wpas_dbus_getter_network_properties(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_network_properties( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct network_handler_args *net = user_data; DBusMessageIter variant_iter, dict_iter; @@ -4071,9 +4250,9 @@ out: * * Setter for "Properties" property of a configured network. */ -dbus_bool_t wpas_dbus_setter_network_properties(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_setter_network_properties( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct network_handler_args *net = user_data; struct wpa_ssid *ssid = net->ssid; @@ -4211,3 +4390,147 @@ out: } #endif /* CONFIG_AP */ + + +DBusMessage * wpas_dbus_handler_vendor_elem_add(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + u8 *ielems; + int len; + struct ieee802_11_elems elems; + dbus_int32_t frame_id; + DBusMessageIter iter, array; + + dbus_message_iter_init(message, &iter); + dbus_message_iter_get_basic(&iter, &frame_id); + if (frame_id < 0 || frame_id >= NUM_VENDOR_ELEM_FRAMES) { + return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, + "Invalid ID"); + } + + dbus_message_iter_next(&iter); + dbus_message_iter_recurse(&iter, &array); + dbus_message_iter_get_fixed_array(&array, &ielems, &len); + if (!ielems || len == 0) { + return dbus_message_new_error( + message, DBUS_ERROR_INVALID_ARGS, "Invalid value"); + } + + if (ieee802_11_parse_elems(ielems, len, &elems, 0) == ParseFailed) { + return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, + "Parse error"); + } + + wpa_s = wpas_vendor_elem(wpa_s, frame_id); + if (!wpa_s->vendor_elem[frame_id]) { + wpa_s->vendor_elem[frame_id] = wpabuf_alloc_copy(ielems, len); + wpas_vendor_elem_update(wpa_s); + return NULL; + } + + if (wpabuf_resize(&wpa_s->vendor_elem[frame_id], len) < 0) { + return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, + "Resize error"); + } + + wpabuf_put_data(wpa_s->vendor_elem[frame_id], ielems, len); + wpas_vendor_elem_update(wpa_s); + return NULL; +} + + +DBusMessage * wpas_dbus_handler_vendor_elem_get(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply; + DBusMessageIter iter, array_iter; + dbus_int32_t frame_id; + const u8 *elem; + size_t elem_len; + + dbus_message_iter_init(message, &iter); + dbus_message_iter_get_basic(&iter, &frame_id); + + if (frame_id < 0 || frame_id >= NUM_VENDOR_ELEM_FRAMES) { + return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, + "Invalid ID"); + } + + wpa_s = wpas_vendor_elem(wpa_s, frame_id); + if (!wpa_s->vendor_elem[frame_id]) { + return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, + "ID value does not exist"); + } + + reply = dbus_message_new_method_return(message); + if (!reply) + return wpas_dbus_error_no_memory(message); + + dbus_message_iter_init_append(reply, &iter); + + elem = wpabuf_head_u8(wpa_s->vendor_elem[frame_id]); + elem_len = wpabuf_len(wpa_s->vendor_elem[frame_id]); + + if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_TYPE_BYTE_AS_STRING, + &array_iter) || + !dbus_message_iter_append_fixed_array(&array_iter, DBUS_TYPE_BYTE, + &elem, elem_len) || + !dbus_message_iter_close_container(&iter, &array_iter)) { + dbus_message_unref(reply); + reply = wpas_dbus_error_no_memory(message); + } + + return reply; +} + + +DBusMessage * wpas_dbus_handler_vendor_elem_remove(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + u8 *ielems; + int len; + struct ieee802_11_elems elems; + DBusMessageIter iter, array; + dbus_int32_t frame_id; + + dbus_message_iter_init(message, &iter); + dbus_message_iter_get_basic(&iter, &frame_id); + if (frame_id < 0 || frame_id >= NUM_VENDOR_ELEM_FRAMES) { + return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, + "Invalid ID"); + } + + dbus_message_iter_next(&iter); + dbus_message_iter_recurse(&iter, &array); + dbus_message_iter_get_fixed_array(&array, &ielems, &len); + if (!ielems || len == 0) { + return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, + "Invalid value"); + } + + wpa_s = wpas_vendor_elem(wpa_s, frame_id); + + if (len == 1 && *ielems == '*') { + wpabuf_free(wpa_s->vendor_elem[frame_id]); + wpa_s->vendor_elem[frame_id] = NULL; + wpas_vendor_elem_update(wpa_s); + return NULL; + } + + if (!wpa_s->vendor_elem[frame_id]) { + return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, + "ID value does not exist"); + } + + if (ieee802_11_parse_elems(ielems, len, &elems, 0) == ParseFailed) { + return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, + "Parse error"); + } + + if (wpas_vendor_elem_remove(wpa_s, frame_id, ielems, len) == 0) + return NULL; + + return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, + "Not found"); +} diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.h b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.h index 50f72ec507bf..1d6235d6f3e4 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.h +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.h @@ -10,6 +10,8 @@ #ifndef CTRL_IFACE_DBUS_NEW_HANDLERS_H #define CTRL_IFACE_DBUS_NEW_HANDLERS_H +#include "dbus_new_helpers.h" + struct network_handler_args { struct wpa_supplicant *wpa_s; struct wpa_ssid *ssid; @@ -50,39 +52,20 @@ DBusMessage * wpas_dbus_handler_remove_interface(DBusMessage *message, DBusMessage * wpas_dbus_handler_get_interface(DBusMessage *message, struct wpa_global *global); -dbus_bool_t wpas_dbus_getter_debug_level(DBusMessageIter *iter, - DBusError *error, - void *user_data); +DBusMessage * wpas_dbus_handler_expect_disconnect(DBusMessage *message, + struct wpa_global *global); -dbus_bool_t wpas_dbus_getter_debug_timestamp(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_debug_show_keys(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_setter_debug_level(DBusMessageIter *iter, - DBusError *error, void *user_data); - -dbus_bool_t wpas_dbus_setter_debug_timestamp(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_setter_debug_show_keys(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_interfaces(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_eap_methods(DBusMessageIter *iter, - DBusError *error, void *user_data); - -dbus_bool_t wpas_dbus_getter_global_capabilities(DBusMessageIter *iter, - DBusError *error, - void *user_data); +DECLARE_ACCESSOR(wpas_dbus_getter_debug_level); +DECLARE_ACCESSOR(wpas_dbus_getter_debug_timestamp); +DECLARE_ACCESSOR(wpas_dbus_getter_debug_show_keys); +DECLARE_ACCESSOR(wpas_dbus_setter_debug_level); +DECLARE_ACCESSOR(wpas_dbus_setter_debug_timestamp); +DECLARE_ACCESSOR(wpas_dbus_setter_debug_show_keys); +DECLARE_ACCESSOR(wpas_dbus_getter_interfaces); +DECLARE_ACCESSOR(wpas_dbus_getter_eap_methods); +DECLARE_ACCESSOR(wpas_dbus_getter_global_capabilities); +DECLARE_ACCESSOR(wpas_dbus_getter_iface_global); +DECLARE_ACCESSOR(wpas_dbus_setter_iface_global); DBusMessage * wpas_dbus_handler_scan(DBusMessage *message, struct wpa_supplicant *wpa_s); @@ -146,150 +129,52 @@ DBusMessage * wpas_dbus_handler_eap_logoff(DBusMessage *message, DBusMessage * wpas_dbus_handler_eap_logon(DBusMessage *message, struct wpa_supplicant *wpa_s); -dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter, - DBusError *error, void *user_data); - -dbus_bool_t wpas_dbus_getter_state(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_scanning(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_ap_scan(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_setter_ap_scan(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_fast_reauth(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_setter_fast_reauth(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_disconnect_reason(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_bss_expire_age(DBusMessageIter *iter, - DBusError *error, void *user_data); - -dbus_bool_t wpas_dbus_setter_bss_expire_age(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_bss_expire_count(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_setter_bss_expire_count(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_country(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_setter_country(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_scan_interval(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_setter_scan_interval(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_ifname(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_driver(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_bridge_ifname(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_current_bss(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_current_network(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_current_auth_mode(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_bsss(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_networks(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_pkcs11_engine_path(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_pkcs11_module_path(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_blobs(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_bss_bssid(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_bss_ssid(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_bss_privacy(DBusMessageIter *iter, - DBusError *error, void *user_data); - -dbus_bool_t wpas_dbus_getter_bss_mode(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_bss_signal(DBusMessageIter *iter, - DBusError *error, void *user_data); - -dbus_bool_t wpas_dbus_getter_bss_frequency(DBusMessageIter *iter, - DBusError *error, void *user_data); - -dbus_bool_t wpas_dbus_getter_bss_rates(DBusMessageIter *iter, - DBusError *error, void *user_data); - -dbus_bool_t wpas_dbus_getter_bss_wpa(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_bss_rsn(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_bss_wps(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_bss_ies(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_bss_age(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_enabled(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_setter_enabled(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_network_properties(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_setter_network_properties(DBusMessageIter *iter, - DBusError *error, - void *user_data); +DECLARE_ACCESSOR(wpas_dbus_getter_capabilities); +DECLARE_ACCESSOR(wpas_dbus_getter_state); +DECLARE_ACCESSOR(wpas_dbus_getter_scanning); +DECLARE_ACCESSOR(wpas_dbus_getter_ap_scan); +DECLARE_ACCESSOR(wpas_dbus_setter_ap_scan); +DECLARE_ACCESSOR(wpas_dbus_getter_fast_reauth); +DECLARE_ACCESSOR(wpas_dbus_setter_fast_reauth); +DECLARE_ACCESSOR(wpas_dbus_getter_disconnect_reason); +DECLARE_ACCESSOR(wpas_dbus_getter_disassociate_reason); +DECLARE_ACCESSOR(wpas_dbus_getter_assoc_status_code); +DECLARE_ACCESSOR(wpas_dbus_getter_bss_expire_age); +DECLARE_ACCESSOR(wpas_dbus_setter_bss_expire_age); +DECLARE_ACCESSOR(wpas_dbus_getter_bss_expire_count); +DECLARE_ACCESSOR(wpas_dbus_setter_bss_expire_count); +DECLARE_ACCESSOR(wpas_dbus_getter_country); +DECLARE_ACCESSOR(wpas_dbus_setter_country); +DECLARE_ACCESSOR(wpas_dbus_getter_scan_interval); +DECLARE_ACCESSOR(wpas_dbus_setter_scan_interval); +DECLARE_ACCESSOR(wpas_dbus_getter_ifname); +DECLARE_ACCESSOR(wpas_dbus_getter_driver); +DECLARE_ACCESSOR(wpas_dbus_getter_bridge_ifname); +DECLARE_ACCESSOR(wpas_dbus_getter_config_file); +DECLARE_ACCESSOR(wpas_dbus_getter_current_bss); +DECLARE_ACCESSOR(wpas_dbus_getter_current_network); +DECLARE_ACCESSOR(wpas_dbus_getter_current_auth_mode); +DECLARE_ACCESSOR(wpas_dbus_getter_bsss); +DECLARE_ACCESSOR(wpas_dbus_getter_networks); +DECLARE_ACCESSOR(wpas_dbus_getter_pkcs11_engine_path); +DECLARE_ACCESSOR(wpas_dbus_getter_pkcs11_module_path); +DECLARE_ACCESSOR(wpas_dbus_getter_blobs); +DECLARE_ACCESSOR(wpas_dbus_getter_bss_bssid); +DECLARE_ACCESSOR(wpas_dbus_getter_bss_ssid); +DECLARE_ACCESSOR(wpas_dbus_getter_bss_privacy); +DECLARE_ACCESSOR(wpas_dbus_getter_bss_mode); +DECLARE_ACCESSOR(wpas_dbus_getter_bss_signal); +DECLARE_ACCESSOR(wpas_dbus_getter_bss_frequency); +DECLARE_ACCESSOR(wpas_dbus_getter_bss_rates); +DECLARE_ACCESSOR(wpas_dbus_getter_bss_wpa); +DECLARE_ACCESSOR(wpas_dbus_getter_bss_rsn); +DECLARE_ACCESSOR(wpas_dbus_getter_bss_wps); +DECLARE_ACCESSOR(wpas_dbus_getter_bss_ies); +DECLARE_ACCESSOR(wpas_dbus_getter_bss_age); +DECLARE_ACCESSOR(wpas_dbus_getter_enabled); +DECLARE_ACCESSOR(wpas_dbus_setter_enabled); +DECLARE_ACCESSOR(wpas_dbus_getter_network_properties); +DECLARE_ACCESSOR(wpas_dbus_setter_network_properties); DBusMessage * wpas_dbus_handler_wps_start(DBusMessage *message, struct wpa_supplicant *wpa_s); @@ -297,20 +182,10 @@ DBusMessage * wpas_dbus_handler_wps_start(DBusMessage *message, DBusMessage * wpas_dbus_handler_wps_cancel(DBusMessage *message, struct wpa_supplicant *wpa_s); -dbus_bool_t wpas_dbus_getter_process_credentials(DBusMessageIter *iter, - DBusError *error, void *user_data); - -dbus_bool_t wpas_dbus_setter_process_credentials(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_config_methods(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_setter_config_methods(DBusMessageIter *iter, - DBusError *error, - void *user_data); +DECLARE_ACCESSOR(wpas_dbus_getter_process_credentials); +DECLARE_ACCESSOR(wpas_dbus_setter_process_credentials); +DECLARE_ACCESSOR(wpas_dbus_getter_config_methods); +DECLARE_ACCESSOR(wpas_dbus_setter_config_methods); DBusMessage * wpas_dbus_handler_tdls_discover(DBusMessage *message, struct wpa_supplicant *wpa_s); @@ -321,6 +196,16 @@ DBusMessage * wpas_dbus_handler_tdls_status(DBusMessage *message, DBusMessage * wpas_dbus_handler_tdls_teardown(DBusMessage *message, struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_vendor_elem_add(DBusMessage *message, + struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_vendor_elem_get(DBusMessage *message, + struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_vendor_elem_remove( + DBusMessage *message, struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_handler_save_config(DBusMessage *message, + struct wpa_supplicant *wpa_s); + DBusMessage * wpas_dbus_error_invalid_args(DBusMessage *message, const char *arg); DBusMessage * wpas_dbus_error_unknown_error(DBusMessage *message, diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.c b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.c index 67c079e7506d..73b9e20c20b0 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.c @@ -364,13 +364,14 @@ DBusMessage * wpas_dbus_handler_p2p_group_add(DBusMessage *message, goto inv_args; if (wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0, 0, 0, - NULL, 0, 0)) { + 0, 0, NULL, 0, 0)) { reply = wpas_dbus_error_unknown_error( message, "Failed to reinvoke a persistent group"); goto out; } - } else if (wpas_p2p_group_add(wpa_s, persistent_group, freq, 0, 0)) + } else if (wpas_p2p_group_add(wpa_s, persistent_group, freq, 0, 0, 0, + 0)) goto inv_args; out: @@ -582,7 +583,7 @@ DBusMessage * wpas_dbus_handler_p2p_connect(DBusMessage *message, new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method, persistent_group, 0, join, authorize_only, - go_intent, freq, -1, 0, 0, 0); + go_intent, freq, 0, -1, 0, 0, 0, 0, NULL, 0); if (new_pin >= 0) { char npin[9]; @@ -733,8 +734,8 @@ DBusMessage * wpas_dbus_handler_p2p_invite(DBusMessage *message, if (ssid == NULL || ssid->disabled != 2) goto err; - if (wpas_p2p_invite(wpa_s, peer_addr, ssid, NULL, 0, 0, 0, 0) < - 0) { + if (wpas_p2p_invite(wpa_s, peer_addr, ssid, NULL, 0, 0, 0, 0, 0, + 0) < 0) { reply = wpas_dbus_error_unknown_error( message, "Failed to reinvoke a persistent group"); @@ -807,9 +808,9 @@ DBusMessage * wpas_dbus_handler_p2p_prov_disc_req(DBusMessage *message, * P2P Device property accessor methods. */ -dbus_bool_t wpas_dbus_getter_p2p_device_config(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_device_config( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; DBusMessageIter variant_iter, dict_iter; @@ -916,9 +917,9 @@ err_no_mem: } -dbus_bool_t wpas_dbus_setter_p2p_device_config(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_setter_p2p_device_config( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; DBusMessageIter variant_iter, iter_dict; @@ -944,7 +945,8 @@ dbus_bool_t wpas_dbus_setter_p2p_device_config(DBusMessageIter *iter, if (os_strcmp(entry.key, "DeviceName") == 0) { char *devname; - if (entry.type != DBUS_TYPE_STRING) + if (entry.type != DBUS_TYPE_STRING || + os_strlen(entry.str_value) > WPS_DEV_NAME_MAX_LEN) goto error; devname = os_strdup(entry.str_value); @@ -1087,8 +1089,9 @@ dbus_bool_t wpas_dbus_setter_p2p_device_config(DBusMessageIter *iter, } -dbus_bool_t wpas_dbus_getter_p2p_peers(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_peers( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; struct p2p_data *p2p = wpa_s->global->p2p; @@ -1201,8 +1204,9 @@ static enum wpas_p2p_role wpas_get_p2p_role(struct wpa_supplicant *wpa_s) } -dbus_bool_t wpas_dbus_getter_p2p_role(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_role( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; char *str; @@ -1224,8 +1228,9 @@ dbus_bool_t wpas_dbus_getter_p2p_role(DBusMessageIter *iter, DBusError *error, } -dbus_bool_t wpas_dbus_getter_p2p_group(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_group( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; char path_buf[WPAS_DBUS_OBJECT_PATH_MAX]; @@ -1243,8 +1248,9 @@ dbus_bool_t wpas_dbus_getter_p2p_group(DBusMessageIter *iter, DBusError *error, } -dbus_bool_t wpas_dbus_getter_p2p_peergo(DBusMessageIter *iter, - DBusError *error, void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_peergo( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; char go_peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path; @@ -1271,9 +1277,9 @@ dbus_bool_t wpas_dbus_getter_p2p_peergo(DBusMessageIter *iter, * Peer object properties accessor methods */ -dbus_bool_t wpas_dbus_getter_p2p_peer_device_name(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_peer_device_name( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; @@ -1309,9 +1315,9 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_device_name(DBusMessageIter *iter, } -dbus_bool_t wpas_dbus_getter_p2p_peer_manufacturer(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_peer_manufacturer( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; @@ -1346,9 +1352,9 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_manufacturer(DBusMessageIter *iter, } -dbus_bool_t wpas_dbus_getter_p2p_peer_modelname(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_peer_modelname( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; @@ -1383,9 +1389,9 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_modelname(DBusMessageIter *iter, } -dbus_bool_t wpas_dbus_getter_p2p_peer_modelnumber(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_peer_modelnumber( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; @@ -1420,9 +1426,9 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_modelnumber(DBusMessageIter *iter, } -dbus_bool_t wpas_dbus_getter_p2p_peer_serialnumber(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_peer_serialnumber( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; @@ -1458,6 +1464,7 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_serialnumber(DBusMessageIter *iter, dbus_bool_t wpas_dbus_getter_p2p_peer_primary_device_type( + const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; @@ -1483,9 +1490,9 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_primary_device_type( } -dbus_bool_t wpas_dbus_getter_p2p_peer_config_method(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_peer_config_method( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; @@ -1508,9 +1515,9 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_config_method(DBusMessageIter *iter, } -dbus_bool_t wpas_dbus_getter_p2p_peer_level(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_peer_level( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; @@ -1533,9 +1540,9 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_level(DBusMessageIter *iter, } -dbus_bool_t wpas_dbus_getter_p2p_peer_device_capability(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_peer_device_capability( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; @@ -1558,9 +1565,9 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_device_capability(DBusMessageIter *iter, } -dbus_bool_t wpas_dbus_getter_p2p_peer_group_capability(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_peer_group_capability( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; @@ -1584,6 +1591,7 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_group_capability(DBusMessageIter *iter, dbus_bool_t wpas_dbus_getter_p2p_peer_secondary_device_types( + const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; @@ -1649,9 +1657,9 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_secondary_device_types( } -dbus_bool_t wpas_dbus_getter_p2p_peer_vendor_extension(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_peer_vendor_extension( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpabuf *vendor_extension[P2P_MAX_WPS_VENDOR_EXT]; unsigned int i, num = 0; @@ -1684,8 +1692,9 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_vendor_extension(DBusMessageIter *iter, } -dbus_bool_t wpas_dbus_getter_p2p_peer_ies(DBusMessageIter *iter, - DBusError *error, void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_peer_ies( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; @@ -1709,9 +1718,9 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_ies(DBusMessageIter *iter, } -dbus_bool_t wpas_dbus_getter_p2p_peer_device_address(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_peer_device_address( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; @@ -1774,9 +1783,9 @@ out_of_memory: } -dbus_bool_t wpas_dbus_getter_p2p_peer_groups(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_peer_groups( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; @@ -1842,9 +1851,9 @@ out: * * Getter for "PersistentGroups" property. */ -dbus_bool_t wpas_dbus_getter_persistent_groups(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_persistent_groups( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; struct wpa_ssid *ssid; @@ -1904,16 +1913,16 @@ out: * * Getter for "Properties" property of a persistent group. */ -dbus_bool_t wpas_dbus_getter_persistent_group_properties(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_persistent_group_properties( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct network_handler_args *net = user_data; /* Leveraging the fact that persistent group object is still * represented in same manner as network within. */ - return wpas_dbus_getter_network_properties(iter, error, net); + return wpas_dbus_getter_network_properties(property_desc, iter, error, net); } @@ -1927,9 +1936,9 @@ dbus_bool_t wpas_dbus_getter_persistent_group_properties(DBusMessageIter *iter, * * Setter for "Properties" property of a persistent group. */ -dbus_bool_t wpas_dbus_setter_persistent_group_properties(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_setter_persistent_group_properties( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct network_handler_args *net = user_data; struct wpa_ssid *ssid = net->ssid; @@ -2142,9 +2151,9 @@ DBusMessage * wpas_dbus_handler_remove_all_persistent_groups( * Group object properties accessor methods */ -dbus_bool_t wpas_dbus_getter_p2p_group_members(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_group_members( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; struct wpa_ssid *ssid; @@ -2211,8 +2220,9 @@ out_of_memory: } -dbus_bool_t wpas_dbus_getter_p2p_group_ssid(DBusMessageIter *iter, - DBusError *error, void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_group_ssid( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; @@ -2224,9 +2234,9 @@ dbus_bool_t wpas_dbus_getter_p2p_group_ssid(DBusMessageIter *iter, } -dbus_bool_t wpas_dbus_getter_p2p_group_bssid(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_group_bssid( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; u8 role = wpas_get_p2p_role(wpa_s); @@ -2248,9 +2258,9 @@ dbus_bool_t wpas_dbus_getter_p2p_group_bssid(DBusMessageIter *iter, } -dbus_bool_t wpas_dbus_getter_p2p_group_frequency(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_group_frequency( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; u16 op_freq; @@ -2271,9 +2281,9 @@ dbus_bool_t wpas_dbus_getter_p2p_group_frequency(DBusMessageIter *iter, } -dbus_bool_t wpas_dbus_getter_p2p_group_passphrase(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_group_passphrase( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; char *p_pass; @@ -2292,8 +2302,9 @@ dbus_bool_t wpas_dbus_getter_p2p_group_passphrase(DBusMessageIter *iter, } -dbus_bool_t wpas_dbus_getter_p2p_group_psk(DBusMessageIter *iter, - DBusError *error, void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_group_psk( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; u8 *p_psk = NULL; @@ -2313,9 +2324,9 @@ dbus_bool_t wpas_dbus_getter_p2p_group_psk(DBusMessageIter *iter, } -dbus_bool_t wpas_dbus_getter_p2p_group_vendor_ext(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_group_vendor_ext( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; struct hostapd_data *hapd; @@ -2348,9 +2359,9 @@ dbus_bool_t wpas_dbus_getter_p2p_group_vendor_ext(DBusMessageIter *iter, } -dbus_bool_t wpas_dbus_setter_p2p_group_vendor_ext(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_setter_p2p_group_vendor_ext( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; DBusMessageIter variant_iter, iter_dict, array_iter, sub; @@ -2876,8 +2887,9 @@ DBusMessage * wpas_dbus_handler_p2p_serv_disc_external( #ifdef CONFIG_WIFI_DISPLAY -dbus_bool_t wpas_dbus_getter_global_wfd_ies(DBusMessageIter *iter, - DBusError *error, void *user_data) +dbus_bool_t wpas_dbus_getter_global_wfd_ies( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_global *global = user_data; struct wpabuf *ie; @@ -2898,8 +2910,9 @@ dbus_bool_t wpas_dbus_getter_global_wfd_ies(DBusMessageIter *iter, } -dbus_bool_t wpas_dbus_setter_global_wfd_ies(DBusMessageIter *iter, - DBusError *error, void *user_data) +dbus_bool_t wpas_dbus_setter_global_wfd_ies( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_global *global = user_data; DBusMessageIter variant, array; diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.h b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.h index 2aecbbe46507..c4c02615dbc3 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.h +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.h @@ -89,139 +89,50 @@ DBusMessage *wpas_dbus_handler_p2p_serv_disc_external( /* * P2P Device property accessor methods. */ -dbus_bool_t wpas_dbus_setter_p2p_device_config(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_p2p_device_config(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_p2p_peers(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_p2p_role(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_p2p_group(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_p2p_peergo(DBusMessageIter *iter, - DBusError *error, - void *user_data); +DECLARE_ACCESSOR(wpas_dbus_setter_p2p_device_config); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_device_config); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peers); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_role); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peergo); /* * P2P Peer properties. */ - -dbus_bool_t wpas_dbus_getter_p2p_peer_device_name(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_p2p_peer_manufacturer(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_p2p_peer_modelname(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_p2p_peer_modelnumber(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_p2p_peer_serialnumber(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_p2p_peer_primary_device_type( - DBusMessageIter *iter, DBusError *error, void *user_data); - -dbus_bool_t wpas_dbus_getter_p2p_peer_config_method(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_p2p_peer_level(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_p2p_peer_device_capability(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_p2p_peer_group_capability(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_p2p_peer_secondary_device_types( - DBusMessageIter *iter, DBusError *error, void *user_data); - -dbus_bool_t wpas_dbus_getter_p2p_peer_vendor_extension(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_p2p_peer_ies(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_p2p_peer_device_address(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_p2p_peer_groups(DBusMessageIter *iter, - DBusError *error, - void *user_data); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_device_name); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_manufacturer); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_modelname); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_modelnumber); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_serialnumber); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_primary_device_type); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_config_method); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_level); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_device_capability); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_group_capability); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_secondary_device_types); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_vendor_extension); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_ies); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_device_address); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_groups); /* * P2P Group properties */ - -dbus_bool_t wpas_dbus_getter_p2p_group_members(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_p2p_group_ssid(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_p2p_group_bssid(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_p2p_group_frequency(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_p2p_group_passphrase(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_p2p_group_psk(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_p2p_group_vendor_ext(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_setter_p2p_group_vendor_ext(DBusMessageIter *iter, - DBusError *error, - void *user_data); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group_members); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group_ssid); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group_bssid); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group_frequency); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group_passphrase); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group_psk); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group_vendor_ext); +DECLARE_ACCESSOR(wpas_dbus_setter_p2p_group_vendor_ext); /* * P2P Persistent Groups and properties */ - -dbus_bool_t wpas_dbus_getter_persistent_groups(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_persistent_group_properties(DBusMessageIter *iter, - DBusError *error, void *user_data); - -dbus_bool_t wpas_dbus_setter_persistent_group_properties(DBusMessageIter *iter, - DBusError *error, - void *user_data); +DECLARE_ACCESSOR(wpas_dbus_getter_persistent_groups); +DECLARE_ACCESSOR(wpas_dbus_getter_persistent_group_properties); +DECLARE_ACCESSOR(wpas_dbus_setter_persistent_group_properties); DBusMessage * wpas_dbus_handler_add_persistent_group( DBusMessage *message, struct wpa_supplicant *wpa_s); @@ -233,15 +144,8 @@ DBusMessage * wpas_dbus_handler_remove_all_persistent_groups( DBusMessage *message, struct wpa_supplicant *wpa_s); #ifdef CONFIG_WIFI_DISPLAY - -dbus_bool_t wpas_dbus_getter_global_wfd_ies(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_setter_global_wfd_ies(DBusMessageIter *iter, - DBusError *error, - void *user_data); - +DECLARE_ACCESSOR(wpas_dbus_getter_global_wfd_ies); +DECLARE_ACCESSOR(wpas_dbus_setter_global_wfd_ies); #endif /* CONFIG_WIFI_DISPLAY */ #endif /* DBUS_NEW_HANDLERS_P2P_H */ diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_wps.c b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_wps.c index b2251baa3fe5..f16e2290c7ed 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_wps.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_wps.c @@ -325,7 +325,7 @@ DBusMessage * wpas_dbus_handler_wps_start(DBusMessage *message, * @wpa_s: %wpa_supplicant data structure * Returns: NULL on success or DBus error on failure * - * Handler for "Cancel" method call. Returns NULL if WPS cancel successfull + * Handler for "Cancel" method call. Returns NULL if WPS cancel successful * or DBus error on WPS cancel failure */ DBusMessage * wpas_dbus_handler_wps_cancel(DBusMessage *message, @@ -349,9 +349,9 @@ DBusMessage * wpas_dbus_handler_wps_cancel(DBusMessage *message, * true if wps_cred_processing configuration field is not equal to 1 or false * if otherwise. */ -dbus_bool_t wpas_dbus_getter_process_credentials(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_process_credentials( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; dbus_bool_t process = wpa_s->conf->wps_cred_processing != 1; @@ -371,9 +371,9 @@ dbus_bool_t wpas_dbus_getter_process_credentials(DBusMessageIter *iter, * Setter for "ProcessCredentials" property. Sets credentials_processed on 2 * if boolean argument is true or on 1 if otherwise. */ -dbus_bool_t wpas_dbus_setter_process_credentials(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_setter_process_credentials( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; dbus_bool_t process_credentials, old_pc; @@ -407,9 +407,9 @@ dbus_bool_t wpas_dbus_setter_process_credentials(DBusMessageIter *iter, * Getter for "ConfigMethods" property. Returned boolean will be true if * providing the relevant string worked, or false otherwise. */ -dbus_bool_t wpas_dbus_getter_config_methods(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_config_methods( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; char *methods = wpa_s->conf->config_methods; @@ -431,9 +431,9 @@ dbus_bool_t wpas_dbus_getter_config_methods(DBusMessageIter *iter, * Setter for "ConfigMethods" property. Sets the methods string, apply such * change and returns true on success. Returns false otherwise. */ -dbus_bool_t wpas_dbus_setter_config_methods(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_setter_config_methods( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; char *methods, *new_methods; diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.c b/contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.c index 45623f346465..0115e32a1d34 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.c @@ -46,7 +46,7 @@ static dbus_bool_t fill_dict_with_properties( goto error; /* An error getting a property fails the request entirely */ - if (!dsc->getter(&entry_iter, error, user_data)) { + if (!dsc->getter(dsc, &entry_iter, error, user_data)) { wpa_printf(MSG_INFO, "dbus: %s dbus_interface=%s dbus_property=%s getter failed", __func__, dsc->dbus_interface, @@ -176,7 +176,7 @@ static DBusMessage * properties_get(DBusMessage *message, dbus_message_iter_init_append(reply, &iter); dbus_error_init(&error); - if (dsc->getter(&iter, &error, user_data) == FALSE) { + if (dsc->getter(dsc, &iter, &error, user_data) == FALSE) { dbus_message_unref(reply); reply = wpas_dbus_reply_new_from_error( message, &error, DBUS_ERROR_FAILED, @@ -213,7 +213,7 @@ static DBusMessage * properties_set(DBusMessage *message, /* Iter will now point to the property's new value */ dbus_error_init(&error); - if (dsc->setter(&iter, &error, user_data) == TRUE) { + if (dsc->setter(dsc, &iter, &error, user_data) == TRUE) { /* Success */ reply = dbus_message_new_method_return(message); } else { @@ -627,7 +627,8 @@ static dbus_bool_t put_changed_properties( return FALSE; dbus_error_init(&error); - if (!dsc->getter(&entry_iter, &error, obj_dsc->user_data)) { + if (!dsc->getter(dsc, &entry_iter, &error, obj_dsc->user_data)) + { if (dbus_error_is_set(&error)) { wpa_printf(MSG_ERROR, "dbus: %s: Cannot get new value of property %s: (%s) %s", diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.h b/contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.h index 6e2c1f1933f1..7b63b28d7707 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.h +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.h @@ -16,9 +16,13 @@ typedef DBusMessage * (*WPADBusMethodHandler)(DBusMessage *message, void *user_data); typedef void (*WPADBusArgumentFreeFunction)(void *handler_arg); -typedef dbus_bool_t (*WPADBusPropertyAccessor)(DBusMessageIter *iter, - DBusError *error, - void *user_data); +struct wpa_dbus_property_desc; +typedef dbus_bool_t (*WPADBusPropertyAccessor)( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data); +#define DECLARE_ACCESSOR(f) \ +dbus_bool_t f(const struct wpa_dbus_property_desc *property_desc, \ + DBusMessageIter *iter, DBusError *error, void *user_data) struct wpa_dbus_object_desc { DBusConnection *connection; @@ -89,6 +93,8 @@ struct wpa_dbus_property_desc { WPADBusPropertyAccessor getter; /* property setter function */ WPADBusPropertyAccessor setter; + /* other data */ + const char *data; }; diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_introspect.c b/contrib/wpa/wpa_supplicant/dbus/dbus_new_introspect.c index fba57e6361ae..aee105b4b54c 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_introspect.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_introspect.c @@ -38,7 +38,7 @@ static struct interfaces * add_interface(struct dl_list *list, if (!iface) return NULL; iface->dbus_interface = os_strdup(dbus_interface); - iface->xml = wpabuf_alloc(6000); + iface->xml = wpabuf_alloc(15000); if (iface->dbus_interface == NULL || iface->xml == NULL) { os_free(iface->dbus_interface); wpabuf_free(iface->xml); @@ -257,7 +257,7 @@ DBusMessage * wpa_dbus_introspect(DBusMessage *message, DBusMessage *reply; struct wpabuf *xml; - xml = wpabuf_alloc(15000); + xml = wpabuf_alloc(20000); if (xml == NULL) return NULL; diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers.c b/contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers.c index e8f62ef6bdc3..e540832f254b 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers.c @@ -717,16 +717,13 @@ DBusMessage * wpas_dbus_iface_add_network(DBusMessage *message, char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *path = path_buf; if (wpa_s->dbus_path) - ssid = wpa_config_add_network(wpa_s->conf); + 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; } - wpas_notify_network_added(wpa_s, ssid); - ssid->disabled = 1; - wpa_config_set_network_defaults(ssid); /* Construct the object path for this network. */ os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX, @@ -758,7 +755,7 @@ DBusMessage * wpas_dbus_iface_remove_network(DBusMessage *message, const char *op; char *iface = NULL, *net_id = NULL; int id; - struct wpa_ssid *ssid; + int result; if (!dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &op, @@ -781,19 +778,12 @@ DBusMessage * wpas_dbus_iface_remove_network(DBusMessage *message, } id = strtoul(net_id, NULL, 10); - ssid = wpa_config_get_network(wpa_s->conf, id); - if (ssid == NULL) { + result = wpa_supplicant_remove_network(wpa_s, id); + if (result == -1) { reply = wpas_dbus_new_invalid_network_error(message); goto out; } - - wpas_notify_network_removed(wpa_s, ssid); - - if (ssid == wpa_s->current_ssid) - wpa_supplicant_deauthenticate(wpa_s, - WLAN_REASON_DEAUTH_LEAVING); - - if (wpa_config_remove_network(wpa_s->conf, id) < 0) { + if (result == -2) { reply = dbus_message_new_error( message, WPAS_ERROR_REMOVE_NETWORK_ERROR, "error removing the specified on this interface."); @@ -1069,8 +1059,7 @@ out: DBusMessage * wpas_dbus_iface_disconnect(DBusMessage *message, struct wpa_supplicant *wpa_s) { - wpa_s->disconnected = 1; - wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); + wpas_request_disconnection(wpa_s); return wpas_dbus_new_success_reply(message); } diff --git a/contrib/wpa/wpa_supplicant/defconfig b/contrib/wpa/wpa_supplicant/defconfig index 01a8c2ccb00b..1d05198f849a 100644 --- a/contrib/wpa/wpa_supplicant/defconfig +++ b/contrib/wpa/wpa_supplicant/defconfig @@ -31,6 +31,9 @@ CONFIG_DRIVER_WEXT=y # Driver interface for Linux drivers using the nl80211 kernel interface CONFIG_DRIVER_NL80211=y +# QCA vendor extensions to nl80211 +#CONFIG_DRIVER_NL80211_QCA=y + # driver_nl80211.c requires libnl. If you are compiling it yourself # you may need to point hostapd to your version of libnl. # @@ -267,6 +270,9 @@ CONFIG_BACKEND=file # Should we use epoll instead of select? Select is used by default. #CONFIG_ELOOP_EPOLL=y +# Should we use kqueue instead of select? Select is used by default. +#CONFIG_ELOOP_KQUEUE=y + # Select layer 2 packet implementation # linux = Linux packet socket (default) # pcap = libpcap/libdnet/WinPcap @@ -276,6 +282,12 @@ CONFIG_BACKEND=file # none = Empty template #CONFIG_L2_PACKET=linux +# Disable Linux packet socket workaround applicable for station interface +# in a bridge for EAPOL frames. This should be uncommented only if the kernel +# is known to not have the regression issue in packet socket behavior with +# bridge interfaces (commit 'bridge: respect RFC2863 operational state')'). +#CONFIG_NO_LINUX_PACKET_SOCKET_WAR=y + # PeerKey handshake for Station to Station Link (IEEE 802.11e DLS) CONFIG_PEERKEY=y @@ -455,6 +467,9 @@ CONFIG_PEERKEY=y # Hotspot 2.0 #CONFIG_HS20=y +# Enable interface matching in wpa_supplicant +#CONFIG_MATCH_IFACE=y + # Disable roaming in wpa_supplicant #CONFIG_NO_ROAMING=y @@ -504,3 +519,32 @@ CONFIG_PEERKEY=y # OS X builds. This is only for building eapol_test. #CONFIG_OSX=y + +# Automatic Channel Selection +# This will allow wpa_supplicant to pick the channel automatically when channel +# is set to "0". +# +# TODO: Extend parser to be able to parse "channel=acs_survey" as an alternative +# to "channel=0". This would enable us to eventually add other ACS algorithms in +# similar way. +# +# Automatic selection is currently only done through initialization, later on +# we hope to do background checks to keep us moving to more ideal channels as +# time goes by. ACS is currently only supported through the nl80211 driver and +# your driver must have survey dump capability that is filled by the driver +# during scanning. +# +# TODO: In analogy to hostapd be able to customize the ACS survey algorithm with +# a newly to create wpa_supplicant.conf variable acs_num_scans. +# +# Supported ACS drivers: +# * ath9k +# * ath5k +# * ath10k +# +# For more details refer to: +# http://wireless.kernel.org/en/users/Documentation/acs +#CONFIG_ACS=y + +# Support Multi Band Operation +#CONFIG_MBO=y diff --git a/contrib/wpa/wpa_supplicant/driver_i.h b/contrib/wpa/wpa_supplicant/driver_i.h index 73768c756f0a..220b7ba3ddca 100644 --- a/contrib/wpa/wpa_supplicant/driver_i.h +++ b/contrib/wpa/wpa_supplicant/driver_i.h @@ -100,12 +100,10 @@ static inline int wpa_drv_scan(struct wpa_supplicant *wpa_s, } static inline int wpa_drv_sched_scan(struct wpa_supplicant *wpa_s, - struct wpa_driver_scan_params *params, - u32 interval) + struct wpa_driver_scan_params *params) { if (wpa_s->driver->sched_scan) - return wpa_s->driver->sched_scan(wpa_s->drv_priv, - params, interval); + return wpa_s->driver->sched_scan(wpa_s->drv_priv, params); return -1; } @@ -160,6 +158,15 @@ static inline int wpa_drv_set_key(struct wpa_supplicant *wpa_s, return -1; } +static inline int wpa_drv_get_seqnum(struct wpa_supplicant *wpa_s, + const u8 *addr, int idx, u8 *seq) +{ + if (wpa_s->driver->get_seqnum) + return wpa_s->driver->get_seqnum(wpa_s->ifname, wpa_s->drv_priv, + addr, idx, seq); + return -1; +} + static inline int wpa_drv_sta_deauth(struct wpa_supplicant *wpa_s, const u8 *addr, int reason_code) { @@ -292,7 +299,7 @@ static inline int wpa_drv_send_mlme(struct wpa_supplicant *wpa_s, if (wpa_s->driver->send_mlme) return wpa_s->driver->send_mlme(wpa_s->drv_priv, data, data_len, noack, - freq); + freq, NULL, 0); return -1; } @@ -401,7 +408,7 @@ static inline int wpa_drv_if_add(struct wpa_supplicant *wpa_s, if (wpa_s->driver->if_add) return wpa_s->driver->if_add(wpa_s->drv_priv, type, ifname, addr, bss_ctx, NULL, force_ifname, - if_addr, bridge, 0); + if_addr, bridge, 0, 0); return -1; } @@ -726,12 +733,11 @@ static inline int wpa_drv_set_replay_protect(struct wpa_supplicant *wpa_s, } static inline int wpa_drv_set_current_cipher_suite(struct wpa_supplicant *wpa_s, - const u8 *cs, size_t cs_len) + u64 cs) { if (!wpa_s->driver->set_current_cipher_suite) return -1; - return wpa_s->driver->set_current_cipher_suite(wpa_s->drv_priv, cs, - cs_len); + return wpa_s->driver->set_current_cipher_suite(wpa_s->drv_priv, cs); } static inline int wpa_drv_enable_controlled_port(struct wpa_supplicant *wpa_s, @@ -912,4 +918,62 @@ static inline int wpa_drv_set_prob_oper_freq(struct wpa_supplicant *wpa_s, return wpa_s->driver->set_prob_oper_freq(wpa_s->drv_priv, freq); } +static inline int wpa_drv_abort_scan(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->driver->abort_scan) + return -1; + return wpa_s->driver->abort_scan(wpa_s->drv_priv); +} + +static inline int wpa_drv_configure_frame_filters(struct wpa_supplicant *wpa_s, + u32 filters) +{ + if (!wpa_s->driver->configure_data_frame_filters) + return -1; + return wpa_s->driver->configure_data_frame_filters(wpa_s->drv_priv, + filters); +} + +static inline int wpa_drv_get_ext_capa(struct wpa_supplicant *wpa_s, + enum wpa_driver_if_type type) +{ + if (!wpa_s->driver->get_ext_capab) + return -1; + return wpa_s->driver->get_ext_capab(wpa_s->drv_priv, type, + &wpa_s->extended_capa, + &wpa_s->extended_capa_mask, + &wpa_s->extended_capa_len); +} + +static inline int wpa_drv_p2p_lo_start(struct wpa_supplicant *wpa_s, + unsigned int channel, + unsigned int period, + unsigned int interval, + unsigned int count, + const u8 *device_types, + size_t dev_types_len, + const u8 *ies, size_t ies_len) +{ + if (!wpa_s->driver->p2p_lo_start) + return -1; + return wpa_s->driver->p2p_lo_start(wpa_s->drv_priv, channel, period, + interval, count, device_types, + dev_types_len, ies, ies_len); +} + +static inline int wpa_drv_p2p_lo_stop(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->driver->p2p_lo_stop) + return -1; + return wpa_s->driver->p2p_lo_stop(wpa_s->drv_priv); +} + +static inline int wpa_drv_set_default_scan_ies(struct wpa_supplicant *wpa_s, + const u8 *ies, size_t len) +{ + if (!wpa_s->driver->set_default_scan_ies) + return -1; + return wpa_s->driver->set_default_scan_ies(wpa_s->drv_priv, ies, len); +} + #endif /* DRIVER_I_H */ diff --git a/contrib/wpa/wpa_supplicant/eapol_test.c b/contrib/wpa/wpa_supplicant/eapol_test.c index dce7d1fadd44..6548bd17b11f 100644 --- a/contrib/wpa/wpa_supplicant/eapol_test.c +++ b/contrib/wpa/wpa_supplicant/eapol_test.c @@ -14,6 +14,7 @@ #include "common.h" #include "utils/ext_password.h" +#include "common/version.h" #include "config.h" #include "eapol_supp/eapol_supp_sm.h" #include "eap_peer/eap.h" @@ -192,7 +193,7 @@ static void ieee802_1x_encapsulate_radius(struct eapol_test_data *e, return; } - radius_msg_make_authenticator(msg, (u8 *) e, sizeof(*e)); + radius_msg_make_authenticator(msg); hdr = (const struct eap_hdr *) eap; pos = (const u8 *) (hdr + 1); @@ -257,6 +258,13 @@ static void ieee802_1x_encapsulate_radius(struct eapol_test_data *e, goto fail; } + if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_SERVICE_TYPE) && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_SERVICE_TYPE, + RADIUS_SERVICE_TYPE_FRAMED)) { + printf("Could not add Service-Type\n"); + goto fail; + } + os_snprintf(buf, sizeof(buf), "%s", e->connect_info); if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_CONNECT_INFO) && !radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, @@ -1239,7 +1247,7 @@ static void eapol_test_terminate(int sig, void *signal_ctx) static void usage(void) { printf("usage:\n" - "eapol_test [-enWS] -c [-a] [-p] " + "eapol_test [-enWSv] -c [-a] [-p] " "[-s]\\\n" " [-r] [-t] [-C] \\\n" " [-M] [-o = sets timeout in seconds (default: 30 s)\n" " -C = RADIUS Connect-Info (default: " "CONNECT 11Mbps 802.11b)\n" @@ -1317,7 +1326,7 @@ int main(int argc, char *argv[]) wpa_debug_show_keys = 1; for (;;) { - c = getopt(argc, argv, "a:A:c:C:ei:M:nN:o:p:P:r:R:s:St:T:W"); + c = getopt(argc, argv, "a:A:c:C:ei:M:nN:o:p:P:r:R:s:St:T:vW"); if (c < 0) break; switch (c) { @@ -1383,6 +1392,9 @@ int main(int argc, char *argv[]) ctrl_iface = optarg; eapol_test.ctrl_iface = 1; break; + case 'v': + printf("eapol_test v" VERSION_STR "\n"); + return 0; case 'W': wait_for_monitor++; break; diff --git a/contrib/wpa/wpa_supplicant/events.c b/contrib/wpa/wpa_supplicant/events.c index e2105ec505f5..abe3b476773d 100644 --- a/contrib/wpa/wpa_supplicant/events.c +++ b/contrib/wpa/wpa_supplicant/events.c @@ -72,6 +72,7 @@ static int wpas_temp_disabled(struct wpa_supplicant *wpa_s, } +#ifndef CONFIG_NO_SCAN_PROCESSING /** * wpas_reenabled_network_time - Time until first network is re-enabled * @wpa_s: Pointer to wpa_supplicant data @@ -107,6 +108,7 @@ static int wpas_reenabled_network_time(struct wpa_supplicant *wpa_s) return res; } +#endif /* CONFIG_NO_SCAN_PROCESSING */ void wpas_network_reenabled(void *eloop_ctx, void *timeout_ctx) @@ -279,6 +281,11 @@ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s) wpa_supplicant_ap_deinit(wpa_s); #endif /* CONFIG_AP */ +#ifdef CONFIG_HS20 + /* Clear possibly configured frame filters */ + wpa_drv_configure_frame_filters(wpa_s, 0); +#endif /* CONFIG_HS20 */ + if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) return; @@ -565,11 +572,36 @@ static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s, break; } #endif /* CONFIG_IEEE80211W */ + if ((ie.capabilities & WPA_CAPABILITY_MFPR) && + wpas_get_ssid_pmf(wpa_s, ssid) == + NO_MGMT_FRAME_PROTECTION) { + wpa_dbg(wpa_s, MSG_DEBUG, + " skip RSN IE - no mgmt frame protection enabled but AP requires it"); + break; + } +#ifdef CONFIG_MBO + if (!(ie.capabilities & WPA_CAPABILITY_MFPC) && + wpas_mbo_get_bss_attr(bss, MBO_ATTR_ID_AP_CAPA_IND) && + wpas_get_ssid_pmf(wpa_s, ssid) != + NO_MGMT_FRAME_PROTECTION) { + wpa_dbg(wpa_s, MSG_DEBUG, + " skip RSN IE - no mgmt frame protection enabled on MBO AP"); + break; + } +#endif /* CONFIG_MBO */ wpa_dbg(wpa_s, MSG_DEBUG, " selected based on RSN IE"); return 1; } +#ifdef CONFIG_IEEE80211W + if (wpas_get_ssid_pmf(wpa_s, ssid) == MGMT_FRAME_PROTECTION_REQUIRED) { + wpa_dbg(wpa_s, MSG_DEBUG, + " skip - MFP Required but network not MFP Capable"); + return 0; + } +#endif /* CONFIG_IEEE80211W */ + wpa_ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); while ((ssid->proto & WPA_PROTO_WPA) && wpa_ie) { proto_match++; @@ -807,10 +839,10 @@ static int addr_in_list(const u8 *addr, const u8 *list, size_t num) } -static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, - int i, struct wpa_bss *bss, - struct wpa_ssid *group, - int only_first_ssid) +struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, + int i, struct wpa_bss *bss, + struct wpa_ssid *group, + int only_first_ssid) { u8 wpa_ie_len, rsn_ie_len; int wpa; @@ -818,6 +850,9 @@ static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, const u8 *ie; struct wpa_ssid *ssid; int osen; +#ifdef CONFIG_MBO + const u8 *assoc_disallow; +#endif /* CONFIG_MBO */ ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); wpa_ie_len = ie ? ie[1] : 0; @@ -979,8 +1014,16 @@ static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, continue; } - if (!bss_is_ess(bss)) { - wpa_dbg(wpa_s, MSG_DEBUG, " skip - not ESS network"); + if (ssid->mode != IEEE80211_MODE_MESH && !bss_is_ess(bss) && + !bss_is_pbss(bss)) { + wpa_dbg(wpa_s, MSG_DEBUG, + " skip - not ESS, PBSS, or MBSS"); + continue; + } + + if (ssid->pbss != 2 && ssid->pbss != bss_is_pbss(bss)) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - PBSS mismatch (ssid %d bss %d)", + ssid->pbss, bss_is_pbss(bss)); continue; } @@ -990,6 +1033,14 @@ static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, continue; } +#ifdef CONFIG_MESH + if (ssid->mode == IEEE80211_MODE_MESH && ssid->frequency > 0 && + ssid->frequency != bss->freq) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - frequency not allowed (mesh)"); + continue; + } +#endif /* CONFIG_MESH */ + if (!rate_match(wpa_s, bss)) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - rate sets do " "not match"); @@ -1049,6 +1100,29 @@ static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, (unsigned int) diff.usec); continue; } +#ifdef CONFIG_MBO +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_s->ignore_assoc_disallow) + goto skip_assoc_disallow; +#endif /* CONFIG_TESTING_OPTIONS */ + assoc_disallow = wpas_mbo_get_bss_attr( + bss, MBO_ATTR_ID_ASSOC_DISALLOW); + if (assoc_disallow && assoc_disallow[1] >= 1) { + wpa_dbg(wpa_s, MSG_DEBUG, + " skip - MBO association disallowed (reason %u)", + assoc_disallow[2]); + continue; + } + + if (wpa_is_bss_tmp_disallowed(wpa_s, bss->bssid)) { + wpa_dbg(wpa_s, MSG_DEBUG, + " skip - MBO retry delay has not passed yet"); + continue; + } +#ifdef CONFIG_TESTING_OPTIONS + skip_assoc_disallow: +#endif /* CONFIG_TESTING_OPTIONS */ +#endif /* CONFIG_MBO */ /* Matching configuration found */ return ssid; @@ -1302,6 +1376,7 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s, struct wpa_bss *current_bss = NULL; #ifndef CONFIG_NO_ROAMING int min_diff; + int to_5ghz; #endif /* CONFIG_NO_ROAMING */ if (wpa_s->reassociate) @@ -1357,7 +1432,10 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s, return 1; } - if (current_bss->level < 0 && current_bss->level > selected->level) { + to_5ghz = selected->freq > 4000 && current_bss->freq < 4000; + + if (current_bss->level < 0 && + current_bss->level > selected->level + to_5ghz * 2) { wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - Current BSS has better " "signal level"); return 0; @@ -1376,6 +1454,13 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s, else min_diff = 5; } + if (to_5ghz) { + /* Make it easier to move to 5 GHz band */ + if (min_diff > 2) + min_diff -= 2; + else + min_diff = 0; + } if (abs(current_bss->level - selected->level) < min_diff) { wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - too small difference " "in signal level"); @@ -1418,6 +1503,8 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, return -1; if (!own_request) return -1; + if (data && data->scan_info.external_scan) + return -1; wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results - try " "scanning again"); wpa_supplicant_req_new_scan(wpa_s, 1, 0); @@ -1442,7 +1529,7 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, #endif /* CONFIG_NO_RANDOM_POOL */ if (own_request && wpa_s->scan_res_handler && - (wpa_s->own_scan_running || !wpa_s->radio->external_scan_running)) { + !(data && data->scan_info.external_scan)) { void (*scan_res_handler)(struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res); @@ -1463,9 +1550,11 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, } wpa_dbg(wpa_s, MSG_DEBUG, "New scan results available (own=%u ext=%u)", - wpa_s->own_scan_running, wpa_s->radio->external_scan_running); + wpa_s->own_scan_running, + data ? data->scan_info.external_scan : 0); if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && - wpa_s->manual_scan_use_id && wpa_s->own_scan_running) { + wpa_s->manual_scan_use_id && wpa_s->own_scan_running && + own_request && !(data && data->scan_info.external_scan)) { wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS "id=%u", wpa_s->manual_scan_id); wpa_s->manual_scan_use_id = 0; @@ -1476,7 +1565,7 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, wpas_notify_scan_done(wpa_s, 1); - if (!wpa_s->own_scan_running && wpa_s->radio->external_scan_running) { + if (data && data->scan_info.external_scan) { wpa_dbg(wpa_s, MSG_DEBUG, "Do not use results from externally requested scan operation for network selection"); wpa_scan_results_free(scan_res); return 0; @@ -1505,9 +1594,13 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, wpas_wps_update_ap_info(wpa_s, scan_res); + if (wpa_s->wpa_state >= WPA_AUTHENTICATING && + wpa_s->wpa_state < WPA_COMPLETED) + goto scan_work_done; + wpa_scan_results_free(scan_res); - if (wpa_s->scan_work) { + if (own_request && wpa_s->scan_work) { struct wpa_radio_work *work = wpa_s->scan_work; wpa_s->scan_work = NULL; radio_work_done(work); @@ -1517,7 +1610,7 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, scan_work_done: wpa_scan_results_free(scan_res); - if (wpa_s->scan_work) { + if (own_request && wpa_s->scan_work) { struct wpa_radio_work *work = wpa_s->scan_work; wpa_s->scan_work = NULL; radio_work_done(work); @@ -1548,6 +1641,14 @@ static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s, selected = wpa_supplicant_pick_network(wpa_s, &ssid); +#ifdef CONFIG_MESH + if (wpa_s->ifmsh) { + wpa_msg(wpa_s, MSG_INFO, + "Avoiding join because we already joined a mesh group"); + return 0; + } +#endif /* CONFIG_MESH */ + if (selected) { int skip; skip = !wpa_supplicant_need_to_roam(wpa_s, selected, ssid); @@ -1557,6 +1658,13 @@ static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s, return 0; } + if (ssid != wpa_s->current_ssid && + wpa_s->wpa_state >= WPA_AUTHENTICATING) { + wpa_s->own_disconnect_req = 1; + wpa_supplicant_deauthenticate( + wpa_s, WLAN_REASON_DEAUTH_LEAVING); + } + if (wpa_supplicant_connect(wpa_s, selected, ssid) < 0) { wpa_dbg(wpa_s, MSG_DEBUG, "Connect failed"); return -1; @@ -1569,13 +1677,6 @@ static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s, */ return 1; } else { -#ifdef CONFIG_MESH - if (wpa_s->ifmsh) { - wpa_msg(wpa_s, MSG_INFO, - "Avoiding join because we already joined a mesh group"); - return 0; - } -#endif /* CONFIG_MESH */ wpa_dbg(wpa_s, MSG_DEBUG, "No suitable network found"); ssid = wpa_supplicant_pick_new_network(wpa_s); if (ssid) { @@ -1831,6 +1932,50 @@ static void interworking_process_assoc_resp(struct wpa_supplicant *wpa_s, #endif /* CONFIG_INTERWORKING */ +#ifdef CONFIG_FST +static int wpas_fst_update_mbie(struct wpa_supplicant *wpa_s, + const u8 *ie, size_t ie_len) +{ + struct mb_ies_info mb_ies; + + if (!ie || !ie_len || !wpa_s->fst) + return -ENOENT; + + os_memset(&mb_ies, 0, sizeof(mb_ies)); + + while (ie_len >= 2 && mb_ies.nof_ies < MAX_NOF_MB_IES_SUPPORTED) { + size_t len; + + len = 2 + ie[1]; + if (len > ie_len) { + wpa_hexdump(MSG_DEBUG, "FST: Truncated IE found", + ie, ie_len); + break; + } + + if (ie[0] == WLAN_EID_MULTI_BAND) { + wpa_printf(MSG_DEBUG, "MB IE of %u bytes found", + (unsigned int) len); + mb_ies.ies[mb_ies.nof_ies].ie = ie + 2; + mb_ies.ies[mb_ies.nof_ies].ie_len = len - 2; + mb_ies.nof_ies++; + } + + ie_len -= len; + ie += len; + } + + if (mb_ies.nof_ies > 0) { + wpabuf_free(wpa_s->received_mb_ies); + wpa_s->received_mb_ies = mb_ies_by_info(&mb_ies); + return 0; + } + + return -ENOENT; +} +#endif /* CONFIG_FST */ + + static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { @@ -1881,6 +2026,8 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, } if ((p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 6 && (os_memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0)) || + (p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 4 && + (os_memcmp(&p[2], "\x50\x6F\x9A\x12", 4) == 0)) || (p[0] == WLAN_EID_RSN && p[1] >= 2)) { if (wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, p, len)) break; @@ -2013,19 +2160,6 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, if (wpa_found || rsn_found) wpa_s->ap_ies_from_associnfo = 1; -#ifdef CONFIG_FST - wpabuf_free(wpa_s->received_mb_ies); - wpa_s->received_mb_ies = NULL; - if (wpa_s->fst) { - struct mb_ies_info mb_ies; - - wpa_printf(MSG_DEBUG, "Looking for MB IE"); - if (!mb_ies_info_by_ies(&mb_ies, data->assoc_info.resp_ies, - data->assoc_info.resp_ies_len)) - wpa_s->received_mb_ies = mb_ies_by_info(&mb_ies); - } -#endif /* CONFIG_FST */ - if (wpa_s->assoc_freq && data->assoc_info.freq && wpa_s->assoc_freq != data->assoc_info.freq) { wpa_printf(MSG_DEBUG, "Operating frequency changed from " @@ -2064,11 +2198,50 @@ static int wpa_supplicant_assoc_update_ie(struct wpa_supplicant *wpa_s) } +static void wpas_fst_update_mb_assoc(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ +#ifdef CONFIG_FST + struct assoc_info *ai = data ? &data->assoc_info : NULL; + struct wpa_bss *bss = wpa_s->current_bss; + const u8 *ieprb, *iebcn; + + wpabuf_free(wpa_s->received_mb_ies); + wpa_s->received_mb_ies = NULL; + + if (ai && + !wpas_fst_update_mbie(wpa_s, ai->resp_ies, ai->resp_ies_len)) { + wpa_printf(MSG_DEBUG, + "FST: MB IEs updated from Association Response frame"); + return; + } + + if (ai && + !wpas_fst_update_mbie(wpa_s, ai->beacon_ies, ai->beacon_ies_len)) { + wpa_printf(MSG_DEBUG, + "FST: MB IEs updated from association event Beacon IEs"); + return; + } + + if (!bss) + return; + + ieprb = (const u8 *) (bss + 1); + iebcn = ieprb + bss->ie_len; + + if (!wpas_fst_update_mbie(wpa_s, ieprb, bss->ie_len)) + wpa_printf(MSG_DEBUG, "FST: MB IEs updated from bss IE"); + else if (!wpas_fst_update_mbie(wpa_s, iebcn, bss->beacon_ie_len)) + wpa_printf(MSG_DEBUG, "FST: MB IEs updated from bss beacon IE"); +#endif /* CONFIG_FST */ +} + + static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { u8 bssid[ETH_ALEN]; - int ft_completed; + int ft_completed, already_authorized; int new_bss = 0; #ifdef CONFIG_AP @@ -2124,6 +2297,8 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, "WPA/RSN IEs not updated"); } + wpas_fst_update_mb_assoc(wpa_s, data); + #ifdef CONFIG_SME os_memcpy(wpa_s->sme.prev_bssid, bssid, ETH_ALEN); wpa_s->sme.prev_bssid_set = 1; @@ -2142,6 +2317,8 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, if (wpa_s->l2) l2_packet_notify_auth_start(wpa_s->l2); + already_authorized = data && data->assoc_info.authorized; + /* * Set portEnabled first to FALSE in order to get EAP state machine out * of the SUCCESS state and eapSuccess cleared. Without this, EAPOL PAE @@ -2150,11 +2327,12 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, * AUTHENTICATED without ever giving chance to EAP state machine to * reset the state. */ - if (!ft_completed) { + if (!ft_completed && !already_authorized) { eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE); eapol_sm_notify_portValid(wpa_s->eapol, FALSE); } - if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) || ft_completed) + if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) || ft_completed || + already_authorized) eapol_sm_notify_eap_success(wpa_s->eapol, FALSE); /* 802.1X::portControl = Auto */ eapol_sm_notify_portEnabled(wpa_s->eapol, TRUE); @@ -2246,7 +2424,7 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, wpa_s->key_mgmt != WPA_KEY_MGMT_NONE && wpa_s->key_mgmt != WPA_KEY_MGMT_WPA_NONE && wpa_s->ibss_rsn == NULL) { - wpa_s->ibss_rsn = ibss_rsn_init(wpa_s); + wpa_s->ibss_rsn = ibss_rsn_init(wpa_s, wpa_s->current_ssid); if (!wpa_s->ibss_rsn) { wpa_msg(wpa_s, MSG_INFO, "Failed to init IBSS RSN"); wpa_supplicant_deauthenticate( @@ -2340,6 +2518,7 @@ static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s, struct wpa_bss *fast_reconnect = NULL; struct wpa_ssid *fast_reconnect_ssid = NULL; struct wpa_ssid *last_ssid; + struct wpa_bss *curr = NULL; authenticating = wpa_s->wpa_state == WPA_AUTHENTICATING; os_memcpy(prev_pending_bssid, wpa_s->pending_bssid, ETH_ALEN); @@ -2355,6 +2534,19 @@ static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s, return; } + if (!wpa_s->disconnected && wpa_s->wpa_state >= WPA_AUTHENTICATING && + reason_code == WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY && + locally_generated) + /* + * Remove the inactive AP (which is probably out of range) from + * the BSS list after marking disassociation. In particular + * mac80211-based drivers use the + * WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY reason code in + * locally generated disconnection events for cases where the + * AP does not reply anymore. + */ + curr = wpa_s->current_bss; + if (could_be_psk_mismatch(wpa_s, reason_code, locally_generated)) { wpa_msg(wpa_s, MSG_INFO, "WPA: 4-Way Handshake failed - " "pre-shared key may be incorrect"); @@ -2365,7 +2557,8 @@ static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s, if (!wpa_s->disconnected && (!wpa_s->auto_reconnect_disabled || wpa_s->key_mgmt == WPA_KEY_MGMT_WPS || - wpas_wps_searching(wpa_s))) { + wpas_wps_searching(wpa_s) || + wpas_wps_reenable_networks_pending(wpa_s))) { wpa_dbg(wpa_s, MSG_DEBUG, "Auto connect enabled: try to " "reconnect (wps=%d/%d wpa_state=%d)", wpa_s->key_mgmt == WPA_KEY_MGMT_WPS, @@ -2415,6 +2608,9 @@ static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s, last_ssid = wpa_s->current_ssid; wpa_supplicant_mark_disassoc(wpa_s); + if (curr) + wpa_bss_remove(wpa_s, curr, "Connection to AP lost"); + if (authenticating && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)) { sme_disassoc_while_authenticating(wpa_s, prev_pending_bssid); wpa_s->current_ssid = last_ssid; @@ -2425,7 +2621,8 @@ static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s, !disallowed_bssid(wpa_s, fast_reconnect->bssid) && !disallowed_ssid(wpa_s, fast_reconnect->ssid, fast_reconnect->ssid_len) && - !wpas_temp_disabled(wpa_s, fast_reconnect_ssid)) { + !wpas_temp_disabled(wpa_s, fast_reconnect_ssid) && + !wpa_is_bss_tmp_disallowed(wpa_s, fast_reconnect->bssid)) { #ifndef CONFIG_NO_SCAN_PROCESSING wpa_dbg(wpa_s, MSG_DEBUG, "Try to reconnect to the same BSS"); if (wpa_supplicant_connect(wpa_s, fast_reconnect, @@ -2623,6 +2820,13 @@ wpa_supplicant_event_interface_status(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_P2P */ +#ifdef CONFIG_MATCH_IFACE + if (wpa_s->matched) { + wpa_supplicant_remove_iface(wpa_s->global, wpa_s, 0); + break; + } +#endif /* CONFIG_MATCH_IFACE */ + #ifdef CONFIG_TERMINATE_ONLASTIF /* check if last interface */ if (!any_interfaces(wpa_s->global->ifaces)) @@ -3008,7 +3212,16 @@ static void wpa_supplicant_update_channel_list( { struct wpa_supplicant *ifs; - wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_REGDOM_CHANGE "init=%s type=%s%s%s", + /* + * To allow backwards compatibility with higher level layers that + * assumed the REGDOM_CHANGE event is sent over the initially added + * interface. Find the highest parent of this interface and use it to + * send the event. + */ + for (ifs = wpa_s; ifs->parent && ifs != ifs->parent; ifs = ifs->parent) + ; + + wpa_msg(ifs, MSG_INFO, WPA_EVENT_REGDOM_CHANGE "init=%s type=%s%s%s", reg_init_str(info->initiator), reg_type_str(info->type), info->alpha2[0] ? " alpha2=" : "", info->alpha2[0] ? info->alpha2 : ""); @@ -3023,14 +3236,16 @@ static void wpa_supplicant_update_channel_list( free_hw_features(ifs); ifs->hw.modes = wpa_drv_get_hw_feature_data( ifs, &ifs->hw.num_modes, &ifs->hw.flags); - } - /* Restart sched_scan with updated channel list */ - if (wpa_s->sched_scanning) { - wpa_dbg(wpa_s, MSG_DEBUG, - "Channel list changed restart sched scan."); - wpa_supplicant_cancel_sched_scan(wpa_s); - wpa_supplicant_req_scan(wpa_s, 0, 0); + /* Restart PNO/sched_scan with updated channel list */ + if (ifs->pno) { + wpas_stop_pno(ifs); + wpas_start_pno(ifs); + } else if (ifs->sched_scanning && !ifs->pno_sched_pending) { + wpa_dbg(ifs, MSG_DEBUG, + "Channel list changed - restart sched_scan"); + wpas_scan_restart_sched_scan(ifs); + } } wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_DRIVER); @@ -3120,6 +3335,14 @@ static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_INTERWORKING */ + if (category == WLAN_ACTION_RADIO_MEASUREMENT && + payload[0] == WLAN_RRM_RADIO_MEASUREMENT_REQUEST) { + wpas_rrm_handle_radio_measurement_request(wpa_s, mgmt->sa, + payload + 1, + plen - 1); + return; + } + if (category == WLAN_ACTION_RADIO_MEASUREMENT && payload[0] == WLAN_RRM_NEIGHBOR_REPORT_RESPONSE) { wpas_rrm_process_neighbor_rep(wpa_s, payload + 1, plen - 1); @@ -3210,6 +3433,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED && event != EVENT_INTERFACE_ENABLED && event != EVENT_INTERFACE_STATUS && + event != EVENT_SCAN_RESULTS && event != EVENT_SCHED_SCAN_STOPPED) { wpa_dbg(wpa_s, MSG_DEBUG, "Ignore event %s (%d) while interface is disabled", @@ -3238,18 +3462,43 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, switch (event) { case EVENT_AUTH: +#ifdef CONFIG_FST + if (!wpas_fst_update_mbie(wpa_s, data->auth.ies, + data->auth.ies_len)) + wpa_printf(MSG_DEBUG, + "FST: MB IEs updated from auth IE"); +#endif /* CONFIG_FST */ sme_event_auth(wpa_s, data); break; case EVENT_ASSOC: +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_s->ignore_auth_resp) { + wpa_printf(MSG_INFO, + "EVENT_ASSOC - ignore_auth_resp active!"); + break; + } +#endif /* CONFIG_TESTING_OPTIONS */ wpa_supplicant_event_assoc(wpa_s, data); if (data && data->assoc_info.authorized) wpa_supplicant_event_assoc_auth(wpa_s, data); + if (data) { + wpa_msg(wpa_s, MSG_INFO, + WPA_EVENT_SUBNET_STATUS_UPDATE "status=%u", + data->assoc_info.subnet_status); + } break; case EVENT_DISASSOC: wpas_event_disassoc(wpa_s, data ? &data->disassoc_info : NULL); break; case EVENT_DEAUTH: +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_s->ignore_auth_resp) { + wpa_printf(MSG_INFO, + "EVENT_DEAUTH - ignore_auth_resp active!"); + break; + } +#endif /* CONFIG_TESTING_OPTIONS */ wpas_event_deauth(wpa_s, data ? &data->deauth_info : NULL); break; @@ -3258,10 +3507,11 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, break; #ifndef CONFIG_NO_SCAN_PROCESSING case EVENT_SCAN_STARTED: - os_get_reltime(&wpa_s->scan_start_time); - if (wpa_s->own_scan_requested) { + if (wpa_s->own_scan_requested || + (data && !data->scan_info.external_scan)) { struct os_reltime diff; + os_get_reltime(&wpa_s->scan_start_time); os_reltime_sub(&wpa_s->scan_start_time, &wpa_s->scan_trigger_time, &diff); wpa_dbg(wpa_s, MSG_DEBUG, "Own scan request started a scan in %ld.%06ld seconds", @@ -3284,7 +3534,16 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, } break; case EVENT_SCAN_RESULTS: - if (os_reltime_initialized(&wpa_s->scan_start_time)) { + if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { + wpa_s->scan_res_handler = NULL; + wpa_s->own_scan_running = 0; + wpa_s->radio->external_scan_running = 0; + wpa_s->last_scan_req = NORMAL_SCAN_REQ; + break; + } + + if (!(data && data->scan_info.external_scan) && + os_reltime_initialized(&wpa_s->scan_start_time)) { struct os_reltime now, diff; os_get_reltime(&now); os_reltime_sub(&now, &wpa_s->scan_start_time, &diff); @@ -3295,8 +3554,10 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, } if (wpa_supplicant_event_scan_results(wpa_s, data)) break; /* interface may have been removed */ - wpa_s->own_scan_running = 0; - wpa_s->radio->external_scan_running = 0; + if (!(data && data->scan_info.external_scan)) + wpa_s->own_scan_running = 0; + if (data && data->scan_info.nl_scan_event) + wpa_s->radio->external_scan_running = 0; radio_work_check_next(wpa_s); break; #endif /* CONFIG_NO_SCAN_PROCESSING */ @@ -3337,13 +3598,17 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, case EVENT_ASSOC_REJECT: if (data->assoc_reject.bssid) wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT - "bssid=" MACSTR " status_code=%u", + "bssid=" MACSTR " status_code=%u%s", MAC2STR(data->assoc_reject.bssid), - data->assoc_reject.status_code); + data->assoc_reject.status_code, + data->assoc_reject.timed_out ? " timeout" : ""); else wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT - "status_code=%u", - data->assoc_reject.status_code); + "status_code=%u%s", + data->assoc_reject.status_code, + data->assoc_reject.timed_out ? " timeout" : ""); + wpa_s->assoc_status_code = data->assoc_reject.status_code; + wpas_notify_assoc_status_code(wpa_s); if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) sme_event_assoc_reject(wpa_s, data); else { @@ -3399,17 +3664,20 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, #endif /* CONFIG_AP */ #ifdef CONFIG_OFFCHANNEL wpa_dbg(wpa_s, MSG_DEBUG, "EVENT_TX_STATUS pending_dst=" - MACSTR, MAC2STR(wpa_s->parent->pending_action_dst)); + MACSTR, MAC2STR(wpa_s->p2pdev->pending_action_dst)); /* * Catch TX status events for Action frames we sent via group - * interface in GO mode. + * interface in GO mode, or via standalone AP interface. + * Note, wpa_s->p2pdev will be the same as wpa_s->parent, + * except when the primary interface is used as a GO interface + * (for drivers which do not have group interface concurrency) */ if (data->tx_status.type == WLAN_FC_TYPE_MGMT && data->tx_status.stype == WLAN_FC_STYPE_ACTION && - os_memcmp(wpa_s->parent->pending_action_dst, + os_memcmp(wpa_s->p2pdev->pending_action_dst, data->tx_status.dst, ETH_ALEN) == 0) { offchannel_send_action_tx_status( - wpa_s->parent, data->tx_status.dst, + wpa_s->p2pdev, data->tx_status.dst, data->tx_status.data, data->tx_status.data_len, data->tx_status.ack ? @@ -3452,20 +3720,34 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, data->rx_from_unknown.wds); break; case EVENT_CH_SWITCH: - if (!data) - break; - if (!wpa_s->ap_iface) { - wpa_dbg(wpa_s, MSG_DEBUG, "AP: Ignore channel switch " - "event in non-AP mode"); + if (!data || !wpa_s->current_ssid) break; + + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_CHANNEL_SWITCH + "freq=%d ht_enabled=%d ch_offset=%d ch_width=%s cf1=%d cf2=%d", + data->ch_switch.freq, + data->ch_switch.ht_enabled, + data->ch_switch.ch_offset, + channel_width_to_string(data->ch_switch.ch_width), + data->ch_switch.cf1, + data->ch_switch.cf2); + + wpa_s->assoc_freq = data->ch_switch.freq; + wpa_s->current_ssid->frequency = data->ch_switch.freq; + + if (wpa_s->current_ssid->mode == WPAS_MODE_AP || + wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO || + wpa_s->current_ssid->mode == + WPAS_MODE_P2P_GROUP_FORMATION) { + wpas_ap_ch_switch(wpa_s, data->ch_switch.freq, + data->ch_switch.ht_enabled, + data->ch_switch.ch_offset, + data->ch_switch.ch_width, + data->ch_switch.cf1, + data->ch_switch.cf2); } - wpas_ap_ch_switch(wpa_s, data->ch_switch.freq, - data->ch_switch.ht_enabled, - data->ch_switch.ch_offset, - data->ch_switch.ch_width, - data->ch_switch.cf1, - data->ch_switch.cf2); + wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_CS); break; #ifdef NEED_AP_MLME case EVENT_DFS_RADAR_DETECTED: @@ -3522,12 +3804,14 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, #endif /* CONFIG_AP */ #ifdef CONFIG_P2P if (stype == WLAN_FC_STYPE_PROBE_REQ && - data->rx_mgmt.frame_len > 24) { + data->rx_mgmt.frame_len > IEEE80211_HDRLEN) { const u8 *src = mgmt->sa; - const u8 *ie = mgmt->u.probe_req.variable; - size_t ie_len = data->rx_mgmt.frame_len - - (mgmt->u.probe_req.variable - - data->rx_mgmt.frame); + const u8 *ie; + size_t ie_len; + + ie = data->rx_mgmt.frame + IEEE80211_HDRLEN; + ie_len = data->rx_mgmt.frame_len - + IEEE80211_HDRLEN; wpas_p2p_probe_req_rx( wpa_s, src, mgmt->da, mgmt->bssid, ie, ie_len, @@ -3567,11 +3851,12 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, } if (stype == WLAN_FC_STYPE_PROBE_REQ && - data->rx_mgmt.frame_len > 24) { - const u8 *ie = mgmt->u.probe_req.variable; - size_t ie_len = data->rx_mgmt.frame_len - - (mgmt->u.probe_req.variable - - data->rx_mgmt.frame); + data->rx_mgmt.frame_len > IEEE80211_HDRLEN) { + const u8 *ie; + size_t ie_len; + + ie = data->rx_mgmt.frame + IEEE80211_HDRLEN; + ie_len = data->rx_mgmt.frame_len - IEEE80211_HDRLEN; wpas_notify_preq(wpa_s, mgmt->sa, mgmt->da, mgmt->bssid, ie, ie_len, @@ -3714,6 +3999,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, wpa_s, WLAN_REASON_DEAUTH_LEAVING, 1); } wpa_supplicant_mark_disassoc(wpa_s); + wpa_bss_flush(wpa_s); radio_remove_works(wpa_s, NULL, 0); wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED); @@ -3772,14 +4058,27 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, data->driver_gtk_rekey.replay_ctr); break; case EVENT_SCHED_SCAN_STOPPED: - wpa_s->pno = 0; wpa_s->sched_scanning = 0; - resched = wpa_s->scanning; + resched = wpa_s->scanning && wpas_scan_scheduled(wpa_s); wpa_supplicant_notify_scanning(wpa_s, 0); if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) break; + /* + * If the driver stopped scanning without being requested to, + * request a new scan to continue scanning for networks. + */ + if (!wpa_s->sched_scan_stop_req && + wpa_s->wpa_state == WPA_SCANNING) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Restart scanning after unexpected sched_scan stop event"); + wpa_supplicant_req_scan(wpa_s, 1, 0); + break; + } + + wpa_s->sched_scan_stop_req = 0; + /* * Start a new sched scan to continue searching for more SSIDs * either if timed out or PNO schedule scan is pending. @@ -3821,8 +4120,72 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, data->mesh_peer.ie_len); #endif /* CONFIG_MESH */ break; + case EVENT_SURVEY: +#ifdef CONFIG_AP + if (!wpa_s->ap_iface) + break; + hostapd_event_get_survey(wpa_s->ap_iface, + &data->survey_results); +#endif /* CONFIG_AP */ + break; + case EVENT_ACS_CHANNEL_SELECTED: +#ifdef CONFIG_ACS + if (!wpa_s->ap_iface) + break; + hostapd_acs_channel_selected(wpa_s->ap_iface->bss[0], + &data->acs_selected_channels); +#endif /* CONFIG_ACS */ + break; + case EVENT_P2P_LO_STOP: +#ifdef CONFIG_P2P + wpa_s->p2p_lo_started = 0; + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_LISTEN_OFFLOAD_STOP + P2P_LISTEN_OFFLOAD_STOP_REASON "reason=%d", + data->p2p_lo_stop.reason_code); +#endif /* CONFIG_P2P */ + break; default: wpa_msg(wpa_s, MSG_INFO, "Unknown event %d", event); break; } } + + +void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event, + union wpa_event_data *data) +{ + struct wpa_supplicant *wpa_s; + + if (event != EVENT_INTERFACE_STATUS) + return; + + wpa_s = wpa_supplicant_get_iface(ctx, data->interface_status.ifname); + if (wpa_s && wpa_s->driver->get_ifindex) { + unsigned int ifindex; + + ifindex = wpa_s->driver->get_ifindex(wpa_s->drv_priv); + if (ifindex != data->interface_status.ifindex) { + wpa_dbg(wpa_s, MSG_DEBUG, + "interface status ifindex %d mismatch (%d)", + ifindex, data->interface_status.ifindex); + return; + } + } +#ifdef CONFIG_MATCH_IFACE + else if (data->interface_status.ievent == EVENT_INTERFACE_ADDED) { + struct wpa_interface *wpa_i; + + wpa_i = wpa_supplicant_match_iface( + ctx, data->interface_status.ifname); + if (!wpa_i) + return; + wpa_s = wpa_supplicant_add_iface(ctx, wpa_i, NULL); + os_free(wpa_i); + if (wpa_s) + wpa_s->matched = 1; + } +#endif /* CONFIG_MATCH_IFACE */ + + if (wpa_s) + wpa_supplicant_event(wpa_s, event, data); +} diff --git a/contrib/wpa/wpa_supplicant/gas_query.c b/contrib/wpa/wpa_supplicant/gas_query.c index 10ecce7b4d3d..691de0345d13 100644 --- a/contrib/wpa/wpa_supplicant/gas_query.c +++ b/contrib/wpa/wpa_supplicant/gas_query.c @@ -17,6 +17,7 @@ #include "common/wpa_ctrl.h" #include "rsn_supp/wpa.h" #include "wpa_supplicant_i.h" +#include "config.h" #include "driver_i.h" #include "offchannel.h" #include "gas_query.h" @@ -25,6 +26,9 @@ /** GAS query timeout in seconds */ #define GAS_QUERY_TIMEOUT_PERIOD 2 +/* GAS query wait-time / duration in ms */ +#define GAS_QUERY_WAIT_TIME_INITIAL 1000 +#define GAS_QUERY_WAIT_TIME_COMEBACK 150 /** * struct gas_query_pending - Pending GAS query @@ -37,6 +41,7 @@ struct gas_query_pending { u8 next_frag_id; unsigned int wait_comeback:1; unsigned int offchannel_tx_started:1; + unsigned int retry:1; int freq; u16 status_code; struct wpabuf *req; @@ -63,6 +68,10 @@ struct gas_query { static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx); static void gas_query_timeout(void *eloop_data, void *user_ctx); +static void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx); +static void gas_query_tx_initial_req(struct gas_query *gas, + struct gas_query_pending *query); +static int gas_query_new_dialog_token(struct gas_query *gas, const u8 *dst); static int ms_from_time(struct os_reltime *last) @@ -108,8 +117,6 @@ static const char * gas_result_txt(enum gas_query_result result) return "PEER_ERROR"; case GAS_QUERY_INTERNAL_ERROR: return "INTERNAL_ERROR"; - case GAS_QUERY_CANCELLED: - return "CANCELLED"; case GAS_QUERY_DELETED_AT_DEINIT: return "DELETED_AT_DEINIT"; } @@ -151,6 +158,7 @@ static void gas_query_done(struct gas_query *gas, offchannel_send_action_done(gas->wpa_s); eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query); eloop_cancel_timeout(gas_query_timeout, gas, query); + eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query); dl_list_del(&query->list); query->cb(query->ctx, query->addr, query->dialog_token, result, query->adv_proto, query->resp, query->status_code); @@ -235,6 +243,13 @@ static void gas_query_tx_status(struct wpa_supplicant *wpa_s, eloop_cancel_timeout(gas_query_timeout, gas, query); eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0, gas_query_timeout, gas, query); + if (query->wait_comeback && !query->retry) { + eloop_cancel_timeout(gas_query_rx_comeback_timeout, + gas, query); + eloop_register_timeout( + 0, (GAS_QUERY_WAIT_TIME_COMEBACK + 10) * 1000, + gas_query_rx_comeback_timeout, gas, query); + } } if (result == OFFCHANNEL_SEND_ACTION_FAILED) { eloop_cancel_timeout(gas_query_timeout, gas, query); @@ -254,10 +269,13 @@ static int pmf_in_use(struct wpa_supplicant *wpa_s, const u8 *addr) static int gas_query_tx(struct gas_query *gas, struct gas_query_pending *query, - struct wpabuf *req) + struct wpabuf *req, unsigned int wait_time) { - unsigned int wait_time; int res, prot = pmf_in_use(gas->wpa_s, query->addr); + const u8 *bssid; + const u8 wildcard_bssid[ETH_ALEN] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + }; wpa_printf(MSG_DEBUG, "GAS: Send action frame to " MACSTR " len=%u " "freq=%d prot=%d", MAC2STR(query->addr), @@ -267,12 +285,18 @@ static int gas_query_tx(struct gas_query *gas, struct gas_query_pending *query, *categ = WLAN_ACTION_PROTECTED_DUAL; } os_get_reltime(&query->last_oper); - wait_time = 1000; if (gas->wpa_s->max_remain_on_chan && wait_time > gas->wpa_s->max_remain_on_chan) wait_time = gas->wpa_s->max_remain_on_chan; + if (!gas->wpa_s->conf->gas_address3 || + (gas->wpa_s->current_ssid && + gas->wpa_s->wpa_state >= WPA_ASSOCIATED && + os_memcmp(query->addr, gas->wpa_s->bssid, ETH_ALEN) == 0)) + bssid = query->addr; + else + bssid = wildcard_bssid; res = offchannel_send_action(gas->wpa_s, query->freq, query->addr, - gas->wpa_s->own_addr, query->addr, + gas->wpa_s->own_addr, bssid, wpabuf_head(req), wpabuf_len(req), wait_time, gas_query_tx_status, 0); if (res == 0) @@ -285,6 +309,7 @@ static void gas_query_tx_comeback_req(struct gas_query *gas, struct gas_query_pending *query) { struct wpabuf *req; + unsigned int wait_time; req = gas_build_comeback_req(query->dialog_token); if (req == NULL) { @@ -292,7 +317,10 @@ static void gas_query_tx_comeback_req(struct gas_query *gas, return; } - if (gas_query_tx(gas, query, req) < 0) { + wait_time = (query->retry || !query->offchannel_tx_started) ? + GAS_QUERY_WAIT_TIME_INITIAL : GAS_QUERY_WAIT_TIME_COMEBACK; + + if (gas_query_tx(gas, query, req, wait_time) < 0) { wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to " MACSTR, MAC2STR(query->addr)); gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR); @@ -302,6 +330,35 @@ static void gas_query_tx_comeback_req(struct gas_query *gas, } +static void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx) +{ + struct gas_query *gas = eloop_data; + struct gas_query_pending *query = user_ctx; + int dialog_token; + + wpa_printf(MSG_DEBUG, + "GAS: No response to comeback request received (retry=%u)", + query->retry); + if (gas->current != query || query->retry) + return; + dialog_token = gas_query_new_dialog_token(gas, query->addr); + if (dialog_token < 0) + return; + wpa_printf(MSG_DEBUG, + "GAS: Retry GAS query due to comeback response timeout"); + query->retry = 1; + query->dialog_token = dialog_token; + *(wpabuf_mhead_u8(query->req) + 2) = dialog_token; + query->wait_comeback = 0; + query->next_frag_id = 0; + wpabuf_free(query->adv_proto); + query->adv_proto = NULL; + eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query); + eloop_cancel_timeout(gas_query_timeout, gas, query); + gas_query_tx_initial_req(gas, query); +} + + static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx) { struct gas_query *gas = eloop_data; @@ -319,6 +376,11 @@ static void gas_query_tx_comeback_req_delay(struct gas_query *gas, { unsigned int secs, usecs; + if (comeback_delay > 1 && query->offchannel_tx_started) { + offchannel_send_action_done(gas->wpa_s); + query->offchannel_tx_started = 0; + } + secs = (comeback_delay * 1024) / 1000000; usecs = comeback_delay * 1024 - secs * 1000000; wpa_printf(MSG_DEBUG, "GAS: Send comeback request to " MACSTR @@ -371,6 +433,7 @@ static void gas_query_rx_comeback(struct gas_query *gas, "comeback_delay=%u)", MAC2STR(query->addr), query->dialog_token, frag_id, more_frags, comeback_delay); + eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query); if ((size_t) 2 + adv_proto[1] != wpabuf_len(query->adv_proto) || os_memcmp(adv_proto, wpabuf_head(query->adv_proto), @@ -447,8 +510,16 @@ int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa, if (gas == NULL || len < 4) return -1; + pos = data; + action = *pos++; + dialog_token = *pos++; + + if (action != WLAN_PA_GAS_INITIAL_RESP && + action != WLAN_PA_GAS_COMEBACK_RESP) + return -1; /* Not a GAS response */ + prot = categ == WLAN_ACTION_PROTECTED_DUAL; - pmf = pmf_in_use(gas->wpa_s, bssid); + pmf = pmf_in_use(gas->wpa_s, sa); if (prot && !pmf) { wpa_printf(MSG_DEBUG, "GAS: Drop unexpected protected GAS frame when PMF is disabled"); return 0; @@ -458,14 +529,6 @@ int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa, return 0; } - pos = data; - action = *pos++; - dialog_token = *pos++; - - if (action != WLAN_PA_GAS_INITIAL_RESP && - action != WLAN_PA_GAS_COMEBACK_RESP) - return -1; /* Not a GAS response */ - query = gas_query_get_pending(gas, sa, dialog_token); if (query == NULL) { wpa_printf(MSG_DEBUG, "GAS: No pending query found for " MACSTR @@ -620,11 +683,18 @@ static void gas_query_start_cb(struct wpa_radio_work *work, int deinit) } gas->work = work; + gas_query_tx_initial_req(gas, query); +} - if (gas_query_tx(gas, query, query->req) < 0) { + +static void gas_query_tx_initial_req(struct gas_query *gas, + struct gas_query_pending *query) +{ + if (gas_query_tx(gas, query, query->req, + GAS_QUERY_WAIT_TIME_INITIAL) < 0) { wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to " MACSTR, MAC2STR(query->addr)); - gas_query_free(query, 1); + gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR); return; } gas->current = query; @@ -633,7 +703,24 @@ static void gas_query_start_cb(struct wpa_radio_work *work, int deinit) query->dialog_token); eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0, gas_query_timeout, gas, query); +} + +static int gas_query_new_dialog_token(struct gas_query *gas, const u8 *dst) +{ + static int next_start = 0; + int dialog_token; + + for (dialog_token = 0; dialog_token < 256; dialog_token++) { + if (gas_query_dialog_token_available( + gas, dst, (next_start + dialog_token) % 256)) + break; + } + if (dialog_token == 256) + return -1; /* Too many pending queries */ + dialog_token = (next_start + dialog_token) % 256; + next_start = (dialog_token + 1) % 256; + return dialog_token; } @@ -658,20 +745,13 @@ int gas_query_req(struct gas_query *gas, const u8 *dst, int freq, { struct gas_query_pending *query; int dialog_token; - static int next_start = 0; if (wpabuf_len(req) < 3) return -1; - for (dialog_token = 0; dialog_token < 256; dialog_token++) { - if (gas_query_dialog_token_available( - gas, dst, (next_start + dialog_token) % 256)) - break; - } - if (dialog_token == 256) - return -1; /* Too many pending queries */ - dialog_token = (next_start + dialog_token) % 256; - next_start = (dialog_token + 1) % 256; + dialog_token = gas_query_new_dialog_token(gas, dst); + if (dialog_token < 0) + return -1; query = os_zalloc(sizeof(*query)); if (query == NULL) @@ -694,26 +774,10 @@ int gas_query_req(struct gas_query *gas, const u8 *dst, int freq, if (radio_add_work(gas->wpa_s, freq, "gas-query", 0, gas_query_start_cb, query) < 0) { + query->req = NULL; /* caller will free this in error case */ gas_query_free(query, 1); return -1; } return dialog_token; } - - -/** - * gas_query_cancel - Cancel a pending GAS query - * @gas: GAS query data from gas_query_init() - * @dst: Destination MAC address for the query - * @dialog_token: Dialog token from gas_query_req() - */ -void gas_query_cancel(struct gas_query *gas, const u8 *dst, u8 dialog_token) -{ - struct gas_query_pending *query; - - query = gas_query_get_pending(gas, dst, dialog_token); - if (query) - gas_query_done(gas, query, GAS_QUERY_CANCELLED); - -} diff --git a/contrib/wpa/wpa_supplicant/gas_query.h b/contrib/wpa/wpa_supplicant/gas_query.h index ad1349088ee1..ef82097e2424 100644 --- a/contrib/wpa/wpa_supplicant/gas_query.h +++ b/contrib/wpa/wpa_supplicant/gas_query.h @@ -29,7 +29,6 @@ enum gas_query_result { GAS_QUERY_TIMEOUT, GAS_QUERY_PEER_ERROR, GAS_QUERY_INTERNAL_ERROR, - GAS_QUERY_CANCELLED, GAS_QUERY_DELETED_AT_DEINIT }; @@ -40,7 +39,6 @@ int gas_query_req(struct gas_query *gas, const u8 *dst, int freq, const struct wpabuf *adv_proto, const struct wpabuf *resp, u16 status_code), void *ctx); -void gas_query_cancel(struct gas_query *gas, const u8 *dst, u8 dialog_token); #else /* CONFIG_GAS */ diff --git a/contrib/wpa/wpa_supplicant/hs20_supplicant.c b/contrib/wpa/wpa_supplicant/hs20_supplicant.c index a1afc85ff9bb..e88f147bbd1b 100644 --- a/contrib/wpa/wpa_supplicant/hs20_supplicant.c +++ b/contrib/wpa/wpa_supplicant/hs20_supplicant.c @@ -25,6 +25,7 @@ #include "gas_query.h" #include "interworking.h" #include "hs20_supplicant.h" +#include "base64.h" #define OSU_MAX_ITEMS 10 @@ -60,6 +61,46 @@ struct osu_provider { }; +void hs20_configure_frame_filters(struct wpa_supplicant *wpa_s) +{ + struct wpa_bss *bss = wpa_s->current_bss; + u8 *bssid = wpa_s->bssid; + const u8 *ie; + const u8 *ext_capa; + u32 filter = 0; + + if (!bss || !is_hs20_network(wpa_s, wpa_s->current_ssid, bss)) { + wpa_printf(MSG_DEBUG, + "Not configuring frame filtering - BSS " MACSTR + " is not a Hotspot 2.0 network", MAC2STR(bssid)); + return; + } + + ie = wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE); + + /* Check if DGAF disabled bit is zero (5th byte in the IE) */ + if (!ie || ie[1] < 5) + wpa_printf(MSG_DEBUG, + "Not configuring frame filtering - Can't extract DGAF bit"); + else if (!(ie[6] & HS20_DGAF_DISABLED)) + filter |= WPA_DATA_FRAME_FILTER_FLAG_GTK; + + ext_capa = wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB); + if (!ext_capa || ext_capa[1] < 2) { + wpa_printf(MSG_DEBUG, + "Not configuring frame filtering - Can't extract Proxy ARP bit"); + return; + } + + /* Check if Proxy ARP is enabled (2nd byte in the IE) */ + if (ext_capa[3] & BIT(4)) + filter |= WPA_DATA_FRAME_FILTER_FLAG_ARP | + WPA_DATA_FRAME_FILTER_FLAG_NA; + + wpa_drv_configure_frame_filters(wpa_s, filter); +} + + void wpas_hs20_add_indication(struct wpabuf *buf, int pps_mo_id) { u8 conf; @@ -164,8 +205,8 @@ void hs20_put_anqp_req(u32 stypes, const u8 *payload, size_t payload_len, } -struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload, - size_t payload_len) +static struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload, + size_t payload_len) { struct wpabuf *buf; @@ -180,13 +221,14 @@ struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload, int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes, - const u8 *payload, size_t payload_len) + const u8 *payload, size_t payload_len, int inmem) { struct wpabuf *buf; int ret = 0; int freq; struct wpa_bss *bss; int res; + struct icon_entry *icon_entry; bss = wpa_bss_get_bssid(wpa_s, dst); if (!bss) { @@ -210,15 +252,127 @@ int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes, if (res < 0) { wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request"); wpabuf_free(buf); - ret = -1; + return -1; } else wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token " "%u", res); + if (inmem) { + icon_entry = os_zalloc(sizeof(struct icon_entry)); + if (!icon_entry) + return -1; + os_memcpy(icon_entry->bssid, dst, ETH_ALEN); + icon_entry->file_name = os_malloc(payload_len + 1); + if (!icon_entry->file_name) { + os_free(icon_entry); + return -1; + } + os_memcpy(icon_entry->file_name, payload, payload_len); + icon_entry->file_name[payload_len] = '\0'; + icon_entry->dialog_token = res; + + dl_list_add(&wpa_s->icon_head, &icon_entry->list); + } + return ret; } +static struct icon_entry * hs20_find_icon(struct wpa_supplicant *wpa_s, + const u8 *bssid, + const char *file_name) +{ + struct icon_entry *icon; + + dl_list_for_each(icon, &wpa_s->icon_head, struct icon_entry, list) { + if (os_memcmp(icon->bssid, bssid, ETH_ALEN) == 0 && + os_strcmp(icon->file_name, file_name) == 0 && icon->image) + return icon; + } + + return NULL; +} + + +int hs20_get_icon(struct wpa_supplicant *wpa_s, const u8 *bssid, + const char *file_name, size_t offset, size_t size, + char *reply, size_t buf_len) +{ + struct icon_entry *icon; + size_t out_size; + unsigned char *b64; + size_t b64_size; + int reply_size; + + wpa_printf(MSG_DEBUG, "HS20: Get icon " MACSTR " %s @ %u +%u (%u)", + MAC2STR(bssid), file_name, (unsigned int) offset, + (unsigned int) size, (unsigned int) buf_len); + + icon = hs20_find_icon(wpa_s, bssid, file_name); + if (!icon || !icon->image || offset >= icon->image_len) + return -1; + if (size > icon->image_len - offset) + size = icon->image_len - offset; + out_size = buf_len - 3 /* max base64 padding */; + if (size * 4 > out_size * 3) + size = out_size * 3 / 4; + if (size == 0) + return -1; + + b64 = base64_encode(&icon->image[offset], size, &b64_size); + if (b64 && buf_len >= b64_size) { + os_memcpy(reply, b64, b64_size); + reply_size = b64_size; + } else { + reply_size = -1; + } + os_free(b64); + return reply_size; +} + + +static void hs20_free_icon_entry(struct icon_entry *icon) +{ + wpa_printf(MSG_DEBUG, "HS20: Free stored icon from " MACSTR + " dialog_token=%u file_name=%s image_len=%u", + MAC2STR(icon->bssid), icon->dialog_token, + icon->file_name ? icon->file_name : "N/A", + (unsigned int) icon->image_len); + os_free(icon->file_name); + os_free(icon->image); + os_free(icon); +} + + +int hs20_del_icon(struct wpa_supplicant *wpa_s, const u8 *bssid, + const char *file_name) +{ + struct icon_entry *icon, *tmp; + int count = 0; + + if (!bssid) + wpa_printf(MSG_DEBUG, "HS20: Delete all stored icons"); + else if (!file_name) + wpa_printf(MSG_DEBUG, "HS20: Delete all stored icons for " + MACSTR, MAC2STR(bssid)); + else + wpa_printf(MSG_DEBUG, "HS20: Delete stored icons for " + MACSTR " file name %s", MAC2STR(bssid), file_name); + + dl_list_for_each_safe(icon, tmp, &wpa_s->icon_head, struct icon_entry, + list) { + if ((!bssid || os_memcmp(icon->bssid, bssid, ETH_ALEN) == 0) && + (!file_name || + os_strcmp(icon->file_name, file_name) == 0)) { + dl_list_del(&icon->list); + hs20_free_icon_entry(icon); + count++; + } + } + return count == 0 ? -1 : 0; +} + + static void hs20_set_osu_access_permission(const char *osu_dir, const char *fname) { @@ -243,16 +397,53 @@ static void hs20_set_osu_access_permission(const char *osu_dir, } } + +static void hs20_remove_duplicate_icons(struct wpa_supplicant *wpa_s, + struct icon_entry *new_icon) +{ + struct icon_entry *icon, *tmp; + + dl_list_for_each_safe(icon, tmp, &wpa_s->icon_head, struct icon_entry, + list) { + if (icon == new_icon) + continue; + if (os_memcmp(icon->bssid, new_icon->bssid, ETH_ALEN) == 0 && + os_strcmp(icon->file_name, new_icon->file_name) == 0) { + dl_list_del(&icon->list); + hs20_free_icon_entry(icon); + } + } +} + + static int hs20_process_icon_binary_file(struct wpa_supplicant *wpa_s, const u8 *sa, const u8 *pos, - size_t slen) + size_t slen, u8 dialog_token) { char fname[256]; int png; FILE *f; u16 data_len; + struct icon_entry *icon; - wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR " Icon Binary File", + dl_list_for_each(icon, &wpa_s->icon_head, struct icon_entry, list) { + if (icon->dialog_token == dialog_token && !icon->image && + os_memcmp(icon->bssid, sa, ETH_ALEN) == 0) { + icon->image = os_malloc(slen); + if (!icon->image) + return -1; + os_memcpy(icon->image, pos, slen); + icon->image_len = slen; + hs20_remove_duplicate_icons(wpa_s, icon); + wpa_msg(wpa_s, MSG_INFO, + RX_HS20_ICON MACSTR " %s %u", + MAC2STR(sa), icon->file_name, + (unsigned int) icon->image_len); + return 0; + } + } + + wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR " Icon Binary File", MAC2STR(sa)); if (slen < 4) { @@ -315,7 +506,7 @@ static int hs20_process_icon_binary_file(struct wpa_supplicant *wpa_s, } fclose(f); - wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP-ICON %s", fname); + wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP_ICON "%s", fname); return 0; } @@ -358,7 +549,7 @@ static void hs20_osu_icon_fetch_result(struct wpa_supplicant *wpa_s, int res) void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, const u8 *sa, - const u8 *data, size_t slen) + const u8 *data, size_t slen, u8 dialog_token) { const u8 *pos = data; u8 subtype; @@ -379,7 +570,7 @@ void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s, switch (subtype) { case HS20_STYPE_CAPABILITY_LIST: - wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR + wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR " HS Capability List", MAC2STR(sa)); wpa_hexdump_ascii(MSG_DEBUG, "HS Capability List", pos, slen); if (anqp) { @@ -389,7 +580,7 @@ void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s, } break; case HS20_STYPE_OPERATOR_FRIENDLY_NAME: - wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR + wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR " Operator Friendly Name", MAC2STR(sa)); wpa_hexdump_ascii(MSG_DEBUG, "oper friendly name", pos, slen); if (anqp) { @@ -405,7 +596,7 @@ void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s, "Metrics value from " MACSTR, MAC2STR(sa)); break; } - wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR + wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR " WAN Metrics %02x:%u:%u:%u:%u:%u", MAC2STR(sa), pos[0], WPA_GET_LE32(pos + 1), WPA_GET_LE32(pos + 5), pos[9], pos[10], WPA_GET_LE16(pos + 11)); @@ -415,7 +606,7 @@ void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s, } break; case HS20_STYPE_CONNECTION_CAPABILITY: - wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR + wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR " Connection Capability", MAC2STR(sa)); wpa_hexdump_ascii(MSG_DEBUG, "conn capability", pos, slen); if (anqp) { @@ -425,7 +616,7 @@ void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s, } break; case HS20_STYPE_OPERATING_CLASS: - wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR + wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR " Operating Class", MAC2STR(sa)); wpa_hexdump_ascii(MSG_DEBUG, "Operating Class", pos, slen); if (anqp) { @@ -435,7 +626,7 @@ void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s, } break; case HS20_STYPE_OSU_PROVIDERS_LIST: - wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR + wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR " OSU Providers list", MAC2STR(sa)); wpa_s->num_prov_found++; if (anqp) { @@ -445,7 +636,8 @@ void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s, } break; case HS20_STYPE_ICON_BINARY_FILE: - ret = hs20_process_icon_binary_file(wpa_s, sa, pos, slen); + ret = hs20_process_icon_binary_file(wpa_s, sa, pos, slen, + dialog_token); if (wpa_s->fetch_osu_icon_in_progress) { hs20_osu_icon_fetch_result(wpa_s, ret); eloop_cancel_timeout(hs20_continue_icon_fetch, @@ -511,7 +703,10 @@ static void hs20_osu_fetch_done(struct wpa_supplicant *wpa_s) wpa_s->conf->osu_dir); f = fopen(fname, "w"); if (f == NULL) { + wpa_msg(wpa_s, MSG_INFO, + "Could not write OSU provider information"); hs20_free_osu_prov(wpa_s); + wpa_s->fetch_anqp_in_progress = 0; return; } @@ -579,7 +774,8 @@ void hs20_next_osu_icon(struct wpa_supplicant *wpa_s) if (hs20_anqp_send_req(wpa_s, osu->bssid, BIT(HS20_STYPE_ICON_REQUEST), (u8 *) icon->filename, - os_strlen(icon->filename)) < 0) { + os_strlen(icon->filename), + 0) < 0) { icon->failed = 1; continue; } @@ -617,7 +813,7 @@ static void hs20_osu_add_prov(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, prov->osu_ssid_len = osu_ssid_len; /* OSU Friendly Name Length */ - if (pos + 2 > end) { + if (end - pos < 2) { wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU " "Friendly Name Length"); return; @@ -633,9 +829,9 @@ static void hs20_osu_add_prov(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, pos += len2; /* OSU Friendly Name Duples */ - while (pos2 + 4 <= pos && prov->friendly_name_count < OSU_MAX_ITEMS) { + while (pos - pos2 >= 4 && prov->friendly_name_count < OSU_MAX_ITEMS) { struct osu_lang_string *f; - if (pos2 + 1 + pos2[0] > pos || pos2[0] < 3) { + if (1 + pos2[0] > pos - pos2 || pos2[0] < 3) { wpa_printf(MSG_DEBUG, "Invalid OSU Friendly Name"); break; } @@ -646,7 +842,7 @@ static void hs20_osu_add_prov(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, } /* OSU Server URI */ - if (pos + 1 > end) { + if (end - pos < 1) { wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Server URI length"); return; @@ -661,7 +857,7 @@ static void hs20_osu_add_prov(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, pos += uri_len; /* OSU Method list */ - if (pos + 1 > end) { + if (end - pos < 1) { wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Method " "list length"); return; @@ -681,7 +877,7 @@ static void hs20_osu_add_prov(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, } /* Icons Available Length */ - if (pos + 2 > end) { + if (end - pos < 2) { wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for Icons " "Available Length"); return; @@ -701,7 +897,7 @@ static void hs20_osu_add_prov(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, struct osu_icon *icon = &prov->icon[prov->icon_count]; u8 flen; - if (pos2 + 2 + 2 + 3 + 1 + 1 > pos) { + if (2 + 2 + 3 + 1 + 1 > pos - pos2) { wpa_printf(MSG_DEBUG, "HS 2.0: Invalid Icon Metadata"); break; } @@ -713,46 +909,46 @@ static void hs20_osu_add_prov(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, os_memcpy(icon->lang, pos2, 3); pos2 += 3; - flen = pos2[0]; - if (flen > pos - pos2 - 1) { + flen = *pos2++; + if (flen > pos - pos2) { wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon Type"); break; } - os_memcpy(icon->icon_type, pos2 + 1, flen); - pos2 += 1 + flen; + os_memcpy(icon->icon_type, pos2, flen); + pos2 += flen; - if (pos2 + 1 > pos) { + if (pos - pos2 < 1) { wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon " "Filename length"); break; } - flen = pos2[0]; - if (flen > pos - pos2 - 1) { + flen = *pos2++; + if (flen > pos - pos2) { wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon " "Filename"); break; } - os_memcpy(icon->filename, pos2 + 1, flen); - pos2 += 1 + flen; + os_memcpy(icon->filename, pos2, flen); + pos2 += flen; prov->icon_count++; } /* OSU_NAI */ - if (pos + 1 > end) { + if (end - pos < 1) { wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU_NAI"); return; } - osu_nai_len = pos[0]; - if (osu_nai_len > end - pos - 1) { + osu_nai_len = *pos++; + if (osu_nai_len > end - pos) { wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU_NAI"); return; } - os_memcpy(prov->osu_nai, pos + 1, osu_nai_len); - pos += 1 + osu_nai_len; + os_memcpy(prov->osu_nai, pos, osu_nai_len); + pos += osu_nai_len; /* OSU Service Description Length */ - if (pos + 2 > end) { + if (end - pos < 2) { wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU " "Service Description Length"); return; @@ -768,20 +964,20 @@ static void hs20_osu_add_prov(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, pos += len2; /* OSU Service Description Duples */ - while (pos2 + 4 <= pos && prov->serv_desc_count < OSU_MAX_ITEMS) { + while (pos - pos2 >= 4 && prov->serv_desc_count < OSU_MAX_ITEMS) { struct osu_lang_string *f; u8 descr_len; - descr_len = pos2[0]; - if (descr_len > pos - pos2 - 1 || descr_len < 3) { + descr_len = *pos2++; + if (descr_len > pos - pos2 || descr_len < 3) { wpa_printf(MSG_DEBUG, "Invalid OSU Service " "Description"); break; } f = &prov->serv_desc[prov->serv_desc_count++]; - os_memcpy(f->lang, pos2 + 1, 3); - os_memcpy(f->text, pos2 + 1 + 3, descr_len - 3); - pos2 += 1 + descr_len; + os_memcpy(f->lang, pos2, 3); + os_memcpy(f->text, pos2 + 3, descr_len - 3); + pos2 += descr_len; } wpa_printf(MSG_DEBUG, "HS 2.0: Added OSU Provider through " MACSTR, @@ -816,9 +1012,9 @@ void hs20_osu_icon_fetch(struct wpa_supplicant *wpa_s) end = pos + wpabuf_len(prov_anqp); /* OSU SSID */ - if (pos + 1 > end) + if (end - pos < 1) continue; - if (pos + 1 + pos[0] > end) { + if (1 + pos[0] > end - pos) { wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for " "OSU SSID"); continue; @@ -832,7 +1028,7 @@ void hs20_osu_icon_fetch(struct wpa_supplicant *wpa_s) osu_ssid = pos; pos += osu_ssid_len; - if (pos + 1 > end) { + if (end - pos < 1) { wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for " "Number of OSU Providers"); continue; @@ -842,7 +1038,7 @@ void hs20_osu_icon_fetch(struct wpa_supplicant *wpa_s) num_providers); /* OSU Providers */ - while (pos + 2 < end && num_providers > 0) { + while (end - pos > 2 && num_providers > 0) { num_providers--; len = WPA_GET_LE16(pos); pos += 2; @@ -882,7 +1078,7 @@ static void hs20_osu_scan_res_handler(struct wpa_supplicant *wpa_s, } -int hs20_fetch_osu(struct wpa_supplicant *wpa_s) +int hs20_fetch_osu(struct wpa_supplicant *wpa_s, int skip_scan) { if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - " @@ -913,7 +1109,16 @@ int hs20_fetch_osu(struct wpa_supplicant *wpa_s) wpa_msg(wpa_s, MSG_INFO, "Starting OSU provisioning information fetch"); wpa_s->num_osu_scans = 0; wpa_s->num_prov_found = 0; - hs20_start_osu_scan(wpa_s); + if (skip_scan) { + wpa_s->network_select = 0; + wpa_s->fetch_all_anqp = 1; + wpa_s->fetch_osu_info = 1; + wpa_s->fetch_osu_icon_in_progress = 0; + + interworking_start_fetch_anqp(wpa_s); + } else { + hs20_start_osu_scan(wpa_s); + } return 0; } @@ -1002,8 +1207,16 @@ void hs20_rx_deauth_imminent_notice(struct wpa_supplicant *wpa_s, u8 code, } +void hs20_init(struct wpa_supplicant *wpa_s) +{ + dl_list_init(&wpa_s->icon_head); +} + + void hs20_deinit(struct wpa_supplicant *wpa_s) { eloop_cancel_timeout(hs20_continue_icon_fetch, wpa_s, NULL); hs20_free_osu_prov(wpa_s); + if (wpa_s->icon_head.next) + hs20_del_icon(wpa_s, NULL, NULL); } diff --git a/contrib/wpa/wpa_supplicant/hs20_supplicant.h b/contrib/wpa/wpa_supplicant/hs20_supplicant.h index 85b512012a97..0dd559fdbf01 100644 --- a/contrib/wpa/wpa_supplicant/hs20_supplicant.h +++ b/contrib/wpa/wpa_supplicant/hs20_supplicant.h @@ -8,17 +8,16 @@ #ifndef HS20_SUPPLICANT_H #define HS20_SUPPLICANT_H +void hs20_configure_frame_filters(struct wpa_supplicant *wpa_s); void wpas_hs20_add_indication(struct wpabuf *buf, int pps_mo_id); int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes, - const u8 *payload, size_t payload_len); -struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload, - size_t payload_len); + const u8 *payload, size_t payload_len, int inmem); void hs20_put_anqp_req(u32 stypes, const u8 *payload, size_t payload_len, struct wpabuf *buf); void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, const u8 *sa, - const u8 *data, size_t slen); + const u8 *data, size_t slen, u8 dialog_token); int is_hs20_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct wpa_bss *bss); int hs20_get_pps_mo_id(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); @@ -32,10 +31,16 @@ void hs20_rx_deauth_imminent_notice(struct wpa_supplicant *wpa_s, u8 code, void hs20_free_osu_prov(struct wpa_supplicant *wpa_s); void hs20_next_osu_icon(struct wpa_supplicant *wpa_s); void hs20_osu_icon_fetch(struct wpa_supplicant *wpa_s); -int hs20_fetch_osu(struct wpa_supplicant *wpa_s); +int hs20_fetch_osu(struct wpa_supplicant *wpa_s, int skip_scan); void hs20_cancel_fetch_osu(struct wpa_supplicant *wpa_s); void hs20_icon_fetch_failed(struct wpa_supplicant *wpa_s); void hs20_start_osu_scan(struct wpa_supplicant *wpa_s); +void hs20_init(struct wpa_supplicant *wpa_s); void hs20_deinit(struct wpa_supplicant *wpa_s); +int hs20_get_icon(struct wpa_supplicant *wpa_s, const u8 *bssid, + const char *file_name, size_t offset, size_t size, + char *reply, size_t buf_len); +int hs20_del_icon(struct wpa_supplicant *wpa_s, const u8 *bssid, + const char *file_name); #endif /* HS20_SUPPLICANT_H */ diff --git a/contrib/wpa/wpa_supplicant/ibss_rsn.c b/contrib/wpa/wpa_supplicant/ibss_rsn.c index d9d0ae7f10dd..53d7d57bde35 100644 --- a/contrib/wpa/wpa_supplicant/ibss_rsn.c +++ b/contrib/wpa/wpa_supplicant/ibss_rsn.c @@ -221,6 +221,7 @@ static int ibss_rsn_supp_init(struct ibss_rsn_peer *peer, const u8 *own_addr, peer->supp = wpa_sm_init(ctx); if (peer->supp == NULL) { wpa_printf(MSG_DEBUG, "SUPP: wpa_sm_init() failed"); + os_free(ctx); return -1; } @@ -230,7 +231,7 @@ static int ibss_rsn_supp_init(struct ibss_rsn_peer *peer, const u8 *own_addr, wpa_sm_set_param(peer->supp, WPA_PARAM_PAIRWISE, WPA_CIPHER_CCMP); wpa_sm_set_param(peer->supp, WPA_PARAM_GROUP, WPA_CIPHER_CCMP); wpa_sm_set_param(peer->supp, WPA_PARAM_KEY_MGMT, WPA_KEY_MGMT_PSK); - wpa_sm_set_pmk(peer->supp, psk, PMK_LEN, NULL); + wpa_sm_set_pmk(peer->supp, psk, PMK_LEN, NULL, NULL); peer->supp_ie_len = sizeof(peer->supp_ie); if (wpa_sm_set_assoc_wpa_ie_default(peer->supp, peer->supp_ie, @@ -404,7 +405,7 @@ static void auth_set_eapol(void *ctx, const u8 *addr, static int ibss_rsn_auth_init_group(struct ibss_rsn *ibss_rsn, - const u8 *own_addr) + const u8 *own_addr, struct wpa_ssid *ssid) { struct wpa_auth_config conf; struct wpa_auth_callbacks cb; @@ -418,7 +419,7 @@ static int ibss_rsn_auth_init_group(struct ibss_rsn *ibss_rsn, conf.rsn_pairwise = WPA_CIPHER_CCMP; conf.wpa_group = WPA_CIPHER_CCMP; conf.eapol_version = 2; - conf.wpa_group_rekey = 600; + conf.wpa_group_rekey = ssid->group_rekey ? ssid->group_rekey : 600; os_memset(&cb, 0, sizeof(cb)); cb.ctx = ibss_rsn; @@ -665,7 +666,8 @@ void ibss_rsn_stop(struct ibss_rsn *ibss_rsn, const u8 *peermac) } -struct ibss_rsn * ibss_rsn_init(struct wpa_supplicant *wpa_s) +struct ibss_rsn * ibss_rsn_init(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) { struct ibss_rsn *ibss_rsn; @@ -674,7 +676,7 @@ struct ibss_rsn * ibss_rsn_init(struct wpa_supplicant *wpa_s) return NULL; ibss_rsn->wpa_s = wpa_s; - if (ibss_rsn_auth_init_group(ibss_rsn, wpa_s->own_addr) < 0) { + if (ibss_rsn_auth_init_group(ibss_rsn, wpa_s->own_addr, ssid) < 0) { ibss_rsn_deinit(ibss_rsn); return NULL; } diff --git a/contrib/wpa/wpa_supplicant/ibss_rsn.h b/contrib/wpa/wpa_supplicant/ibss_rsn.h index 67fae2d14ab7..626c543546c8 100644 --- a/contrib/wpa/wpa_supplicant/ibss_rsn.h +++ b/contrib/wpa/wpa_supplicant/ibss_rsn.h @@ -51,7 +51,8 @@ struct ibss_rsn { }; -struct ibss_rsn * ibss_rsn_init(struct wpa_supplicant *wpa_s); +struct ibss_rsn * ibss_rsn_init(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); void ibss_rsn_deinit(struct ibss_rsn *ibss_rsn); int ibss_rsn_start(struct ibss_rsn *ibss_rsn, const u8 *addr); void ibss_rsn_stop(struct ibss_rsn *ibss_rsn, const u8 *peermac); diff --git a/contrib/wpa/wpa_supplicant/interworking.c b/contrib/wpa/wpa_supplicant/interworking.c index fd47c179ea4b..1fb40c74e5cf 100644 --- a/contrib/wpa/wpa_supplicant/interworking.c +++ b/contrib/wpa/wpa_supplicant/interworking.c @@ -362,13 +362,13 @@ static const u8 * nai_realm_parse_eap(struct nai_realm_eap *e, const u8 *pos, u8 elen, auth_count, a; const u8 *e_end; - if (pos + 3 > end) { + if (end - pos < 3) { wpa_printf(MSG_DEBUG, "No room for EAP Method fixed fields"); return NULL; } elen = *pos++; - if (pos + elen > end || elen < 2) { + if (elen > end - pos || elen < 2) { wpa_printf(MSG_DEBUG, "No room for EAP Method subfield"); return NULL; } @@ -381,14 +381,19 @@ static const u8 * nai_realm_parse_eap(struct nai_realm_eap *e, const u8 *pos, for (a = 0; a < auth_count; a++) { u8 id, len; - if (pos + 2 > end || pos + 2 + pos[1] > end) { - wpa_printf(MSG_DEBUG, "No room for Authentication " - "Parameter subfield"); + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, + "No room for Authentication Parameter subfield header"); return NULL; } id = *pos++; len = *pos++; + if (len > end - pos) { + wpa_printf(MSG_DEBUG, + "No room for Authentication Parameter subfield"); + return NULL; + } switch (id) { case NAI_REALM_EAP_AUTH_NON_EAP_INNER_AUTH: @@ -463,7 +468,7 @@ static const u8 * nai_realm_parse_realm(struct nai_realm *r, const u8 *pos, len = WPA_GET_LE16(pos); /* NAI Realm Data field Length */ pos += 2; - if (pos + len > end || len < 3) { + if (len > end - pos || len < 3) { wpa_printf(MSG_DEBUG, "No room for NAI Realm Data " "(len=%u; left=%u)", len, (unsigned int) (end - pos)); @@ -473,7 +478,7 @@ static const u8 * nai_realm_parse_realm(struct nai_realm *r, const u8 *pos, r->encoding = *pos++; realm_len = *pos++; - if (pos + realm_len > f_end) { + if (realm_len > f_end - pos) { wpa_printf(MSG_DEBUG, "No room for NAI Realm " "(len=%u; left=%u)", realm_len, (unsigned int) (f_end - pos)); @@ -485,13 +490,13 @@ static const u8 * nai_realm_parse_realm(struct nai_realm *r, const u8 *pos, return NULL; pos += realm_len; - if (pos + 1 > f_end) { + if (f_end - pos < 1) { wpa_printf(MSG_DEBUG, "No room for EAP Method Count"); return NULL; } r->eap_count = *pos++; wpa_printf(MSG_DEBUG, "EAP Count: %u", r->eap_count); - if (pos + r->eap_count * 3 > f_end) { + if (r->eap_count * 3 > f_end - pos) { wpa_printf(MSG_DEBUG, "No room for EAP Methods"); return NULL; } @@ -746,7 +751,7 @@ static int plmn_id_match(struct wpabuf *anqp, const char *imsi, int mnc_len) return 0; pos = wpabuf_head_u8(anqp); end = pos + wpabuf_len(anqp); - if (pos + 2 > end) + if (end - pos < 2) return 0; if (*pos != 0) { wpa_printf(MSG_DEBUG, "Unsupported GUD version 0x%x", *pos); @@ -754,7 +759,7 @@ static int plmn_id_match(struct wpabuf *anqp, const char *imsi, int mnc_len) } pos++; udhl = *pos++; - if (pos + udhl > end) { + if (udhl > end - pos) { wpa_printf(MSG_DEBUG, "Invalid UDHL"); return 0; } @@ -764,12 +769,12 @@ static int plmn_id_match(struct wpabuf *anqp, const char *imsi, int mnc_len) plmn[0], plmn[1], plmn[2], plmn2[0], plmn2[1], plmn2[2], imsi, mnc_len); - while (pos + 2 <= end) { + while (end - pos >= 2) { u8 iei, len; const u8 *l_end; iei = *pos++; len = *pos++ & 0x7f; - if (pos + len > end) + if (len > end - pos) break; l_end = pos + len; @@ -780,7 +785,7 @@ static int plmn_id_match(struct wpabuf *anqp, const char *imsi, int mnc_len) pos, len); num = *pos++; for (i = 0; i < num; i++) { - if (pos + 3 > l_end) + if (l_end - pos < 3) break; if (os_memcmp(pos, plmn, 3) == 0 || os_memcmp(pos, plmn2, 3) == 0) @@ -945,11 +950,9 @@ static int interworking_set_hs20_params(struct wpa_supplicant *wpa_s, if (!key_mgmt) key_mgmt = wpa_s->conf->pmf != NO_MGMT_FRAME_PROTECTION ? "WPA-EAP WPA-EAP-SHA256" : "WPA-EAP"; - if (wpa_config_set(ssid, "key_mgmt", key_mgmt, 0) < 0) - return -1; - if (wpa_config_set(ssid, "proto", "RSN", 0) < 0) - return -1; - if (wpa_config_set(ssid, "pairwise", "CCMP", 0) < 0) + if (wpa_config_set(ssid, "key_mgmt", key_mgmt, 0) < 0 || + wpa_config_set(ssid, "proto", "RSN", 0) < 0 || + wpa_config_set(ssid, "pairwise", "CCMP", 0) < 0) return -1; return 0; } @@ -1082,12 +1085,12 @@ static int roaming_consortium_element_match(const u8 *ie, const u8 *rc_id, * OI #1, [OI #2], [OI #3] */ - if (pos + 2 > end) + if (end - pos < 2) return 0; pos++; /* skip Number of ANQP OIs */ lens = *pos++; - if (pos + (lens & 0x0f) + (lens >> 4) > end) + if ((lens & 0x0f) + (lens >> 4) > end - pos) return 0; if ((lens & 0x0f) == rc_len && os_memcmp(pos, rc_id, rc_len) == 0) @@ -1121,7 +1124,7 @@ static int roaming_consortium_anqp_match(const struct wpabuf *anqp, /* Set of duples */ while (pos < end) { len = *pos++; - if (pos + len > end) + if (len > end - pos) break; if (len == rc_len && os_memcmp(pos, rc_id, rc_len) == 0) return 1; @@ -1182,6 +1185,7 @@ static int cred_excluded_ssid(struct wpa_cred *cred, struct wpa_bss *bss) static int cred_below_min_backhaul(struct wpa_supplicant *wpa_s, struct wpa_cred *cred, struct wpa_bss *bss) { +#ifdef CONFIG_HS20 int res; unsigned int dl_bandwidth, ul_bandwidth; const u8 *wan; @@ -1233,6 +1237,7 @@ static int cred_below_min_backhaul(struct wpa_supplicant *wpa_s, if (cred->min_ul_bandwidth_roaming > ul_bandwidth) return 1; } +#endif /* CONFIG_HS20 */ return 0; } @@ -1260,9 +1265,11 @@ static int cred_over_max_bss_load(struct wpa_supplicant *wpa_s, } +#ifdef CONFIG_HS20 + static int has_proto_match(const u8 *pos, const u8 *end, u8 proto) { - while (pos + 4 <= end) { + while (end - pos >= 4) { if (pos[0] == proto && pos[3] == 1 /* Open */) return 1; pos += 4; @@ -1275,7 +1282,7 @@ static int has_proto_match(const u8 *pos, const u8 *end, u8 proto) static int has_proto_port_match(const u8 *pos, const u8 *end, u8 proto, u16 port) { - while (pos + 4 <= end) { + while (end - pos >= 4) { if (pos[0] == proto && WPA_GET_LE16(&pos[1]) == port && pos[3] == 1 /* Open */) return 1; @@ -1285,10 +1292,13 @@ static int has_proto_port_match(const u8 *pos, const u8 *end, u8 proto, return 0; } +#endif /* CONFIG_HS20 */ + static int cred_conn_capab_missing(struct wpa_supplicant *wpa_s, struct wpa_cred *cred, struct wpa_bss *bss) { +#ifdef CONFIG_HS20 int res; const u8 *capab, *end; unsigned int i, j; @@ -1325,6 +1335,7 @@ static int cred_conn_capab_missing(struct wpa_supplicant *wpa_s, } } } +#endif /* CONFIG_HS20 */ return 0; } @@ -1438,7 +1449,24 @@ static int interworking_set_eap_params(struct wpa_ssid *ssid, os_free(anon); } - if (cred->username && cred->username[0] && + if (!ttls && cred->username && cred->username[0] && cred->realm && + !os_strchr(cred->username, '@')) { + char *id; + size_t buflen; + int res; + + buflen = os_strlen(cred->username) + 1 + + os_strlen(cred->realm) + 1; + + id = os_malloc(buflen); + if (!id) + return -1; + os_snprintf(id, buflen, "%s@%s", cred->username, cred->realm); + res = wpa_config_set_quoted(ssid, "identity", id); + os_free(id); + if (res < 0) + return -1; + } else if (cred->username && cred->username[0] && wpa_config_set_quoted(ssid, "identity", cred->username) < 0) return -1; @@ -1560,9 +1588,8 @@ fail: } -static int interworking_connect_helper(struct wpa_supplicant *wpa_s, - struct wpa_bss *bss, int allow_excluded, - int only_add) +int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, + int only_add) { struct wpa_cred *cred, *cred_rc, *cred_3gpp; struct wpa_ssid *ssid; @@ -1570,7 +1597,7 @@ static int interworking_connect_helper(struct wpa_supplicant *wpa_s, struct nai_realm_eap *eap = NULL; u16 count, i; char buf[100]; - int excluded = 0, *excl = allow_excluded ? &excluded : NULL; + int excluded = 0, *excl = &excluded; const char *name; if (wpa_s->conf->cred == NULL || bss == NULL) @@ -1584,8 +1611,8 @@ static int interworking_connect_helper(struct wpa_supplicant *wpa_s, } wpa_printf(MSG_DEBUG, "Interworking: Considering BSS " MACSTR - " for connection (allow_excluded=%d)", - MAC2STR(bss->bssid), allow_excluded); + " for connection", + MAC2STR(bss->bssid)); if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) { /* @@ -1603,7 +1630,7 @@ static int interworking_connect_helper(struct wpa_supplicant *wpa_s, wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Highest roaming consortium matching credential priority %d sp_priority %d", cred_rc->priority, cred_rc->sp_priority); - if (allow_excluded && excl && !(*excl)) + if (excl && !(*excl)) excl = NULL; } @@ -1612,7 +1639,7 @@ static int interworking_connect_helper(struct wpa_supplicant *wpa_s, wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Highest NAI Realm list matching credential priority %d sp_priority %d", cred->priority, cred->sp_priority); - if (allow_excluded && excl && !(*excl)) + if (excl && !(*excl)) excl = NULL; } @@ -1622,7 +1649,7 @@ static int interworking_connect_helper(struct wpa_supplicant *wpa_s, wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Highest 3GPP matching credential priority %d sp_priority %d", cred_3gpp->priority, cred_3gpp->sp_priority); - if (allow_excluded && excl && !(*excl)) + if (excl && !(*excl)) excl = NULL; } @@ -1635,7 +1662,7 @@ static int interworking_connect_helper(struct wpa_supplicant *wpa_s, wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Highest roaming consortium matching credential priority %d sp_priority %d (ignore BW)", cred_rc->priority, cred_rc->sp_priority); - if (allow_excluded && excl && !(*excl)) + if (excl && !(*excl)) excl = NULL; } @@ -1645,7 +1672,7 @@ static int interworking_connect_helper(struct wpa_supplicant *wpa_s, wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Highest NAI Realm list matching credential priority %d sp_priority %d (ignore BW)", cred->priority, cred->sp_priority); - if (allow_excluded && excl && !(*excl)) + if (excl && !(*excl)) excl = NULL; } @@ -1655,7 +1682,7 @@ static int interworking_connect_helper(struct wpa_supplicant *wpa_s, wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Highest 3GPP matching credential priority %d sp_priority %d (ignore BW)", cred_3gpp->priority, cred_3gpp->sp_priority); - if (allow_excluded && excl && !(*excl)) + if (excl && !(*excl)) excl = NULL; } } @@ -1820,13 +1847,6 @@ fail: } -int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, - int only_add) -{ - return interworking_connect_helper(wpa_s, bss, 1, only_add); -} - - #ifdef PCSC_FUNCS static int interworking_pcsc_read_imsi(struct wpa_supplicant *wpa_s) { @@ -2125,23 +2145,27 @@ int domain_name_list_contains(struct wpabuf *domain_names, pos = wpabuf_head(domain_names); end = pos + wpabuf_len(domain_names); - while (pos + 1 < end) { - if (pos + 1 + pos[0] > end) + while (end - pos > 1) { + u8 elen; + + elen = *pos++; + if (elen > end - pos) break; wpa_hexdump_ascii(MSG_DEBUG, "Interworking: AP domain name", - pos + 1, pos[0]); - if (pos[0] == len && - os_strncasecmp(domain, (const char *) (pos + 1), len) == 0) + pos, elen); + if (elen == len && + os_strncasecmp(domain, (const char *) pos, len) == 0) return 1; - if (!exact_match && pos[0] > len && pos[pos[0] - len] == '.') { - const char *ap = (const char *) (pos + 1); - int offset = pos[0] - len; + if (!exact_match && elen > len && pos[elen - len - 1] == '.') { + const char *ap = (const char *) pos; + int offset = elen - len; + if (os_strncasecmp(domain, ap + offset, len) == 0) return 1; } - pos += 1 + pos[0]; + pos += elen; } return 0; @@ -2564,11 +2588,13 @@ static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s) return; } +#ifdef CONFIG_HS20 if (wpa_s->fetch_osu_icon_in_progress) { wpa_printf(MSG_DEBUG, "Interworking: Next icon (in progress)"); hs20_next_osu_icon(wpa_s); return; } +#endif /* CONFIG_HS20 */ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { if (!(bss->caps & IEEE80211_CAP_ESS)) @@ -2602,6 +2628,7 @@ static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s) } if (found == 0) { +#ifdef CONFIG_HS20 if (wpa_s->fetch_osu_info) { if (wpa_s->num_prov_found == 0 && wpa_s->fetch_osu_waiting_scan && @@ -2614,6 +2641,7 @@ static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s) hs20_osu_icon_fetch(wpa_s); return; } +#endif /* CONFIG_HS20 */ wpa_msg(wpa_s, MSG_INFO, "ANQP fetch completed"); wpa_s->fetch_anqp_in_progress = 0; if (wpa_s->network_select) @@ -2664,10 +2692,11 @@ void interworking_stop_fetch_anqp(struct wpa_supplicant *wpa_s) int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, - u16 info_ids[], size_t num_ids, u32 subtypes) + u16 info_ids[], size_t num_ids, u32 subtypes, + int get_cell_pref) { struct wpabuf *buf; - struct wpabuf *hs20_buf = NULL; + struct wpabuf *extra_buf = NULL; int ret = 0; int freq; struct wpa_bss *bss; @@ -2690,15 +2719,31 @@ int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, #ifdef CONFIG_HS20 if (subtypes != 0) { - hs20_buf = wpabuf_alloc(100); - if (hs20_buf == NULL) + extra_buf = wpabuf_alloc(100); + if (extra_buf == NULL) return -1; - hs20_put_anqp_req(subtypes, NULL, 0, hs20_buf); + hs20_put_anqp_req(subtypes, NULL, 0, extra_buf); } #endif /* CONFIG_HS20 */ - buf = anqp_build_req(info_ids, num_ids, hs20_buf); - wpabuf_free(hs20_buf); +#ifdef CONFIG_MBO + if (get_cell_pref) { + struct wpabuf *mbo; + + mbo = mbo_build_anqp_buf(wpa_s, bss); + if (mbo) { + if (wpabuf_resize(&extra_buf, wpabuf_len(mbo))) { + wpabuf_free(extra_buf); + return -1; + } + wpabuf_put_buf(extra_buf, mbo); + wpabuf_free(mbo); + } + } +#endif /* CONFIG_MBO */ + + buf = anqp_build_req(info_ids, num_ids, extra_buf); + wpabuf_free(extra_buf); if (buf == NULL) return -1; @@ -2716,10 +2761,46 @@ int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, } +static void anqp_add_extra(struct wpa_supplicant *wpa_s, + struct wpa_bss_anqp *anqp, u16 info_id, + const u8 *data, size_t slen) +{ + struct wpa_bss_anqp_elem *tmp, *elem = NULL; + + if (!anqp) + return; + + dl_list_for_each(tmp, &anqp->anqp_elems, struct wpa_bss_anqp_elem, + list) { + if (tmp->infoid == info_id) { + elem = tmp; + break; + } + } + + if (!elem) { + elem = os_zalloc(sizeof(*elem)); + if (!elem) + return; + elem->infoid = info_id; + dl_list_add(&anqp->anqp_elems, &elem->list); + } else { + wpabuf_free(elem->payload); + } + + elem->payload = wpabuf_alloc_copy(data, slen); + if (!elem->payload) { + dl_list_del(&elem->list); + os_free(elem); + } +} + + static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, const u8 *sa, u16 info_id, - const u8 *data, size_t slen) + const u8 *data, size_t slen, + u8 dialog_token) { const u8 *pos = data; struct wpa_bss_anqp *anqp = NULL; @@ -2732,7 +2813,7 @@ static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s, switch (info_id) { case ANQP_CAPABILITY_LIST: - wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR + wpa_msg(wpa_s, MSG_INFO, RX_ANQP MACSTR " ANQP Capability list", MAC2STR(sa)); wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Capability list", pos, slen); @@ -2742,7 +2823,7 @@ static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s, } break; case ANQP_VENUE_NAME: - wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR + wpa_msg(wpa_s, MSG_INFO, RX_ANQP MACSTR " Venue Name", MAC2STR(sa)); wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Venue Name", pos, slen); if (anqp) { @@ -2751,7 +2832,7 @@ static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s, } break; case ANQP_NETWORK_AUTH_TYPE: - wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR + wpa_msg(wpa_s, MSG_INFO, RX_ANQP MACSTR " Network Authentication Type information", MAC2STR(sa)); wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Network Authentication " @@ -2762,7 +2843,7 @@ static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s, } break; case ANQP_ROAMING_CONSORTIUM: - wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR + wpa_msg(wpa_s, MSG_INFO, RX_ANQP MACSTR " Roaming Consortium list", MAC2STR(sa)); wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Roaming Consortium", pos, slen); @@ -2772,7 +2853,7 @@ static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s, } break; case ANQP_IP_ADDR_TYPE_AVAILABILITY: - wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR + wpa_msg(wpa_s, MSG_INFO, RX_ANQP MACSTR " IP Address Type Availability information", MAC2STR(sa)); wpa_hexdump(MSG_MSGDUMP, "ANQP: IP Address Availability", @@ -2784,7 +2865,7 @@ static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s, } break; case ANQP_NAI_REALM: - wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR + wpa_msg(wpa_s, MSG_INFO, RX_ANQP MACSTR " NAI Realm list", MAC2STR(sa)); wpa_hexdump_ascii(MSG_DEBUG, "ANQP: NAI Realm", pos, slen); if (anqp) { @@ -2793,7 +2874,7 @@ static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s, } break; case ANQP_3GPP_CELLULAR_NETWORK: - wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR + wpa_msg(wpa_s, MSG_INFO, RX_ANQP MACSTR " 3GPP Cellular Network information", MAC2STR(sa)); wpa_hexdump_ascii(MSG_DEBUG, "ANQP: 3GPP Cellular Network", pos, slen); @@ -2803,7 +2884,7 @@ static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s, } break; case ANQP_DOMAIN_NAME: - wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR + wpa_msg(wpa_s, MSG_INFO, RX_ANQP MACSTR " Domain Name list", MAC2STR(sa)); wpa_hexdump_ascii(MSG_MSGDUMP, "ANQP: Domain Name", pos, slen); if (anqp) { @@ -2829,7 +2910,8 @@ static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s, switch (type) { case HS20_ANQP_OUI_TYPE: hs20_parse_rx_hs20_anqp_resp(wpa_s, bss, sa, - pos, slen); + pos, slen, + dialog_token); break; default: wpa_msg(wpa_s, MSG_DEBUG, @@ -2849,6 +2931,7 @@ static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s, default: wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Unsupported ANQP Info ID %u", info_id); + anqp_add_extra(wpa_s, anqp, info_id, data, slen); break; } } @@ -2871,8 +2954,10 @@ void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token, " dialog_token=%u result=%d status_code=%u", MAC2STR(dst), dialog_token, result, status_code); if (result != GAS_QUERY_SUCCESS) { +#ifdef CONFIG_HS20 if (wpa_s->fetch_osu_icon_in_progress) hs20_icon_fetch_failed(wpa_s); +#endif /* CONFIG_HS20 */ anqp_result = "FAILURE"; goto out; } @@ -2882,8 +2967,10 @@ void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token, pos[1] < 2 || pos[3] != ACCESS_NETWORK_QUERY_PROTOCOL) { wpa_msg(wpa_s, MSG_DEBUG, "ANQP: Unexpected Advertisement Protocol in response"); +#ifdef CONFIG_HS20 if (wpa_s->fetch_osu_icon_in_progress) hs20_icon_fetch_failed(wpa_s); +#endif /* CONFIG_HS20 */ anqp_result = "INVALID_FRAME"; goto out; } @@ -2927,12 +3014,14 @@ void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token, goto out_parse_done; } interworking_parse_rx_anqp_resp(wpa_s, bss, dst, info_id, pos, - slen); + slen, dialog_token); pos += slen; } out_parse_done: +#ifdef CONFIG_HS20 hs20_notify_parse_done(wpa_s); +#endif /* CONFIG_HS20 */ out: wpa_msg(wpa_s, MSG_INFO, ANQP_QUERY_DONE "addr=" MACSTR " result=%s", MAC2STR(dst), anqp_result); diff --git a/contrib/wpa/wpa_supplicant/interworking.h b/contrib/wpa/wpa_supplicant/interworking.h index 3743dc00e905..3d22292618b2 100644 --- a/contrib/wpa/wpa_supplicant/interworking.h +++ b/contrib/wpa/wpa_supplicant/interworking.h @@ -12,7 +12,8 @@ enum gas_query_result; int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, - u16 info_ids[], size_t num_ids, u32 subtypes); + u16 info_ids[], size_t num_ids, u32 subtypes, + int get_cell_pref); void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token, enum gas_query_result result, const struct wpabuf *adv_proto, diff --git a/contrib/wpa/wpa_supplicant/libwpa_test.c b/contrib/wpa/wpa_supplicant/libwpa_test.c new file mode 100644 index 000000000000..e51ab7247665 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/libwpa_test.c @@ -0,0 +1,32 @@ +/* + * libwpa_test - Test program for libwpa_client.* library linking + * Copyright (c) 2015, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common/wpa_ctrl.h" + +int main(int argc, char *argv[]) +{ + struct wpa_ctrl *ctrl; + + ctrl = wpa_ctrl_open("foo"); + if (!ctrl) + return -1; + if (wpa_ctrl_attach(ctrl) == 0) + wpa_ctrl_detach(ctrl); + if (wpa_ctrl_pending(ctrl)) { + char buf[10]; + size_t len; + + len = sizeof(buf); + wpa_ctrl_recv(ctrl, buf, &len); + } + wpa_ctrl_close(ctrl); + + return 0; +} diff --git a/contrib/wpa/wpa_supplicant/main.c b/contrib/wpa/wpa_supplicant/main.c index d5d47e1d77b2..e08c2fd266f1 100644 --- a/contrib/wpa/wpa_supplicant/main.c +++ b/contrib/wpa/wpa_supplicant/main.c @@ -65,41 +65,44 @@ static void usage(void) " -B = run daemon in the background\n" " -c = Configuration file\n" " -C = ctrl_interface parameter (only used if -c is not)\n" - " -i = interface name\n" - " -I = additional configuration file\n" " -d = increase debugging verbosity (-dd even more)\n" " -D = driver name (can be multiple drivers: nl80211,wext)\n" - " -e = entropy file\n"); + " -e = entropy file\n" #ifdef CONFIG_DEBUG_FILE - printf(" -f = log output to debug file instead of stdout\n"); + " -f = log output to debug file instead of stdout\n" #endif /* CONFIG_DEBUG_FILE */ - printf(" -g = global ctrl_interface\n" + " -g = global ctrl_interface\n" " -G = global ctrl_interface group\n" - " -K = include keys (passwords, etc.) in debug output\n"); -#ifdef CONFIG_DEBUG_SYSLOG - printf(" -s = log output to syslog instead of stdout\n"); -#endif /* CONFIG_DEBUG_SYSLOG */ -#ifdef CONFIG_DEBUG_LINUX_TRACING - printf(" -T = record to Linux tracing in addition to logging\n"); - printf(" (records all messages regardless of debug verbosity)\n"); -#endif /* CONFIG_DEBUG_LINUX_TRACING */ - printf(" -t = include timestamp in debug messages\n" " -h = show this help text\n" + " -i = interface name\n" + " -I = additional configuration file\n" + " -K = include keys (passwords, etc.) in debug output\n" " -L = show license (BSD)\n" +#ifdef CONFIG_P2P + " -m = Configuration file for the P2P Device interface\n" +#endif /* CONFIG_P2P */ +#ifdef CONFIG_MATCH_IFACE + " -M = start describing new matching interface\n" +#endif /* CONFIG_MATCH_IFACE */ + " -N = start describing new interface\n" " -o = override driver parameter for new interfaces\n" " -O = override ctrl_interface parameter for new interfaces\n" " -p = driver parameters\n" " -P = PID file\n" - " -q = decrease debugging verbosity (-qq even less)\n"); + " -q = decrease debugging verbosity (-qq even less)\n" +#ifdef CONFIG_DEBUG_SYSLOG + " -s = log output to syslog instead of stdout\n" +#endif /* CONFIG_DEBUG_SYSLOG */ + " -t = include timestamp in debug messages\n" +#ifdef CONFIG_DEBUG_LINUX_TRACING + " -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 - printf(" -u = enable DBus control interface\n"); + " -u = enable DBus control interface\n" #endif /* CONFIG_DBUS */ - printf(" -v = show version\n" - " -W = wait for a control interface monitor before starting\n" -#ifdef CONFIG_P2P - " -m = Configuration file for the P2P Device interface\n" -#endif /* CONFIG_P2P */ - " -N = start describing new interface\n"); + " -v = show version\n" + " -W = wait for a control interface monitor before starting\n"); printf("example:\n" " wpa_supplicant -D%s -iwlan0 -c/etc/wpa_supplicant.conf\n", @@ -153,6 +156,28 @@ static void wpa_supplicant_fd_workaround(int start) } +#ifdef CONFIG_MATCH_IFACE +static int wpa_supplicant_init_match(struct wpa_global *global) +{ + /* + * The assumption is that the first driver is the primary driver and + * will handle the arrival / departure of interfaces. + */ + if (wpa_drivers[0]->global_init && !global->drv_priv[0]) { + global->drv_priv[0] = wpa_drivers[0]->global_init(global); + if (!global->drv_priv[0]) { + wpa_printf(MSG_ERROR, + "Failed to initialize driver '%s'", + wpa_drivers[0]->name); + return -1; + } + } + + return 0; +} +#endif /* CONFIG_MATCH_IFACE */ + + int main(int argc, char *argv[]) { int c, i; @@ -176,7 +201,7 @@ int main(int argc, char *argv[]) for (;;) { c = getopt(argc, argv, - "b:Bc:C:D:de:f:g:G:hi:I:KLm:No:O:p:P:qsTtuvW"); + "b:Bc:C:D:de:f:g:G:hi:I:KLMm:No:O:p:P:qsTtuvW"); if (c < 0) break; switch (c) { @@ -282,6 +307,20 @@ int main(int argc, char *argv[]) case 'W': params.wait_for_monitor++; break; +#ifdef CONFIG_MATCH_IFACE + case 'M': + params.match_iface_count++; + iface = os_realloc_array(params.match_ifaces, + params.match_iface_count, + sizeof(struct wpa_interface)); + if (!iface) + goto out; + params.match_ifaces = iface; + iface = ¶ms.match_ifaces[params.match_iface_count - + 1]; + os_memset(iface, 0, sizeof(*iface)); + break; +#endif /* CONFIG_MATCH_IFACE */ case 'N': iface_count++; iface = os_realloc_array(ifaces, iface_count, @@ -328,6 +367,9 @@ int main(int argc, char *argv[]) ifaces[i].ctrl_interface == NULL) || ifaces[i].ifname == NULL) { if (iface_count == 1 && (params.ctrl_interface || +#ifdef CONFIG_MATCH_IFACE + params.match_iface_count || +#endif /* CONFIG_MATCH_IFACE */ params.dbus_ctrl_interface)) break; usage(); @@ -341,6 +383,11 @@ int main(int argc, char *argv[]) } } +#ifdef CONFIG_MATCH_IFACE + if (exitcode == 0) + exitcode = wpa_supplicant_init_match(global); +#endif /* CONFIG_MATCH_IFACE */ + if (exitcode == 0) exitcode = wpa_supplicant_run(global); @@ -351,6 +398,9 @@ int main(int argc, char *argv[]) out: wpa_supplicant_fd_workaround(0); os_free(ifaces); +#ifdef CONFIG_MATCH_IFACE + os_free(params.match_ifaces); +#endif /* CONFIG_MATCH_IFACE */ os_free(params.pid_file); os_program_deinit(); diff --git a/contrib/wpa/wpa_supplicant/mbo.c b/contrib/wpa/wpa_supplicant/mbo.c new file mode 100644 index 000000000000..7e049be3df41 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/mbo.c @@ -0,0 +1,836 @@ +/* + * wpa_supplicant - MBO + * + * Copyright(c) 2015 Intel Deutschland GmbH + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "common/gas.h" +#include "config.h" +#include "wpa_supplicant_i.h" +#include "driver_i.h" +#include "bss.h" +#include "scan.h" + +/* type + length + oui + oui type */ +#define MBO_IE_HEADER 6 + + +static int wpas_mbo_validate_non_pref_chan(u8 oper_class, u8 chan, u8 reason) +{ + if (reason > MBO_NON_PREF_CHAN_REASON_INT_INTERFERENCE) + return -1; + + /* Only checking the validity of the channel and oper_class */ + if (ieee80211_chan_to_freq(NULL, oper_class, chan) == -1) + return -1; + + return 0; +} + + +const u8 * wpas_mbo_get_bss_attr(struct wpa_bss *bss, enum mbo_attr_id attr) +{ + const u8 *mbo, *end; + + if (!bss) + return NULL; + + mbo = wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE); + if (!mbo) + return NULL; + + end = mbo + 2 + mbo[1]; + mbo += MBO_IE_HEADER; + + return get_ie(mbo, end - mbo, attr); +} + + +static void wpas_mbo_non_pref_chan_attr_body(struct wpa_supplicant *wpa_s, + struct wpabuf *mbo, + u8 start, u8 end) +{ + u8 i; + + wpabuf_put_u8(mbo, wpa_s->non_pref_chan[start].oper_class); + + for (i = start; i < end; i++) + wpabuf_put_u8(mbo, wpa_s->non_pref_chan[i].chan); + + wpabuf_put_u8(mbo, wpa_s->non_pref_chan[start].preference); + wpabuf_put_u8(mbo, wpa_s->non_pref_chan[start].reason); +} + + +static void wpas_mbo_non_pref_chan_attr(struct wpa_supplicant *wpa_s, + struct wpabuf *mbo, u8 start, u8 end) +{ + size_t size = end - start + 3; + + 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_body(wpa_s, mbo, start, end); +} + + +static void wpas_mbo_non_pref_chan_subelem_hdr(struct wpabuf *mbo, u8 len) +{ + wpabuf_put_u8(mbo, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(mbo, len); /* Length */ + wpabuf_put_be24(mbo, OUI_WFA); + wpabuf_put_u8(mbo, MBO_ATTR_ID_NON_PREF_CHAN_REPORT); +} + + +static void wpas_mbo_non_pref_chan_subelement(struct wpa_supplicant *wpa_s, + struct wpabuf *mbo, u8 start, + u8 end) +{ + size_t size = end - start + 7; + + if (size + 2 > wpabuf_tailroom(mbo)) + return; + + wpas_mbo_non_pref_chan_subelem_hdr(mbo, size); + wpas_mbo_non_pref_chan_attr_body(wpa_s, mbo, start, end); +} + + +static void wpas_mbo_non_pref_chan_attrs(struct wpa_supplicant *wpa_s, + struct wpabuf *mbo, int subelement) +{ + u8 i, start = 0; + struct wpa_mbo_non_pref_channel *start_pref; + + if (!wpa_s->non_pref_chan || !wpa_s->non_pref_chan_num) { + if (subelement) + wpas_mbo_non_pref_chan_subelem_hdr(mbo, 4); + return; + } + start_pref = &wpa_s->non_pref_chan[0]; + + for (i = 1; i <= wpa_s->non_pref_chan_num; i++) { + struct wpa_mbo_non_pref_channel *non_pref = NULL; + + if (i < wpa_s->non_pref_chan_num) + non_pref = &wpa_s->non_pref_chan[i]; + if (!non_pref || + non_pref->oper_class != start_pref->oper_class || + non_pref->reason != start_pref->reason || + non_pref->preference != start_pref->preference) { + if (subelement) + wpas_mbo_non_pref_chan_subelement(wpa_s, mbo, + start, i); + else + wpas_mbo_non_pref_chan_attr(wpa_s, mbo, start, + i); + + if (!non_pref) + return; + + start = i; + start_pref = non_pref; + } + } +} + + +int wpas_mbo_ie(struct wpa_supplicant *wpa_s, u8 *buf, size_t len) +{ + struct wpabuf *mbo; + int res; + + if (len < MBO_IE_HEADER + 3 + 7) + return 0; + + /* Leave room for the MBO IE header */ + mbo = wpabuf_alloc(len - MBO_IE_HEADER); + if (!mbo) + return 0; + + /* Add non-preferred channels attribute */ + wpas_mbo_non_pref_chan_attrs(wpa_s, mbo, 0); + + /* + * Send cellular capabilities attribute even if AP does not advertise + * cellular capabilities. + */ + wpabuf_put_u8(mbo, MBO_ATTR_ID_CELL_DATA_CAPA); + wpabuf_put_u8(mbo, 1); + wpabuf_put_u8(mbo, wpa_s->conf->mbo_cell_capa); + + res = mbo_add_ie(buf, len, wpabuf_head_u8(mbo), wpabuf_len(mbo)); + if (!res) + wpa_printf(MSG_ERROR, "Failed to add MBO IE"); + + wpabuf_free(mbo); + return res; +} + + +static void wpas_mbo_send_wnm_notification(struct wpa_supplicant *wpa_s, + const u8 *data, size_t len) +{ + struct wpabuf *buf; + int res; + + /* + * Send WNM-Notification Request frame only in case of a change in + * non-preferred channels list during association, if the AP supports + * MBO. + */ + if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_bss || + !wpa_bss_get_vendor_ie(wpa_s->current_bss, MBO_IE_VENDOR_TYPE)) + return; + + buf = wpabuf_alloc(4 + len); + if (!buf) + return; + + wpabuf_put_u8(buf, WLAN_ACTION_WNM); + wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ); + wpa_s->mbo_wnm_token++; + if (wpa_s->mbo_wnm_token == 0) + wpa_s->mbo_wnm_token++; + wpabuf_put_u8(buf, wpa_s->mbo_wnm_token); + wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); /* Type */ + + wpabuf_put_data(buf, data, len); + + res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, + wpa_s->own_addr, wpa_s->bssid, + wpabuf_head(buf), wpabuf_len(buf), 0); + if (res < 0) + wpa_printf(MSG_DEBUG, + "Failed to send WNM-Notification Request frame with non-preferred channel list"); + + wpabuf_free(buf); +} + + +static void wpas_mbo_non_pref_chan_changed(struct wpa_supplicant *wpa_s) +{ + struct wpabuf *buf; + + buf = wpabuf_alloc(512); + if (!buf) + return; + + wpas_mbo_non_pref_chan_attrs(wpa_s, buf, 1); + wpas_mbo_send_wnm_notification(wpa_s, wpabuf_head_u8(buf), + wpabuf_len(buf)); + wpabuf_free(buf); +} + + +static int wpa_non_pref_chan_is_eq(struct wpa_mbo_non_pref_channel *a, + struct wpa_mbo_non_pref_channel *b) +{ + return a->oper_class == b->oper_class && a->chan == b->chan; +} + + +/* + * wpa_non_pref_chan_cmp - Compare two channels for sorting + * + * In MBO IE non-preferred channel subelement we can put many channels in an + * attribute if they are in the same operating class and have the same + * preference and reason. To make it easy for the functions that build + * the IE attributes and WNM Request subelements, save the channels sorted + * by their oper_class and reason. + */ +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; + if (a->reason != b->reason) + return a->reason - b->reason; + return a->preference - b->preference; +} + + +int wpas_mbo_update_non_pref_chan(struct wpa_supplicant *wpa_s, + const char *non_pref_chan) +{ + char *cmd, *token, *context = NULL; + struct wpa_mbo_non_pref_channel *chans = NULL, *tmp_chans; + size_t num = 0, size = 0; + unsigned i; + + wpa_printf(MSG_DEBUG, "MBO: Update non-preferred channels, non_pref_chan=%s", + non_pref_chan ? non_pref_chan : "N/A"); + + /* + * The shortest channel configuration is 10 characters - commas, 3 + * colons, and 4 values that one of them (oper_class) is 2 digits or + * more. + */ + if (!non_pref_chan || os_strlen(non_pref_chan) < 10) + goto update; + + cmd = os_strdup(non_pref_chan); + if (!cmd) + return -1; + + while ((token = str_token(cmd, " ", &context))) { + struct wpa_mbo_non_pref_channel *chan; + int ret; + unsigned int _oper_class; + unsigned int _chan; + unsigned int _preference; + unsigned int _reason; + + if (num == size) { + size = size ? size * 2 : 1; + tmp_chans = os_realloc_array(chans, size, + sizeof(*chans)); + if (!tmp_chans) { + wpa_printf(MSG_ERROR, + "Couldn't reallocate non_pref_chan"); + goto fail; + } + chans = tmp_chans; + } + + chan = &chans[num]; + + ret = sscanf(token, "%u:%u:%u:%u", &_oper_class, + &_chan, &_preference, &_reason); + if (ret != 4 || + _oper_class > 255 || _chan > 255 || + _preference > 255 || _reason > 65535 ) { + wpa_printf(MSG_ERROR, "Invalid non-pref chan input %s", + token); + goto fail; + } + chan->oper_class = _oper_class; + chan->chan = _chan; + chan->preference = _preference; + chan->reason = _reason; + + if (wpas_mbo_validate_non_pref_chan(chan->oper_class, + chan->chan, chan->reason)) { + wpa_printf(MSG_ERROR, + "Invalid non_pref_chan: oper class %d chan %d reason %d", + chan->oper_class, chan->chan, chan->reason); + goto fail; + } + + for (i = 0; i < num; i++) + if (wpa_non_pref_chan_is_eq(chan, &chans[i])) + break; + if (i != num) { + wpa_printf(MSG_ERROR, + "oper class %d chan %d is duplicated", + chan->oper_class, chan->chan); + goto fail; + } + + num++; + } + + os_free(cmd); + + if (chans) { + qsort(chans, num, sizeof(struct wpa_mbo_non_pref_channel), + wpa_non_pref_chan_cmp); + } + +update: + os_free(wpa_s->non_pref_chan); + wpa_s->non_pref_chan = chans; + wpa_s->non_pref_chan_num = num; + wpas_mbo_non_pref_chan_changed(wpa_s); + + return 0; + +fail: + os_free(chans); + os_free(cmd); + return -1; +} + + +void wpas_mbo_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ie) +{ + wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(ie, 7); + wpabuf_put_be24(ie, OUI_WFA); + wpabuf_put_u8(ie, MBO_OUI_TYPE); + + wpabuf_put_u8(ie, MBO_ATTR_ID_CELL_DATA_CAPA); + wpabuf_put_u8(ie, 1); + wpabuf_put_u8(ie, wpa_s->conf->mbo_cell_capa); +} + + +enum chan_allowed { + NOT_ALLOWED, ALLOWED +}; + +static enum chan_allowed allow_channel(struct hostapd_hw_modes *mode, u8 chan, + unsigned int *flags) +{ + int i; + + for (i = 0; i < mode->num_channels; i++) { + if (mode->channels[i].chan == chan) + break; + } + + if (i == mode->num_channels || + (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED)) + return NOT_ALLOWED; + + if (flags) + *flags = mode->channels[i].flag; + + return ALLOWED; +} + + +static int get_center_80mhz(struct hostapd_hw_modes *mode, u8 channel) +{ + u8 center_channels[] = {42, 58, 106, 122, 138, 155}; + size_t i; + + if (mode->mode != HOSTAPD_MODE_IEEE80211A) + return 0; + + for (i = 0; i < ARRAY_SIZE(center_channels); i++) { + /* + * In 80 MHz, the bandwidth "spans" 12 channels (e.g., 36-48), + * so the center channel is 6 channels away from the start/end. + */ + if (channel >= center_channels[i] - 6 && + channel <= center_channels[i] + 6) + return center_channels[i]; + } + + return 0; +} + + +static enum chan_allowed verify_80mhz(struct hostapd_hw_modes *mode, u8 channel) +{ + u8 center_chan; + unsigned int i; + + center_chan = get_center_80mhz(mode, channel); + if (!center_chan) + return NOT_ALLOWED; + + /* check all the channels are available */ + for (i = 0; i < 4; i++) { + unsigned int flags; + u8 adj_chan = center_chan - 6 + i * 4; + + if (allow_channel(mode, adj_chan, &flags) == NOT_ALLOWED) + return NOT_ALLOWED; + + if ((i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_70)) || + (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_50)) || + (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_30)) || + (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_10))) + return NOT_ALLOWED; + } + + return ALLOWED; +} + + +static int get_center_160mhz(struct hostapd_hw_modes *mode, u8 channel) +{ + u8 center_channels[] = { 50, 114 }; + unsigned int i; + + if (mode->mode != HOSTAPD_MODE_IEEE80211A) + return 0; + + for (i = 0; i < ARRAY_SIZE(center_channels); i++) { + /* + * In 160 MHz, the bandwidth "spans" 28 channels (e.g., 36-64), + * so the center channel is 14 channels away from the start/end. + */ + if (channel >= center_channels[i] - 14 && + channel <= center_channels[i] + 14) + return center_channels[i]; + } + + return 0; +} + + +static enum chan_allowed verify_160mhz(struct hostapd_hw_modes *mode, + u8 channel) +{ + u8 center_chan; + unsigned int i; + + center_chan = get_center_160mhz(mode, channel); + if (!center_chan) + return NOT_ALLOWED; + + /* Check all the channels are available */ + for (i = 0; i < 8; i++) { + unsigned int flags; + u8 adj_chan = center_chan - 14 + i * 4; + + if (allow_channel(mode, adj_chan, &flags) == NOT_ALLOWED) + return NOT_ALLOWED; + + if ((i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_150)) || + (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_130)) || + (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_110)) || + (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_90)) || + (i == 4 && !(flags & HOSTAPD_CHAN_VHT_90_70)) || + (i == 5 && !(flags & HOSTAPD_CHAN_VHT_110_50)) || + (i == 6 && !(flags & HOSTAPD_CHAN_VHT_130_30)) || + (i == 7 && !(flags & HOSTAPD_CHAN_VHT_150_10))) + return NOT_ALLOWED; + } + + return ALLOWED; +} + + +static enum chan_allowed verify_channel(struct hostapd_hw_modes *mode, + u8 channel, u8 bw) +{ + unsigned int flag = 0; + enum chan_allowed res, res2; + + res2 = res = allow_channel(mode, channel, &flag); + if (bw == BW40MINUS) { + if (!(flag & HOSTAPD_CHAN_HT40MINUS)) + return NOT_ALLOWED; + res2 = allow_channel(mode, channel - 4, NULL); + } else if (bw == BW40PLUS) { + if (!(flag & HOSTAPD_CHAN_HT40PLUS)) + return NOT_ALLOWED; + res2 = allow_channel(mode, channel + 4, NULL); + } else if (bw == BW80) { + /* + * channel is a center channel and as such, not necessarily a + * valid 20 MHz channels. Override earlier allow_channel() + * result and use only the 80 MHz specific version. + */ + res2 = res = verify_80mhz(mode, channel); + } else if (bw == BW160) { + /* + * channel is a center channel and as such, not necessarily a + * valid 20 MHz channels. Override earlier allow_channel() + * result and use only the 160 MHz specific version. + */ + res2 = res = verify_160mhz(mode, channel); + } else if (bw == BW80P80) { + /* + * channel is a center channel and as such, not necessarily a + * valid 20 MHz channels. Override earlier allow_channel() + * result and use only the 80 MHz specific version. + */ + res2 = res = verify_80mhz(mode, channel); + } + + if (res == NOT_ALLOWED || res2 == NOT_ALLOWED) + return NOT_ALLOWED; + + return ALLOWED; +} + + +static int wpas_op_class_supported(struct wpa_supplicant *wpa_s, + const struct oper_class_map *op_class) +{ + int chan; + size_t i; + struct hostapd_hw_modes *mode; + int found; + + mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, op_class->mode); + if (!mode) + return 0; + + if (op_class->op_class == 128) { + u8 channels[] = { 42, 58, 106, 122, 138, 155 }; + + for (i = 0; i < ARRAY_SIZE(channels); i++) { + if (verify_channel(mode, channels[i], op_class->bw) == + ALLOWED) + return 1; + } + + return 0; + } + + if (op_class->op_class == 129) { + /* Check if either 160 MHz channels is allowed */ + return verify_channel(mode, 50, op_class->bw) == ALLOWED || + verify_channel(mode, 114, op_class->bw) == ALLOWED; + } + + if (op_class->op_class == 130) { + /* Need at least two non-contiguous 80 MHz segments */ + found = 0; + + if (verify_channel(mode, 42, op_class->bw) == ALLOWED || + verify_channel(mode, 58, op_class->bw) == ALLOWED) + found++; + if (verify_channel(mode, 106, op_class->bw) == ALLOWED || + verify_channel(mode, 122, op_class->bw) == ALLOWED || + verify_channel(mode, 138, op_class->bw) == ALLOWED) + found++; + if (verify_channel(mode, 106, op_class->bw) == ALLOWED && + verify_channel(mode, 138, op_class->bw) == ALLOWED) + found++; + if (verify_channel(mode, 155, op_class->bw) == ALLOWED) + found++; + + if (found >= 2) + return 1; + + return 0; + } + + found = 0; + for (chan = op_class->min_chan; chan <= op_class->max_chan; + chan += op_class->inc) { + if (verify_channel(mode, chan, op_class->bw) == ALLOWED) { + found = 1; + break; + } + } + + return found; +} + + +int wpas_mbo_supp_op_class_ie(struct wpa_supplicant *wpa_s, int freq, u8 *pos, + size_t len) +{ + struct wpabuf *buf; + u8 op, current, chan; + u8 *ie_len; + int res; + + /* + * Assume 20 MHz channel for now. + * TODO: Use the secondary channel and VHT channel width that will be + * used after association. + */ + if (ieee80211_freq_to_channel_ext(freq, 0, VHT_CHANWIDTH_USE_HT, + ¤t, &chan) == NUM_HOSTAPD_MODES) + return 0; + + /* + * Need 3 bytes for EID, length, and current operating class, plus + * 1 byte for every other supported operating class. + */ + buf = wpabuf_alloc(global_op_class_size + 3); + if (!buf) + return 0; + + wpabuf_put_u8(buf, WLAN_EID_SUPPORTED_OPERATING_CLASSES); + /* Will set the length later, putting a placeholder */ + ie_len = wpabuf_put(buf, 1); + wpabuf_put_u8(buf, current); + + for (op = 0; global_op_class[op].op_class; op++) { + if (wpas_op_class_supported(wpa_s, &global_op_class[op])) + wpabuf_put_u8(buf, global_op_class[op].op_class); + } + + *ie_len = wpabuf_len(buf) - 2; + if (*ie_len < 2 || wpabuf_len(buf) > len) { + wpa_printf(MSG_ERROR, + "Failed to add supported operating classes IE"); + res = 0; + } else { + os_memcpy(pos, wpabuf_head(buf), wpabuf_len(buf)); + res = wpabuf_len(buf); + wpa_hexdump_buf(MSG_DEBUG, + "MBO: Added supported operating classes IE", + buf); + } + + wpabuf_free(buf); + return res; +} + + +void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *mbo_ie, + size_t len) +{ + const u8 *pos, *cell_pref = NULL, *reason = NULL; + u8 id, elen; + u16 disallowed_sec = 0; + + if (len <= 4 || WPA_GET_BE24(mbo_ie) != OUI_WFA || + mbo_ie[3] != MBO_OUI_TYPE) + return; + + pos = mbo_ie + 4; + len -= 4; + + while (len >= 2) { + id = *pos++; + elen = *pos++; + len -= 2; + + if (elen > len) + goto fail; + + switch (id) { + case MBO_ATTR_ID_CELL_DATA_PREF: + if (elen != 1) + goto fail; + + if (wpa_s->conf->mbo_cell_capa == + MBO_CELL_CAPA_AVAILABLE) + cell_pref = pos; + else + wpa_printf(MSG_DEBUG, + "MBO: Station does not support Cellular data connection"); + break; + case MBO_ATTR_ID_TRANSITION_REASON: + if (elen != 1) + goto fail; + + reason = pos; + break; + case MBO_ATTR_ID_ASSOC_RETRY_DELAY: + if (elen != 2) + goto fail; + + if (wpa_s->wnm_mode & + WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) { + wpa_printf(MSG_DEBUG, + "MBO: Unexpected association retry delay, BSS is terminating"); + goto fail; + } else if (wpa_s->wnm_mode & + WNM_BSS_TM_REQ_DISASSOC_IMMINENT) { + disallowed_sec = WPA_GET_LE16(pos); + } else { + wpa_printf(MSG_DEBUG, + "MBO: Association retry delay attribute not in disassoc imminent mode"); + } + + break; + case MBO_ATTR_ID_AP_CAPA_IND: + case MBO_ATTR_ID_NON_PREF_CHAN_REPORT: + case MBO_ATTR_ID_CELL_DATA_CAPA: + case MBO_ATTR_ID_ASSOC_DISALLOW: + case MBO_ATTR_ID_TRANSITION_REJECT_REASON: + wpa_printf(MSG_DEBUG, + "MBO: Attribute %d should not be included in BTM Request frame", + id); + break; + default: + wpa_printf(MSG_DEBUG, "MBO: Unknown attribute id %u", + id); + return; + } + + pos += elen; + len -= elen; + } + + if (cell_pref) + wpa_msg(wpa_s, MSG_INFO, MBO_CELL_PREFERENCE "preference=%u", + *cell_pref); + + if (reason) + wpa_msg(wpa_s, MSG_INFO, MBO_TRANSITION_REASON "reason=%u", + *reason); + + if (disallowed_sec && wpa_s->current_bss) + wpa_bss_tmp_disallow(wpa_s, wpa_s->current_bss->bssid, + disallowed_sec); + + return; +fail: + wpa_printf(MSG_DEBUG, "MBO IE parsing failed (id=%u len=%u left=%zu)", + id, elen, len); +} + + +size_t wpas_mbo_ie_bss_trans_reject(struct wpa_supplicant *wpa_s, u8 *pos, + size_t len, + enum mbo_transition_reject_reason reason) +{ + u8 reject_attr[3]; + + reject_attr[0] = MBO_ATTR_ID_TRANSITION_REJECT_REASON; + reject_attr[1] = 1; + reject_attr[2] = reason; + + return mbo_add_ie(pos, len, reject_attr, sizeof(reject_attr)); +} + + +void wpas_mbo_update_cell_capa(struct wpa_supplicant *wpa_s, u8 mbo_cell_capa) +{ + u8 cell_capa[7]; + + if (wpa_s->conf->mbo_cell_capa == mbo_cell_capa) { + wpa_printf(MSG_DEBUG, + "MBO: Cellular capability already set to %u", + mbo_cell_capa); + return; + } + + wpa_s->conf->mbo_cell_capa = mbo_cell_capa; + + cell_capa[0] = WLAN_EID_VENDOR_SPECIFIC; + cell_capa[1] = 5; /* Length */ + WPA_PUT_BE24(cell_capa + 2, OUI_WFA); + cell_capa[5] = MBO_ATTR_ID_CELL_DATA_CAPA; + cell_capa[6] = mbo_cell_capa; + + wpas_mbo_send_wnm_notification(wpa_s, cell_capa, 7); + wpa_supplicant_set_default_scan_ies(wpa_s); +} + + +struct wpabuf * mbo_build_anqp_buf(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss) +{ + struct wpabuf *anqp_buf; + u8 *len_pos; + + if (!wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE)) { + wpa_printf(MSG_INFO, "MBO: " MACSTR + " does not support MBO - cannot request MBO ANQP elements from it", + MAC2STR(bss->bssid)); + return NULL; + } + + anqp_buf = wpabuf_alloc(10); + if (!anqp_buf) + return NULL; + + len_pos = gas_anqp_add_element(anqp_buf, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(anqp_buf, OUI_WFA); + wpabuf_put_u8(anqp_buf, MBO_ANQP_OUI_TYPE); + + wpabuf_put_u8(anqp_buf, MBO_ANQP_SUBTYPE_CELL_CONN_PREF); + gas_anqp_set_element_len(anqp_buf, len_pos); + + return anqp_buf; +} diff --git a/contrib/wpa/wpa_supplicant/mesh.c b/contrib/wpa/wpa_supplicant/mesh.c index 77f708b42daa..d67d3b2aa390 100644 --- a/contrib/wpa/wpa_supplicant/mesh.c +++ b/contrib/wpa/wpa_supplicant/mesh.c @@ -66,9 +66,11 @@ void wpa_supplicant_mesh_iface_deinit(struct wpa_supplicant *wpa_s, } -static struct mesh_conf * mesh_config_create(struct wpa_ssid *ssid) +static struct mesh_conf * mesh_config_create(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) { struct mesh_conf *conf; + int cipher; conf = os_zalloc(sizeof(struct mesh_conf)); if (!conf) @@ -82,6 +84,33 @@ static struct mesh_conf * mesh_config_create(struct wpa_ssid *ssid) MESH_CONF_SEC_AMPE; else conf->security |= MESH_CONF_SEC_NONE; + conf->ieee80211w = ssid->ieee80211w; + if (conf->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT) { + if (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_BIP) + conf->ieee80211w = wpa_s->conf->pmf; + else + conf->ieee80211w = NO_MGMT_FRAME_PROTECTION; + } + + cipher = wpa_pick_pairwise_cipher(ssid->pairwise_cipher, 0); + if (cipher < 0 || cipher == WPA_CIPHER_TKIP) { + wpa_msg(wpa_s, MSG_INFO, "mesh: Invalid pairwise cipher"); + os_free(conf); + return NULL; + } + conf->pairwise_cipher = cipher; + + cipher = wpa_pick_group_cipher(ssid->group_cipher); + if (cipher < 0 || cipher == WPA_CIPHER_TKIP || + cipher == WPA_CIPHER_GTK_NOT_USED) { + wpa_msg(wpa_s, MSG_INFO, "mesh: Invalid group cipher"); + os_free(conf); + return NULL; + } + + conf->group_cipher = cipher; + if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) + conf->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC; /* defaults */ conf->mesh_pp_id = MESH_PATH_PROTOCOL_HWMP; @@ -149,6 +178,7 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s, ifmsh->bss[0] = bss = os_zalloc(sizeof(struct hostapd_data)); if (!bss) goto out_free; + dl_list_init(&bss->nr_db); os_memcpy(bss->own_addr, wpa_s->own_addr, ETH_ALEN); bss->driver = wpa_s->driver; @@ -175,24 +205,41 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s, wpa_s->conf->dot11RSNASAERetransPeriod; os_strlcpy(bss->conf->iface, wpa_s->ifname, sizeof(bss->conf->iface)); - mconf = mesh_config_create(ssid); + mconf = mesh_config_create(wpa_s, ssid); if (!mconf) goto out_free; ifmsh->mconf = mconf; /* need conf->hw_mode for supported rates. */ - if (ssid->frequency == 0) { - conf->hw_mode = HOSTAPD_MODE_IEEE80211G; - conf->channel = 1; - } else { - conf->hw_mode = ieee80211_freq_to_chan(ssid->frequency, - &conf->channel); - } + conf->hw_mode = ieee80211_freq_to_chan(ssid->frequency, &conf->channel); if (conf->hw_mode == NUM_HOSTAPD_MODES) { wpa_printf(MSG_ERROR, "Unsupported mesh mode frequency: %d MHz", ssid->frequency); goto out_free; } + if (ssid->ht40) + conf->secondary_channel = ssid->ht40; + if (conf->hw_mode == HOSTAPD_MODE_IEEE80211A && ssid->vht) { + conf->vht_oper_chwidth = ssid->max_oper_chwidth; + switch (conf->vht_oper_chwidth) { + case VHT_CHANWIDTH_80MHZ: + case VHT_CHANWIDTH_80P80MHZ: + ieee80211_freq_to_chan( + ssid->frequency, + &conf->vht_oper_centr_freq_seg0_idx); + conf->vht_oper_centr_freq_seg0_idx += ssid->ht40 * 2; + break; + case VHT_CHANWIDTH_160MHZ: + ieee80211_freq_to_chan( + ssid->frequency, + &conf->vht_oper_centr_freq_seg0_idx); + conf->vht_oper_centr_freq_seg0_idx += ssid->ht40 * 2; + conf->vht_oper_centr_freq_seg0_idx += 40 / 5; + break; + } + ieee80211_freq_to_chan(ssid->vht_center_freq2, + &conf->vht_oper_centr_freq_seg1_idx); + } if (ssid->mesh_basic_rates == NULL) { /* @@ -318,16 +365,47 @@ int wpa_supplicant_join_mesh(struct wpa_supplicant *wpa_s, wpa_supplicant_mesh_deinit(wpa_s); + wpa_s->pairwise_cipher = WPA_CIPHER_NONE; + wpa_s->group_cipher = WPA_CIPHER_NONE; + wpa_s->mgmt_group_cipher = 0; + os_memset(¶ms, 0, sizeof(params)); params.meshid = ssid->ssid; params.meshid_len = ssid->ssid_len; ibss_mesh_setup_freq(wpa_s, ssid, ¶ms.freq); wpa_s->mesh_ht_enabled = !!params.freq.ht_enabled; + wpa_s->mesh_vht_enabled = !!params.freq.vht_enabled; + if (params.freq.ht_enabled && params.freq.sec_channel_offset) + ssid->ht40 = params.freq.sec_channel_offset; + if (wpa_s->mesh_vht_enabled) { + ssid->vht = 1; + switch (params.freq.bandwidth) { + case 80: + if (params.freq.center_freq2) { + ssid->max_oper_chwidth = VHT_CHANWIDTH_80P80MHZ; + ssid->vht_center_freq2 = + params.freq.center_freq2; + } else { + ssid->max_oper_chwidth = VHT_CHANWIDTH_80MHZ; + } + break; + case 160: + ssid->max_oper_chwidth = VHT_CHANWIDTH_160MHZ; + break; + default: + ssid->max_oper_chwidth = VHT_CHANWIDTH_USE_HT; + break; + } + } if (ssid->beacon_int > 0) params.beacon_int = ssid->beacon_int; else if (wpa_s->conf->beacon_int > 0) params.beacon_int = wpa_s->conf->beacon_int; - params.max_peer_links = wpa_s->conf->max_peer_links; + if (ssid->dtim_period > 0) + params.dtim_period = ssid->dtim_period; + else if (wpa_s->conf->dtim_period > 0) + params.dtim_period = wpa_s->conf->dtim_period; + params.conf.max_peer_links = wpa_s->conf->max_peer_links; if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) { params.flags |= WPA_DRIVER_MESH_FLAG_SAE_AUTH; @@ -337,10 +415,10 @@ int wpa_supplicant_join_mesh(struct wpa_supplicant *wpa_s, if (wpa_s->conf->user_mpm) { params.flags |= WPA_DRIVER_MESH_FLAG_USER_MPM; - params.conf.flags &= ~WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS; + params.conf.auto_plinks = 0; } else { params.flags |= WPA_DRIVER_MESH_FLAG_DRIVER_MPM; - params.conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS; + params.conf.auto_plinks = 1; } params.conf.peer_link_timeout = wpa_s->conf->mesh_max_inactivity; @@ -351,21 +429,32 @@ int wpa_supplicant_join_mesh(struct wpa_supplicant *wpa_s, goto out; } + if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) { + wpa_s->pairwise_cipher = wpa_s->mesh_rsn->pairwise_cipher; + wpa_s->group_cipher = wpa_s->mesh_rsn->group_cipher; + wpa_s->mgmt_group_cipher = wpa_s->mesh_rsn->mgmt_group_cipher; + } + if (wpa_s->ifmsh) { params.ies = wpa_s->ifmsh->mconf->rsn_ie; params.ie_len = wpa_s->ifmsh->mconf->rsn_ie_len; params.basic_rates = wpa_s->ifmsh->basic_rates; + params.conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_HT_OP_MODE; + params.conf.ht_opmode = wpa_s->ifmsh->bss[0]->iface->ht_op_mode; } wpa_msg(wpa_s, MSG_INFO, "joining mesh %s", wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); ret = wpa_drv_join_mesh(wpa_s, ¶ms); if (ret) - wpa_msg(wpa_s, MSG_ERROR, "mesh join error=%d\n", ret); + wpa_msg(wpa_s, MSG_ERROR, "mesh join error=%d", ret); /* hostapd sets the interface down until we associate */ wpa_drv_set_operstate(wpa_s, 1); + if (!ret) + wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); + out: return ret; } @@ -535,9 +624,22 @@ int wpas_mesh_add_interface(struct wpa_supplicant *wpa_s, char *ifname, if (!mesh_wpa_s) { wpa_printf(MSG_ERROR, "mesh: Failed to create new wpa_supplicant interface"); - wpa_supplicant_remove_iface(wpa_s->global, wpa_s, 0); + wpa_drv_if_remove(wpa_s, WPA_IF_MESH, ifname); return -1; } mesh_wpa_s->mesh_if_created = 1; return 0; } + + +int wpas_mesh_peer_remove(struct wpa_supplicant *wpa_s, const u8 *addr) +{ + return mesh_mpm_close_peer(wpa_s, addr); +} + + +int wpas_mesh_peer_add(struct wpa_supplicant *wpa_s, const u8 *addr, + int duration) +{ + return mesh_mpm_connect_peer(wpa_s, addr, duration); +} diff --git a/contrib/wpa/wpa_supplicant/mesh.h b/contrib/wpa/wpa_supplicant/mesh.h index 3cb7f1b1364f..7317083c99cd 100644 --- a/contrib/wpa/wpa_supplicant/mesh.h +++ b/contrib/wpa/wpa_supplicant/mesh.h @@ -18,6 +18,9 @@ int wpas_mesh_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end); int wpas_mesh_add_interface(struct wpa_supplicant *wpa_s, char *ifname, size_t len); +int wpas_mesh_peer_remove(struct wpa_supplicant *wpa_s, const u8 *addr); +int wpas_mesh_peer_add(struct wpa_supplicant *wpa_s, const u8 *addr, + int duration); #ifdef CONFIG_MESH diff --git a/contrib/wpa/wpa_supplicant/mesh_mpm.c b/contrib/wpa/wpa_supplicant/mesh_mpm.c index f81b88c89401..d14c7e3b2045 100644 --- a/contrib/wpa/wpa_supplicant/mesh_mpm.c +++ b/contrib/wpa/wpa_supplicant/mesh_mpm.c @@ -14,17 +14,18 @@ #include "ap/hostapd.h" #include "ap/sta_info.h" #include "ap/ieee802_11.h" +#include "ap/wpa_auth.h" #include "wpa_supplicant_i.h" #include "driver_i.h" #include "mesh_mpm.h" #include "mesh_rsn.h" struct mesh_peer_mgmt_ie { - const u8 *proto_id; - const u8 *llid; - const u8 *plid; - const u8 *reason; - const u8 *pmk; + const u8 *proto_id; /* Mesh Peering Protocol Identifier (2 octets) */ + const u8 *llid; /* Local Link ID (2 octets) */ + const u8 *plid; /* Peer Link ID (conditional, 2 octets) */ + const u8 *reason; /* Reason Code (conditional, 2 octets) */ + const u8 *chosen_pmk; /* Chosen PMK (optional, 16 octets) */ }; static void plink_timer(void *eloop_ctx, void *user_data); @@ -34,18 +35,17 @@ enum plink_event { PLINK_UNDEFINED, OPN_ACPT, OPN_RJCT, - OPN_IGNR, CNF_ACPT, CNF_RJCT, - CNF_IGNR, CLS_ACPT, - CLS_IGNR + REQ_RJCT }; static const char * const mplstate[] = { - [PLINK_LISTEN] = "LISTEN", - [PLINK_OPEN_SENT] = "OPEN_SENT", - [PLINK_OPEN_RCVD] = "OPEN_RCVD", + [0] = "UNINITIALIZED", + [PLINK_IDLE] = "IDLE", + [PLINK_OPN_SNT] = "OPN_SNT", + [PLINK_OPN_RCVD] = "OPN_RCVD", [PLINK_CNF_RCVD] = "CNF_RCVD", [PLINK_ESTAB] = "ESTAB", [PLINK_HOLDING] = "HOLDING", @@ -56,12 +56,10 @@ static const char * const mplevent[] = { [PLINK_UNDEFINED] = "UNDEFINED", [OPN_ACPT] = "OPN_ACPT", [OPN_RJCT] = "OPN_RJCT", - [OPN_IGNR] = "OPN_IGNR", [CNF_ACPT] = "CNF_ACPT", [CNF_RJCT] = "CNF_RJCT", - [CNF_IGNR] = "CNF_IGNR", [CLS_ACPT] = "CLS_ACPT", - [CLS_IGNR] = "CLS_IGNR" + [REQ_RJCT] = "REQ_RJCT", }; @@ -72,10 +70,10 @@ static int mesh_mpm_parse_peer_mgmt(struct wpa_supplicant *wpa_s, { os_memset(mpm_ie, 0, sizeof(*mpm_ie)); - /* remove optional PMK at end */ - if (len >= 16) { - len -= 16; - mpm_ie->pmk = ie + len - 16; + /* Remove optional Chosen PMK field at end */ + if (len >= SAE_PMKID_LEN) { + mpm_ie->chosen_pmk = ie + len - SAE_PMKID_LEN; + len -= SAE_PMKID_LEN; } if ((action_field == PLINK_OPEN && len != 4) || @@ -101,8 +99,8 @@ static int mesh_mpm_parse_peer_mgmt(struct wpa_supplicant *wpa_s, len -= 2; } - /* plid, present for confirm, and possibly close */ - if (len) + /* Peer Link ID, present for confirm, and possibly close */ + if (len >= 2) mpm_ie->plid = ie; return 0; @@ -193,12 +191,13 @@ static void mesh_mpm_init_link(struct wpa_supplicant *wpa_s, sta->my_lid = llid; sta->peer_lid = 0; + sta->peer_aid = 0; /* * We do not use wpa_mesh_set_plink_state() here because there is no * entry in kernel yet. */ - sta->plink_state = PLINK_LISTEN; + sta->plink_state = PLINK_IDLE; } @@ -212,9 +211,6 @@ static void mesh_mpm_send_plink_action(struct wpa_supplicant *wpa_s, struct hostapd_data *bss = ifmsh->bss[0]; struct mesh_conf *conf = ifmsh->mconf; u8 supp_rates[2 + 2 + 32]; -#ifdef CONFIG_IEEE80211N - u8 ht_capa_oper[2 + 26 + 2 + 22]; -#endif /* CONFIG_IEEE80211N */ u8 *pos, *cat; u8 ie_len, add_plid = 0; int ret; @@ -239,6 +235,12 @@ static void mesh_mpm_send_plink_action(struct wpa_supplicant *wpa_s, 2 + 22; /* HT operation */ } #endif /* CONFIG_IEEE80211N */ +#ifdef CONFIG_IEEE80211AC + if (type != PLINK_CLOSE && wpa_s->mesh_vht_enabled) { + buf_len += 2 + 12 + /* VHT Capabilities */ + 2 + 5; /* VHT Operation */ + } +#endif /* CONFIG_IEEE80211AC */ if (type != PLINK_CLOSE) buf_len += conf->rsn_ie_len; /* RSN IE */ @@ -258,7 +260,7 @@ static void mesh_mpm_send_plink_action(struct wpa_supplicant *wpa_s, /* aid */ if (type == PLINK_CONFIRM) - wpabuf_put_le16(buf, sta->peer_lid); + wpabuf_put_le16(buf, sta->aid); /* IE: supp + ext. supp rates */ pos = hostapd_eid_supp_rates(bss, supp_rates); @@ -285,7 +287,8 @@ static void mesh_mpm_send_plink_action(struct wpa_supplicant *wpa_s, /* TODO: Add Connected to Mesh Gate/AS subfields */ wpabuf_put_u8(buf, info); /* always forwarding & accepting plinks for now */ - wpabuf_put_u8(buf, 0x1 | 0x8); + wpabuf_put_u8(buf, MESH_CAP_ACCEPT_ADDITIONAL_PEER | + MESH_CAP_FORWARDING); } else { /* Peer closing frame */ /* IE: Mesh ID */ wpabuf_put_u8(buf, WLAN_EID_MESH_ID); @@ -334,11 +337,22 @@ static void mesh_mpm_send_plink_action(struct wpa_supplicant *wpa_s, #ifdef CONFIG_IEEE80211N if (type != PLINK_CLOSE && wpa_s->mesh_ht_enabled) { + u8 ht_capa_oper[2 + 26 + 2 + 22]; + pos = hostapd_eid_ht_capabilities(bss, ht_capa_oper); pos = hostapd_eid_ht_operation(bss, pos); wpabuf_put_data(buf, ht_capa_oper, pos - ht_capa_oper); } #endif /* CONFIG_IEEE80211N */ +#ifdef CONFIG_IEEE80211AC + if (type != PLINK_CLOSE && wpa_s->mesh_vht_enabled) { + u8 vht_capa_oper[2 + 12 + 2 + 5]; + + pos = hostapd_eid_vht_capabilities(bss, vht_capa_oper, 0); + pos = hostapd_eid_vht_operation(bss, pos); + wpabuf_put_data(buf, vht_capa_oper, pos - vht_capa_oper); + } +#endif /* CONFIG_IEEE80211AC */ if (ampe && mesh_rsn_protect_frame(wpa_s->mesh_rsn, sta, cat, buf)) { wpa_msg(wpa_s, MSG_INFO, @@ -346,6 +360,9 @@ static void mesh_mpm_send_plink_action(struct wpa_supplicant *wpa_s, goto fail; } + wpa_msg(wpa_s, MSG_DEBUG, "Mesh MPM: Sending peering frame type %d to " + MACSTR " (my_lid=0x%x peer_lid=0x%x)", + type, MAC2STR(sta->addr), sta->my_lid, sta->peer_lid); ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, sta->addr, wpa_s->own_addr, wpa_s->own_addr, wpabuf_head(buf), wpabuf_len(buf), 0); @@ -366,15 +383,17 @@ void wpa_mesh_set_plink_state(struct wpa_supplicant *wpa_s, struct hostapd_sta_add_params params; int ret; + wpa_msg(wpa_s, MSG_DEBUG, "MPM set " MACSTR " from %s into %s", + MAC2STR(sta->addr), mplstate[sta->plink_state], + mplstate[state]); sta->plink_state = state; os_memset(¶ms, 0, sizeof(params)); params.addr = sta->addr; params.plink_state = state; + params.peer_aid = sta->peer_aid; params.set = 1; - wpa_msg(wpa_s, MSG_DEBUG, "MPM set " MACSTR " into %s", - MAC2STR(sta->addr), mplstate[state]); ret = wpa_drv_sta_add(wpa_s, ¶ms); if (ret) { wpa_msg(wpa_s, MSG_ERROR, "Driver failed to set " MACSTR @@ -400,10 +419,11 @@ static void plink_timer(void *eloop_ctx, void *user_data) struct sta_info *sta = user_data; u16 reason = 0; struct mesh_conf *conf = wpa_s->ifmsh->mconf; + struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; switch (sta->plink_state) { - case PLINK_OPEN_RCVD: - case PLINK_OPEN_SENT: + case PLINK_OPN_RCVD: + case PLINK_OPN_SNT: /* retry timer */ if (sta->mpm_retries < conf->dot11MeshMaxRetries) { eloop_register_timeout( @@ -429,6 +449,13 @@ static void plink_timer(void *eloop_ctx, void *user_data) break; case PLINK_HOLDING: /* holding timer */ + + if (sta->mesh_sae_pmksa_caching) { + wpa_printf(MSG_DEBUG, "MPM: Peer " MACSTR + " looks like it does not support mesh SAE PMKSA caching, so remove the cached entry for it", + MAC2STR(sta->addr)); + wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr); + } mesh_mpm_fsm_restart(wpa_s, sta); break; default: @@ -453,8 +480,8 @@ mesh_mpm_plink_open(struct wpa_supplicant *wpa_s, struct sta_info *sta, } -int mesh_mpm_plink_close(struct hostapd_data *hapd, - struct sta_info *sta, void *ctx) +static int mesh_mpm_plink_close(struct hostapd_data *hapd, struct sta_info *sta, + void *ctx) { struct wpa_supplicant *wpa_s = ctx; int reason = WLAN_REASON_MESH_PEERING_CANCELLED; @@ -472,6 +499,85 @@ int mesh_mpm_plink_close(struct hostapd_data *hapd, } +int mesh_mpm_close_peer(struct wpa_supplicant *wpa_s, const u8 *addr) +{ + struct hostapd_data *hapd; + struct sta_info *sta; + + if (!wpa_s->ifmsh) { + wpa_msg(wpa_s, MSG_INFO, "Mesh is not prepared yet"); + return -1; + } + + hapd = wpa_s->ifmsh->bss[0]; + sta = ap_get_sta(hapd, addr); + if (!sta) { + wpa_msg(wpa_s, MSG_INFO, "No such mesh peer"); + return -1; + } + + return mesh_mpm_plink_close(hapd, sta, wpa_s) == 0 ? 0 : -1; +} + + +static void peer_add_timer(void *eloop_ctx, void *user_data) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; + + os_memset(hapd->mesh_required_peer, 0, ETH_ALEN); +} + + +int mesh_mpm_connect_peer(struct wpa_supplicant *wpa_s, const u8 *addr, + int duration) +{ + struct wpa_ssid *ssid = wpa_s->current_ssid; + struct hostapd_data *hapd; + struct sta_info *sta; + struct mesh_conf *conf; + + if (!wpa_s->ifmsh) { + wpa_msg(wpa_s, MSG_INFO, "Mesh is not prepared yet"); + return -1; + } + + if (!ssid || !ssid->no_auto_peer) { + wpa_msg(wpa_s, MSG_INFO, + "This command is available only with no_auto_peer mesh network"); + return -1; + } + + hapd = wpa_s->ifmsh->bss[0]; + conf = wpa_s->ifmsh->mconf; + + sta = ap_get_sta(hapd, addr); + if (!sta) { + wpa_msg(wpa_s, MSG_INFO, "No such mesh peer"); + return -1; + } + + if ((PLINK_OPN_SNT <= sta->plink_state && + sta->plink_state <= PLINK_ESTAB) || + (sta->sae && sta->sae->state > SAE_NOTHING)) { + wpa_msg(wpa_s, MSG_INFO, + "Specified peer is connecting/connected"); + return -1; + } + + if (conf->security == MESH_CONF_SEC_NONE) { + mesh_mpm_plink_open(wpa_s, sta, PLINK_OPN_SNT); + } else { + mesh_rsn_auth_sae_sta(wpa_s, sta); + os_memcpy(hapd->mesh_required_peer, addr, ETH_ALEN); + eloop_register_timeout(duration == -1 ? 10 : duration, 0, + peer_add_timer, wpa_s, NULL); + } + + return 0; +} + + void mesh_mpm_deinit(struct wpa_supplicant *wpa_s, struct hostapd_iface *ifmsh) { struct hostapd_data *hapd = ifmsh->bss[0]; @@ -481,6 +587,7 @@ void mesh_mpm_deinit(struct wpa_supplicant *wpa_s, struct hostapd_iface *ifmsh) hapd->num_plinks = 0; hostapd_free_stas(hapd); + eloop_cancel_timeout(peer_add_timer, wpa_s, NULL); } @@ -522,7 +629,7 @@ void mesh_mpm_auth_peer(struct wpa_supplicant *wpa_s, const u8 *addr) if (!sta->my_lid) mesh_mpm_init_link(wpa_s, sta); - mesh_mpm_plink_open(wpa_s, sta, PLINK_OPEN_SENT); + mesh_mpm_plink_open(wpa_s, sta, PLINK_OPN_SNT); } /* @@ -541,6 +648,14 @@ static struct sta_info * mesh_mpm_add_peer(struct wpa_supplicant *wpa_s, struct sta_info *sta; int ret; + if (elems->mesh_config_len >= 7 && + !(elems->mesh_config[6] & MESH_CAP_ACCEPT_ADDITIONAL_PEER)) { + wpa_msg(wpa_s, MSG_DEBUG, + "mesh: Ignore a crowded peer " MACSTR, + MAC2STR(addr)); + return NULL; + } + sta = ap_get_sta(data, addr); if (!sta) { sta = ap_sta_add(data, addr); @@ -548,28 +663,45 @@ static struct sta_info * mesh_mpm_add_peer(struct wpa_supplicant *wpa_s, return NULL; } + /* Set WMM by default since Mesh STAs are QoS STAs */ + sta->flags |= WLAN_STA_WMM; + /* initialize sta */ if (copy_supp_rates(wpa_s, sta, elems)) { ap_free_sta(data, sta); return NULL; } - mesh_mpm_init_link(wpa_s, sta); + if (!sta->my_lid) + mesh_mpm_init_link(wpa_s, sta); #ifdef CONFIG_IEEE80211N copy_sta_ht_capab(data, sta, elems->ht_capabilities); update_ht_state(data, sta); #endif /* CONFIG_IEEE80211N */ +#ifdef CONFIG_IEEE80211AC + copy_sta_vht_capab(data, sta, elems->vht_capabilities); + set_sta_vht_opmode(data, sta, elems->vht_opmode_notif); +#endif /* CONFIG_IEEE80211AC */ + + if (hostapd_get_aid(data, sta) < 0) { + wpa_msg(wpa_s, MSG_ERROR, "No AIDs available"); + ap_free_sta(data, sta); + return NULL; + } + /* insert into driver */ os_memset(¶ms, 0, sizeof(params)); params.supp_rates = sta->supported_rates; params.supp_rates_len = sta->supported_rates_len; params.addr = addr; params.plink_state = sta->plink_state; - params.aid = sta->peer_lid; + params.aid = sta->aid; + params.peer_aid = sta->peer_aid; params.listen_interval = 100; params.ht_capabilities = sta->ht_capabilities; + params.vht_capabilities = sta->vht_capabilities; params.flags |= WPA_STA_WMM; params.flags_mask |= WPA_STA_AUTHENTICATED; if (conf->security == MESH_CONF_SEC_NONE) { @@ -605,7 +737,9 @@ void wpa_mesh_new_mesh_peer(struct wpa_supplicant *wpa_s, const u8 *addr, if (!sta) return; - if (ssid && ssid->no_auto_peer) { + if (ssid && ssid->no_auto_peer && + (is_zero_ether_addr(data->mesh_required_peer) || + os_memcmp(data->mesh_required_peer, addr, ETH_ALEN) != 0)) { wpa_msg(wpa_s, MSG_INFO, "will not initiate new peer link with " MACSTR " because of no_auto_peer", MAC2STR(addr)); if (data->mesh_pending_auth) { @@ -634,10 +768,13 @@ void wpa_mesh_new_mesh_peer(struct wpa_supplicant *wpa_s, const u8 *addr, return; } - if (conf->security == MESH_CONF_SEC_NONE) - mesh_mpm_plink_open(wpa_s, sta, PLINK_OPEN_SENT); - else + if (conf->security == MESH_CONF_SEC_NONE) { + if (sta->plink_state < PLINK_OPN_SNT || + sta->plink_state > PLINK_ESTAB) + mesh_mpm_plink_open(wpa_s, sta, PLINK_OPN_SNT); + } else { mesh_rsn_auth_sae_sta(wpa_s, sta); + } } @@ -664,64 +801,85 @@ static void mesh_mpm_plink_estab(struct wpa_supplicant *wpa_s, MAC2STR(sta->addr)); if (conf->security & MESH_CONF_SEC_AMPE) { - wpa_drv_set_key(wpa_s, WPA_ALG_CCMP, sta->addr, 0, 0, - seq, sizeof(seq), sta->mtk, sizeof(sta->mtk)); - wpa_drv_set_key(wpa_s, WPA_ALG_CCMP, sta->addr, 1, 0, - seq, sizeof(seq), - sta->mgtk, sizeof(sta->mgtk)); - wpa_drv_set_key(wpa_s, WPA_ALG_IGTK, sta->addr, 4, 0, - seq, sizeof(seq), - sta->mgtk, sizeof(sta->mgtk)); + wpa_hexdump_key(MSG_DEBUG, "mesh: MTK", sta->mtk, sta->mtk_len); + wpa_drv_set_key(wpa_s, wpa_cipher_to_alg(conf->pairwise_cipher), + sta->addr, 0, 0, seq, sizeof(seq), + sta->mtk, sta->mtk_len); - wpa_hexdump_key(MSG_DEBUG, "mtk:", sta->mtk, sizeof(sta->mtk)); - wpa_hexdump_key(MSG_DEBUG, "mgtk:", - sta->mgtk, sizeof(sta->mgtk)); + wpa_hexdump_key(MSG_DEBUG, "mesh: RX MGTK Key RSC", + sta->mgtk_rsc, sizeof(sta->mgtk_rsc)); + wpa_hexdump_key(MSG_DEBUG, "mesh: RX MGTK", + sta->mgtk, sta->mgtk_len); + wpa_drv_set_key(wpa_s, wpa_cipher_to_alg(conf->group_cipher), + sta->addr, sta->mgtk_key_id, 0, + sta->mgtk_rsc, sizeof(sta->mgtk_rsc), + sta->mgtk, sta->mgtk_len); + + if (sta->igtk_len) { + wpa_hexdump_key(MSG_DEBUG, "mesh: RX IGTK Key RSC", + sta->igtk_rsc, sizeof(sta->igtk_rsc)); + wpa_hexdump_key(MSG_DEBUG, "mesh: RX IGTK", + sta->igtk, sta->igtk_len); + wpa_drv_set_key( + wpa_s, + wpa_cipher_to_alg(conf->mgmt_group_cipher), + sta->addr, sta->igtk_key_id, 0, + sta->igtk_rsc, sizeof(sta->igtk_rsc), + sta->igtk, sta->igtk_len); + } } wpa_mesh_set_plink_state(wpa_s, sta, PLINK_ESTAB); hapd->num_plinks++; sta->flags |= WLAN_STA_ASSOC; + sta->mesh_sae_pmksa_caching = 0; + eloop_cancel_timeout(peer_add_timer, wpa_s, NULL); + peer_add_timer(wpa_s, NULL); eloop_cancel_timeout(plink_timer, wpa_s, sta); /* Send ctrl event */ - wpa_msg_ctrl(wpa_s, MSG_INFO, MESH_PEER_CONNECTED MACSTR, - MAC2STR(sta->addr)); + wpa_msg(wpa_s, MSG_INFO, MESH_PEER_CONNECTED MACSTR, + MAC2STR(sta->addr)); } static void mesh_mpm_fsm(struct wpa_supplicant *wpa_s, struct sta_info *sta, - enum plink_event event) + enum plink_event event, u16 reason) { struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; struct mesh_conf *conf = wpa_s->ifmsh->mconf; - u16 reason = 0; wpa_msg(wpa_s, MSG_DEBUG, "MPM " MACSTR " state %s event %s", MAC2STR(sta->addr), mplstate[sta->plink_state], mplevent[event]); switch (sta->plink_state) { - case PLINK_LISTEN: + case PLINK_IDLE: switch (event) { case CLS_ACPT: mesh_mpm_fsm_restart(wpa_s, sta); break; case OPN_ACPT: - mesh_mpm_plink_open(wpa_s, sta, PLINK_OPEN_RCVD); + mesh_mpm_plink_open(wpa_s, sta, PLINK_OPN_RCVD); mesh_mpm_send_plink_action(wpa_s, sta, PLINK_CONFIRM, 0); break; + case REQ_RJCT: + mesh_mpm_send_plink_action(wpa_s, sta, + PLINK_CLOSE, reason); + break; default: break; } break; - case PLINK_OPEN_SENT: + case PLINK_OPN_SNT: switch (event) { case OPN_RJCT: case CNF_RJCT: - reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION; + if (!reason) + reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION; /* fall-through */ case CLS_ACPT: wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING); @@ -736,12 +894,13 @@ static void mesh_mpm_fsm(struct wpa_supplicant *wpa_s, struct sta_info *sta, break; case OPN_ACPT: /* retry timer is left untouched */ - wpa_mesh_set_plink_state(wpa_s, sta, PLINK_OPEN_RCVD); + wpa_mesh_set_plink_state(wpa_s, sta, PLINK_OPN_RCVD); mesh_mpm_send_plink_action(wpa_s, sta, PLINK_CONFIRM, 0); break; case CNF_ACPT: wpa_mesh_set_plink_state(wpa_s, sta, PLINK_CNF_RCVD); + eloop_cancel_timeout(plink_timer, wpa_s, sta); eloop_register_timeout( conf->dot11MeshConfirmTimeout / 1000, (conf->dot11MeshConfirmTimeout % 1000) * 1000, @@ -751,11 +910,12 @@ static void mesh_mpm_fsm(struct wpa_supplicant *wpa_s, struct sta_info *sta, break; } break; - case PLINK_OPEN_RCVD: + case PLINK_OPN_RCVD: switch (event) { case OPN_RJCT: case CNF_RJCT: - reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION; + if (!reason) + reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION; /* fall-through */ case CLS_ACPT: wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING); @@ -786,7 +946,8 @@ static void mesh_mpm_fsm(struct wpa_supplicant *wpa_s, struct sta_info *sta, switch (event) { case OPN_RJCT: case CNF_RJCT: - reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION; + if (!reason) + reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION; /* fall-through */ case CLS_ACPT: wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING); @@ -801,6 +962,8 @@ static void mesh_mpm_fsm(struct wpa_supplicant *wpa_s, struct sta_info *sta, PLINK_CLOSE, reason); break; case OPN_ACPT: + if (conf->security & MESH_CONF_SEC_AMPE) + mesh_rsn_derive_mtk(wpa_s, sta); mesh_mpm_plink_estab(wpa_s, sta); mesh_mpm_send_plink_action(wpa_s, sta, PLINK_CONFIRM, 0); @@ -811,9 +974,12 @@ static void mesh_mpm_fsm(struct wpa_supplicant *wpa_s, struct sta_info *sta, break; case PLINK_ESTAB: switch (event) { + case OPN_RJCT: + case CNF_RJCT: case CLS_ACPT: wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING); - reason = WLAN_REASON_MESH_CLOSE_RCVD; + if (!reason) + reason = WLAN_REASON_MESH_CLOSE_RCVD; eloop_register_timeout( conf->dot11MeshHoldingTimeout / 1000, @@ -825,9 +991,8 @@ static void mesh_mpm_fsm(struct wpa_supplicant *wpa_s, struct sta_info *sta, " closed with reason %d", MAC2STR(sta->addr), reason); - wpa_msg_ctrl(wpa_s, MSG_INFO, - MESH_PEER_DISCONNECTED MACSTR, - MAC2STR(sta->addr)); + wpa_msg(wpa_s, MSG_INFO, MESH_PEER_DISCONNECTED MACSTR, + MAC2STR(sta->addr)); hapd->num_plinks--; @@ -875,13 +1040,14 @@ void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s, struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; struct mesh_conf *mconf = wpa_s->ifmsh->mconf; struct sta_info *sta; - u16 plid = 0, llid = 0; + u16 plid = 0, llid = 0, aid = 0; enum plink_event event; struct ieee802_11_elems elems; struct mesh_peer_mgmt_ie peer_mgmt_ie; const u8 *ies; size_t ie_len; int ret; + u16 reason = 0; if (mgmt->u.action.category != WLAN_ACTION_SELF_PROTECTED) return; @@ -912,7 +1078,8 @@ void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s, ie_len -= 2; } if (action_field == PLINK_CONFIRM) { - wpa_printf(MSG_DEBUG, "MPM: AID 0x%x", WPA_GET_LE16(ies)); + aid = WPA_GET_LE16(ies); + wpa_printf(MSG_DEBUG, "MPM: AID 0x%x", aid); ies += 2; /* aid */ ie_len -= 2; } @@ -956,6 +1123,10 @@ void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s, llid = WPA_GET_LE16(peer_mgmt_ie.plid); wpa_printf(MSG_DEBUG, "MPM: plid=0x%x llid=0x%x", plid, llid); + if (action_field == PLINK_CLOSE) + wpa_printf(MSG_DEBUG, "MPM: close reason=%u", + WPA_GET_LE16(peer_mgmt_ie.reason)); + sta = ap_get_sta(hapd, mgmt->sa); /* @@ -963,7 +1134,8 @@ void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s, * open mesh, then go ahead and add the peer before proceeding. */ if (!sta && action_field == PLINK_OPEN && - !(mconf->security & MESH_CONF_SEC_AMPE)) + (!(mconf->security & MESH_CONF_SEC_AMPE) || + wpa_auth_pmksa_get(hapd->wpa_auth, mgmt->sa))) sta = mesh_mpm_add_peer(wpa_s, mgmt->sa, &elems); if (!sta) { @@ -982,12 +1154,24 @@ void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s, if (!sta->my_lid) mesh_mpm_init_link(wpa_s, sta); - if ((mconf->security & MESH_CONF_SEC_AMPE) && - mesh_rsn_process_ampe(wpa_s, sta, &elems, - &mgmt->u.action.category, - ies, ie_len)) { - wpa_printf(MSG_DEBUG, "MPM: RSN process rejected frame"); - return; + if (mconf->security & MESH_CONF_SEC_AMPE) { + int res; + + res = mesh_rsn_process_ampe(wpa_s, sta, &elems, + &mgmt->u.action.category, + peer_mgmt_ie.chosen_pmk, + ies, ie_len); + if (res) { + wpa_printf(MSG_DEBUG, + "MPM: RSN process rejected frame (res=%d)", + res); + if (action_field == PLINK_OPEN && res == -2) { + /* AES-SIV decryption failed */ + mesh_mpm_fsm(wpa_s, sta, OPN_RJCT, + WLAN_REASON_MESH_INVALID_GTK); + } + return; + } } if (sta->plink_state == PLINK_BLOCKED) { @@ -999,12 +1183,16 @@ void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s, switch (action_field) { case PLINK_OPEN: if (plink_free_count(hapd) == 0) { - event = OPN_IGNR; + event = REQ_RJCT; + reason = WLAN_REASON_MESH_MAX_PEERS; wpa_printf(MSG_INFO, "MPM: Peer link num over quota(%d)", hapd->max_plinks); } else if (sta->peer_lid && sta->peer_lid != plid) { - event = OPN_IGNR; + wpa_printf(MSG_DEBUG, + "MPM: peer_lid mismatch: 0x%x != 0x%x", + sta->peer_lid, plid); + return; /* no FSM event */ } else { sta->peer_lid = plid; event = OPN_ACPT; @@ -1012,16 +1200,21 @@ void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s, break; case PLINK_CONFIRM: if (plink_free_count(hapd) == 0) { - event = CNF_IGNR; + event = REQ_RJCT; + reason = WLAN_REASON_MESH_MAX_PEERS; wpa_printf(MSG_INFO, "MPM: Peer link num over quota(%d)", hapd->max_plinks); } else if (sta->my_lid != llid || (sta->peer_lid && sta->peer_lid != plid)) { - event = CNF_IGNR; + wpa_printf(MSG_DEBUG, + "MPM: lid mismatch: my_lid: 0x%x != 0x%x or peer_lid: 0x%x != 0x%x", + sta->my_lid, llid, sta->peer_lid, plid); + return; /* no FSM event */ } else { if (!sta->peer_lid) sta->peer_lid = plid; + sta->peer_aid = aid; event = CNF_ACPT; } break; @@ -1037,12 +1230,19 @@ void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s, * restarted. */ event = CLS_ACPT; - else if (sta->peer_lid != plid) - event = CLS_IGNR; - else if (peer_mgmt_ie.plid && sta->my_lid != llid) - event = CLS_IGNR; - else + else if (sta->peer_lid != plid) { + wpa_printf(MSG_DEBUG, + "MPM: peer_lid mismatch: 0x%x != 0x%x", + sta->peer_lid, plid); + return; /* no FSM event */ + } else if (peer_mgmt_ie.plid && sta->my_lid != llid) { + wpa_printf(MSG_DEBUG, + "MPM: my_lid mismatch: 0x%x != 0x%x", + sta->my_lid, llid); + return; /* no FSM event */ + } else { event = CLS_ACPT; + } break; default: /* @@ -1052,13 +1252,15 @@ void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s, */ return; } - mesh_mpm_fsm(wpa_s, sta, event); + mesh_mpm_fsm(wpa_s, sta, event, reason); } /* called by ap_free_sta */ -void mesh_mpm_free_sta(struct sta_info *sta) +void mesh_mpm_free_sta(struct hostapd_data *hapd, struct sta_info *sta) { + if (sta->plink_state == PLINK_ESTAB) + hapd->num_plinks--; eloop_cancel_timeout(plink_timer, ELOOP_ALL_CTX, sta); eloop_cancel_timeout(mesh_auth_timer, ELOOP_ALL_CTX, sta); } diff --git a/contrib/wpa/wpa_supplicant/mesh_mpm.h b/contrib/wpa/wpa_supplicant/mesh_mpm.h index 7ebaef0cd087..5fc1e6184bcb 100644 --- a/contrib/wpa/wpa_supplicant/mesh_mpm.h +++ b/contrib/wpa/wpa_supplicant/mesh_mpm.h @@ -14,10 +14,13 @@ void wpa_mesh_new_mesh_peer(struct wpa_supplicant *wpa_s, const u8 *addr, struct ieee802_11_elems *elems); void mesh_mpm_deinit(struct wpa_supplicant *wpa_s, struct hostapd_iface *ifmsh); void mesh_mpm_auth_peer(struct wpa_supplicant *wpa_s, const u8 *addr); -void mesh_mpm_free_sta(struct sta_info *sta); +void mesh_mpm_free_sta(struct hostapd_data *hapd, struct sta_info *sta); void wpa_mesh_set_plink_state(struct wpa_supplicant *wpa_s, struct sta_info *sta, enum mesh_plink_state state); +int mesh_mpm_close_peer(struct wpa_supplicant *wpa_s, const u8 *addr); +int mesh_mpm_connect_peer(struct wpa_supplicant *wpa_s, const u8 *addr, + int duration); #ifdef CONFIG_MESH diff --git a/contrib/wpa/wpa_supplicant/mesh_rsn.c b/contrib/wpa/wpa_supplicant/mesh_rsn.c index 747f1ae6968b..27ab8cb36458 100644 --- a/contrib/wpa/wpa_supplicant/mesh_rsn.c +++ b/contrib/wpa/wpa_supplicant/mesh_rsn.c @@ -27,12 +27,12 @@ #define MESH_AUTH_TIMEOUT 10 #define MESH_AUTH_RETRY 3 -#define MESH_AUTH_BLOCK_DURATION 3600 void mesh_auth_timer(void *eloop_ctx, void *user_data) { struct wpa_supplicant *wpa_s = eloop_ctx; struct sta_info *sta = user_data; + struct hostapd_data *hapd; if (sta->sae->state != SAE_ACCEPTED) { wpa_printf(MSG_DEBUG, "AUTH: Re-authenticate with " MACSTR @@ -43,23 +43,20 @@ void mesh_auth_timer(void *eloop_ctx, void *user_data) if (sta->sae_auth_retry < MESH_AUTH_RETRY) { mesh_rsn_auth_sae_sta(wpa_s, sta); } else { + hapd = wpa_s->ifmsh->bss[0]; + if (sta->sae_auth_retry > MESH_AUTH_RETRY) { - ap_free_sta(wpa_s->ifmsh->bss[0], sta); + ap_free_sta(hapd, sta); return; } /* block the STA if exceeded the number of attempts */ wpa_mesh_set_plink_state(wpa_s, sta, PLINK_BLOCKED); sta->sae->state = SAE_NOTHING; - if (wpa_s->mesh_auth_block_duration < - MESH_AUTH_BLOCK_DURATION) - wpa_s->mesh_auth_block_duration += 60; - eloop_register_timeout(wpa_s->mesh_auth_block_duration, - 0, mesh_auth_timer, wpa_s, sta); wpa_msg(wpa_s, MSG_INFO, MESH_SAE_AUTH_BLOCKED "addr=" MACSTR " duration=%d", MAC2STR(sta->addr), - wpa_s->mesh_auth_block_duration); + hapd->conf->ap_max_inactivity); } sta->sae_auth_retry++; } @@ -139,7 +136,8 @@ static int auth_start_ampe(void *ctx, const u8 *addr) } -static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr) +static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr, + enum mfp_options ieee80211w) { struct wpa_auth_config conf; struct wpa_auth_callbacks cb; @@ -148,13 +146,18 @@ static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr) wpa_printf(MSG_DEBUG, "AUTH: Initializing group state machine"); os_memset(&conf, 0, sizeof(conf)); - conf.wpa = 2; + conf.wpa = WPA_PROTO_RSN; conf.wpa_key_mgmt = WPA_KEY_MGMT_SAE; - conf.wpa_pairwise = WPA_CIPHER_CCMP; - conf.rsn_pairwise = WPA_CIPHER_CCMP; - conf.wpa_group = WPA_CIPHER_CCMP; + conf.wpa_pairwise = rsn->pairwise_cipher; + conf.rsn_pairwise = rsn->pairwise_cipher; + conf.wpa_group = rsn->group_cipher; conf.eapol_version = 0; conf.wpa_group_rekey = -1; +#ifdef CONFIG_IEEE80211W + conf.ieee80211w = ieee80211w; + if (ieee80211w != NO_MGMT_FRAME_PROTECTION) + conf.group_mgmt_cipher = rsn->mgmt_group_cipher; +#endif /* CONFIG_IEEE80211W */ os_memset(&cb, 0, sizeof(cb)); cb.ctx = rsn; @@ -170,18 +173,34 @@ static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr) } /* TODO: support rekeying */ - if (random_get_bytes(rsn->mgtk, 16) < 0) { - wpa_deinit(rsn->auth); + rsn->mgtk_len = wpa_cipher_key_len(conf.wpa_group); + if (random_get_bytes(rsn->mgtk, rsn->mgtk_len) < 0) return -1; - } + rsn->mgtk_key_id = 1; - /* group mgmt */ - wpa_drv_set_key(rsn->wpa_s, WPA_ALG_IGTK, NULL, 4, 1, - seq, sizeof(seq), rsn->mgtk, sizeof(rsn->mgtk)); +#ifdef CONFIG_IEEE80211W + if (ieee80211w != NO_MGMT_FRAME_PROTECTION) { + rsn->igtk_len = wpa_cipher_key_len(conf.group_mgmt_cipher); + if (random_get_bytes(rsn->igtk, rsn->igtk_len) < 0) + return -1; + rsn->igtk_key_id = 4; + + /* group mgmt */ + wpa_hexdump_key(MSG_DEBUG, "mesh: Own TX IGTK", + rsn->igtk, rsn->igtk_len); + wpa_drv_set_key(rsn->wpa_s, + wpa_cipher_to_alg(rsn->mgmt_group_cipher), NULL, + rsn->igtk_key_id, 1, + seq, sizeof(seq), rsn->igtk, rsn->igtk_len); + } +#endif /* CONFIG_IEEE80211W */ /* group privacy / data frames */ - wpa_drv_set_key(rsn->wpa_s, WPA_ALG_CCMP, NULL, 1, 1, - seq, sizeof(seq), rsn->mgtk, sizeof(rsn->mgtk)); + wpa_hexdump_key(MSG_DEBUG, "mesh: Own TX MGTK", + rsn->mgtk, rsn->mgtk_len); + wpa_drv_set_key(rsn->wpa_s, wpa_cipher_to_alg(rsn->group_cipher), NULL, + rsn->mgtk_key_id, 1, seq, sizeof(seq), + rsn->mgtk, rsn->mgtk_len); return 0; } @@ -190,6 +209,9 @@ static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr) static void mesh_rsn_deinit(struct mesh_rsn *rsn) { os_memset(rsn->mgtk, 0, sizeof(rsn->mgtk)); + rsn->mgtk_len = 0; + os_memset(rsn->igtk, 0, sizeof(rsn->igtk)); + rsn->igtk_len = 0; if (rsn->auth) wpa_deinit(rsn->auth); } @@ -207,8 +229,12 @@ struct mesh_rsn *mesh_rsn_auth_init(struct wpa_supplicant *wpa_s, if (mesh_rsn == NULL) return NULL; mesh_rsn->wpa_s = wpa_s; + mesh_rsn->pairwise_cipher = conf->pairwise_cipher; + mesh_rsn->group_cipher = conf->group_cipher; + mesh_rsn->mgmt_group_cipher = conf->mgmt_group_cipher; - if (__mesh_rsn_auth_init(mesh_rsn, wpa_s->own_addr) < 0) { + if (__mesh_rsn_auth_init(mesh_rsn, wpa_s->own_addr, + conf->ieee80211w) < 0) { mesh_rsn_deinit(mesh_rsn); os_free(mesh_rsn); return NULL; @@ -291,6 +317,7 @@ int mesh_rsn_auth_sae_sta(struct wpa_supplicant *wpa_s, { struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; struct wpa_ssid *ssid = wpa_s->current_ssid; + struct rsn_pmksa_cache_entry *pmksa; unsigned int rnd; int ret; @@ -306,6 +333,29 @@ int mesh_rsn_auth_sae_sta(struct wpa_supplicant *wpa_s, return -1; } + pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr); + if (pmksa) { + if (!sta->wpa_sm) + sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, + sta->addr, NULL); + if (!sta->wpa_sm) { + wpa_printf(MSG_ERROR, + "mesh: Failed to initialize RSN state machine"); + return -1; + } + + wpa_printf(MSG_DEBUG, + "AUTH: Mesh PMKSA cache entry found for " MACSTR + " - try to use PMKSA caching instead of new SAE authentication", + MAC2STR(sta->addr)); + wpa_auth_pmksa_set_to_sm(pmksa, sta->wpa_sm, hapd->wpa_auth, + sta->sae->pmkid, sta->sae->pmk); + sae_accept_sta(hapd, sta); + sta->mesh_sae_pmksa_caching = 1; + return 0; + } + sta->mesh_sae_pmksa_caching = 0; + if (mesh_rsn_build_sae_commit(wpa_s, ssid, sta)) return -1; @@ -313,7 +363,6 @@ int mesh_rsn_auth_sae_sta(struct wpa_supplicant *wpa_s, "AUTH: started authentication with SAE peer: " MACSTR, MAC2STR(sta->addr)); - wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING); ret = auth_sae_init_committed(hapd, sta); if (ret) return ret; @@ -328,10 +377,7 @@ int mesh_rsn_auth_sae_sta(struct wpa_supplicant *wpa_s, void mesh_rsn_get_pmkid(struct mesh_rsn *rsn, struct sta_info *sta, u8 *pmkid) { - /* don't expect wpa auth to cache the pmkid for now */ - rsn_pmkid(sta->sae->pmk, PMK_LEN, rsn->wpa_s->own_addr, - sta->addr, pmkid, - wpa_key_mgmt_sha256(wpa_auth_sta_key_mgmt(sta->wpa_sm))); + os_memcpy(pmkid, sta->sae->pmkid, SAE_PMKID_LEN); } @@ -340,18 +386,27 @@ mesh_rsn_derive_aek(struct mesh_rsn *rsn, struct sta_info *sta) { u8 *myaddr = rsn->wpa_s->own_addr; u8 *peer = sta->addr; - u8 *addr1 = peer, *addr2 = myaddr; - u8 context[AES_BLOCK_SIZE]; + u8 *addr1, *addr2; + u8 context[RSN_SELECTOR_LEN + 2 * ETH_ALEN], *ptr = context; - /* SAE */ - RSN_SELECTOR_PUT(context, wpa_cipher_to_suite(0, WPA_CIPHER_GCMP)); + /* + * AEK = KDF-Hash-256(PMK, "AEK Derivation", Selected AKM Suite || + * min(localMAC, peerMAC) || max(localMAC, peerMAC)) + */ + /* Selected AKM Suite: SAE */ + RSN_SELECTOR_PUT(ptr, RSN_AUTH_KEY_MGMT_SAE); + ptr += RSN_SELECTOR_LEN; if (os_memcmp(myaddr, peer, ETH_ALEN) < 0) { addr1 = myaddr; addr2 = peer; + } else { + addr1 = peer; + addr2 = myaddr; } - os_memcpy(context + 4, addr1, ETH_ALEN); - os_memcpy(context + 10, addr2, ETH_ALEN); + os_memcpy(ptr, addr1, ETH_ALEN); + ptr += ETH_ALEN; + os_memcpy(ptr, addr2, ETH_ALEN); sha256_prf(sta->sae->pmk, sizeof(sta->sae->pmk), "AEK Derivation", context, sizeof(context), sta->aek, sizeof(sta->aek)); @@ -363,40 +418,44 @@ int mesh_rsn_derive_mtk(struct wpa_supplicant *wpa_s, struct sta_info *sta) { u8 *ptr; u8 *min, *max; - u16 min_lid, max_lid; - size_t nonce_len = sizeof(sta->my_nonce); - size_t lid_len = sizeof(sta->my_lid); u8 *myaddr = wpa_s->own_addr; u8 *peer = sta->addr; - /* 2 nonces, 2 linkids, akm suite, 2 mac addrs */ - u8 context[64 + 4 + 4 + 12]; + u8 context[2 * WPA_NONCE_LEN + 2 * 2 + RSN_SELECTOR_LEN + 2 * ETH_ALEN]; + /* + * MTK = KDF-Hash-Length(PMK, "Temporal Key Derivation", min(localNonce, + * peerNonce) || max(localNonce, peerNonce) || min(localLinkID, + * peerLinkID) || max(localLinkID, peerLinkID) || Selected AKM Suite || + * min(localMAC, peerMAC) || max(localMAC, peerMAC)) + */ ptr = context; - if (os_memcmp(sta->my_nonce, sta->peer_nonce, nonce_len) < 0) { + if (os_memcmp(sta->my_nonce, sta->peer_nonce, WPA_NONCE_LEN) < 0) { min = sta->my_nonce; max = sta->peer_nonce; } else { min = sta->peer_nonce; max = sta->my_nonce; } - os_memcpy(ptr, min, nonce_len); - os_memcpy(ptr + nonce_len, max, nonce_len); - ptr += 2 * nonce_len; + os_memcpy(ptr, min, WPA_NONCE_LEN); + ptr += WPA_NONCE_LEN; + os_memcpy(ptr, max, WPA_NONCE_LEN); + ptr += WPA_NONCE_LEN; if (sta->my_lid < sta->peer_lid) { - min_lid = host_to_le16(sta->my_lid); - max_lid = host_to_le16(sta->peer_lid); + WPA_PUT_LE16(ptr, sta->my_lid); + ptr += 2; + WPA_PUT_LE16(ptr, sta->peer_lid); + ptr += 2; } else { - min_lid = host_to_le16(sta->peer_lid); - max_lid = host_to_le16(sta->my_lid); + WPA_PUT_LE16(ptr, sta->peer_lid); + ptr += 2; + WPA_PUT_LE16(ptr, sta->my_lid); + ptr += 2; } - os_memcpy(ptr, &min_lid, lid_len); - os_memcpy(ptr + lid_len, &max_lid, lid_len); - ptr += 2 * lid_len; - /* SAE */ - RSN_SELECTOR_PUT(ptr, wpa_cipher_to_suite(0, WPA_CIPHER_GCMP)); - ptr += 4; + /* Selected AKM Suite: SAE */ + RSN_SELECTOR_PUT(ptr, RSN_AUTH_KEY_MGMT_SAE); + ptr += RSN_SELECTOR_LEN; if (os_memcmp(myaddr, peer, ETH_ALEN) < 0) { min = myaddr; @@ -406,22 +465,24 @@ int mesh_rsn_derive_mtk(struct wpa_supplicant *wpa_s, struct sta_info *sta) max = myaddr; } os_memcpy(ptr, min, ETH_ALEN); - os_memcpy(ptr + ETH_ALEN, max, ETH_ALEN); + ptr += ETH_ALEN; + os_memcpy(ptr, max, ETH_ALEN); - sha256_prf(sta->sae->pmk, sizeof(sta->sae->pmk), + sta->mtk_len = wpa_cipher_key_len(wpa_s->mesh_rsn->pairwise_cipher); + sha256_prf(sta->sae->pmk, SAE_PMK_LEN, "Temporal Key Derivation", context, sizeof(context), - sta->mtk, sizeof(sta->mtk)); + sta->mtk, sta->mtk_len); return 0; } void mesh_rsn_init_ampe_sta(struct wpa_supplicant *wpa_s, struct sta_info *sta) { - if (random_get_bytes(sta->my_nonce, 32) < 0) { + if (random_get_bytes(sta->my_nonce, WPA_NONCE_LEN) < 0) { wpa_printf(MSG_INFO, "mesh: Failed to derive random nonce"); /* TODO: How to handle this more cleanly? */ } - os_memset(sta->peer_nonce, 0, 32); + os_memset(sta->peer_nonce, 0, WPA_NONCE_LEN); mesh_rsn_derive_aek(wpa_s->mesh_rsn, sta); } @@ -437,65 +498,94 @@ int mesh_rsn_protect_frame(struct mesh_rsn *rsn, struct sta_info *sta, { struct ieee80211_ampe_ie *ampe; u8 const *ie = wpabuf_head_u8(buf) + wpabuf_len(buf); - u8 *ampe_ie = NULL, *mic_ie = NULL, *mic_payload; + u8 *ampe_ie, *pos, *mic_payload; const u8 *aad[] = { rsn->wpa_s->own_addr, sta->addr, cat }; const size_t aad_len[] = { ETH_ALEN, ETH_ALEN, ie - cat }; int ret = 0; + size_t len; - if (AES_BLOCK_SIZE + 2 + sizeof(*ampe) + 2 > wpabuf_tailroom(buf)) { + len = sizeof(*ampe); + if (cat[1] == PLINK_OPEN) + len += rsn->mgtk_len + WPA_KEY_RSC_LEN + 4; +#ifdef CONFIG_IEEE80211W + if (cat[1] == PLINK_OPEN && rsn->igtk_len) + len += 2 + 6 + rsn->igtk_len; +#endif /* CONFIG_IEEE80211W */ + + if (2 + AES_BLOCK_SIZE + 2 + len > wpabuf_tailroom(buf)) { wpa_printf(MSG_ERROR, "protect frame: buffer too small"); return -EINVAL; } - ampe_ie = os_zalloc(2 + sizeof(*ampe)); + ampe_ie = os_zalloc(2 + len); if (!ampe_ie) { wpa_printf(MSG_ERROR, "protect frame: out of memory"); return -ENOMEM; } - mic_ie = os_zalloc(2 + AES_BLOCK_SIZE); - if (!mic_ie) { - wpa_printf(MSG_ERROR, "protect frame: out of memory"); - ret = -ENOMEM; - goto free; - } - /* IE: AMPE */ ampe_ie[0] = WLAN_EID_AMPE; - ampe_ie[1] = sizeof(*ampe); + ampe_ie[1] = len; ampe = (struct ieee80211_ampe_ie *) (ampe_ie + 2); RSN_SELECTOR_PUT(ampe->selected_pairwise_suite, - wpa_cipher_to_suite(WPA_PROTO_RSN, WPA_CIPHER_CCMP)); - os_memcpy(ampe->local_nonce, sta->my_nonce, 32); - os_memcpy(ampe->peer_nonce, sta->peer_nonce, 32); - /* incomplete: see 13.5.4 */ + RSN_CIPHER_SUITE_CCMP); + os_memcpy(ampe->local_nonce, sta->my_nonce, WPA_NONCE_LEN); + os_memcpy(ampe->peer_nonce, sta->peer_nonce, WPA_NONCE_LEN); + + pos = (u8 *) (ampe + 1); + if (cat[1] != PLINK_OPEN) + goto skip_keys; + + /* TODO: Key Replay Counter[8] optionally for + * Mesh Group Key Inform/Acknowledge frames */ + /* TODO: static mgtk for now since we don't support rekeying! */ - os_memcpy(ampe->mgtk, rsn->mgtk, 16); - /* TODO: Populate Key RSC */ - /* expire in 13 decades or so */ - os_memset(ampe->key_expiration, 0xff, 4); + /* + * GTKdata[variable]: + * MGTK[variable] || Key RSC[8] || GTKExpirationTime[4] + */ + os_memcpy(pos, rsn->mgtk, rsn->mgtk_len); + pos += rsn->mgtk_len; + wpa_drv_get_seqnum(rsn->wpa_s, NULL, rsn->mgtk_key_id, pos); + pos += WPA_KEY_RSC_LEN; + /* Use fixed GTKExpirationTime for now */ + WPA_PUT_LE32(pos, 0xffffffff); + pos += 4; + +#ifdef CONFIG_IEEE80211W + /* + * IGTKdata[variable]: + * Key ID[2], IPN[6], IGTK[variable] + */ + if (rsn->igtk_len) { + WPA_PUT_LE16(pos, rsn->igtk_key_id); + pos += 2; + wpa_drv_get_seqnum(rsn->wpa_s, NULL, rsn->igtk_key_id, pos); + pos += 6; + os_memcpy(pos, rsn->igtk, rsn->igtk_len); + } +#endif /* CONFIG_IEEE80211W */ + +skip_keys: + wpa_hexdump_key(MSG_DEBUG, "mesh: Plaintext AMPE element", + ampe_ie, 2 + len); /* IE: MIC */ - mic_ie[0] = WLAN_EID_MIC; - mic_ie[1] = AES_BLOCK_SIZE; - wpabuf_put_data(buf, mic_ie, 2); + wpabuf_put_u8(buf, WLAN_EID_MIC); + wpabuf_put_u8(buf, AES_BLOCK_SIZE); /* MIC field is output ciphertext */ /* encrypt after MIC */ - mic_payload = (u8 *) wpabuf_put(buf, 2 + sizeof(*ampe) + - AES_BLOCK_SIZE); + mic_payload = wpabuf_put(buf, 2 + len + AES_BLOCK_SIZE); - if (aes_siv_encrypt(sta->aek, ampe_ie, 2 + sizeof(*ampe), 3, + if (aes_siv_encrypt(sta->aek, ampe_ie, 2 + len, 3, aad, aad_len, mic_payload)) { wpa_printf(MSG_ERROR, "protect frame: failed to encrypt"); ret = -ENOMEM; - goto free; } -free: os_free(ampe_ie); - os_free(mic_ie); return ret; } @@ -503,18 +593,37 @@ free: int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta, struct ieee802_11_elems *elems, const u8 *cat, + const u8 *chosen_pmk, const u8 *start, size_t elems_len) { int ret = 0; struct ieee80211_ampe_ie *ampe; - u8 null_nonce[32] = {}; + u8 null_nonce[WPA_NONCE_LEN] = {}; u8 ampe_eid; u8 ampe_ie_len; - u8 *ampe_buf, *crypt = NULL; + u8 *ampe_buf, *crypt = NULL, *pos, *end; 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 }; + size_t key_len; + + if (!sta->sae) { + struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; + + if (!wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr)) { + wpa_printf(MSG_INFO, + "Mesh RSN: SAE is not prepared yet"); + return -1; + } + mesh_rsn_auth_sae_sta(wpa_s, sta); + } + + if (chosen_pmk && os_memcmp(chosen_pmk, sta->sae->pmkid, PMKID_LEN)) { + wpa_msg(wpa_s, MSG_DEBUG, + "Mesh RSN: Invalid PMKID (Chosen PMK did not match calculated PMKID)"); + return -1; + } if (!elems->mic || elems->mic_len < AES_BLOCK_SIZE) { wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: missing mic ie"); @@ -526,7 +635,7 @@ int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta, return -1; crypt_len = elems_len - (elems->mic - start); - if (crypt_len < 2) { + if (crypt_len < 2 + AES_BLOCK_SIZE) { wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: missing ampe ie"); return -1; } @@ -544,14 +653,19 @@ int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta, if (aes_siv_decrypt(sta->aek, crypt, crypt_len, 3, aad, aad_len, ampe_buf)) { wpa_printf(MSG_ERROR, "Mesh RSN: frame verification failed!"); - ret = -1; + ret = -2; goto free; } + crypt_len -= AES_BLOCK_SIZE; + wpa_hexdump_key(MSG_DEBUG, "mesh: Decrypted AMPE element", + ampe_buf, crypt_len); + ampe_eid = *ampe_buf++; ampe_ie_len = *ampe_buf++; if (ampe_eid != WLAN_EID_AMPE || + (size_t) 2 + ampe_ie_len > crypt_len || ampe_ie_len < sizeof(struct ieee80211_ampe_ie)) { wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: invalid ampe ie"); ret = -1; @@ -559,17 +673,89 @@ int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta, } ampe = (struct ieee80211_ampe_ie *) ampe_buf; - if (os_memcmp(ampe->peer_nonce, null_nonce, 32) != 0 && - os_memcmp(ampe->peer_nonce, sta->my_nonce, 32) != 0) { + pos = (u8 *) (ampe + 1); + end = ampe_buf + ampe_ie_len; + if (os_memcmp(ampe->peer_nonce, null_nonce, WPA_NONCE_LEN) != 0 && + os_memcmp(ampe->peer_nonce, sta->my_nonce, WPA_NONCE_LEN) != 0) { wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: invalid peer nonce"); ret = -1; goto free; } os_memcpy(sta->peer_nonce, ampe->local_nonce, sizeof(ampe->local_nonce)); - os_memcpy(sta->mgtk, ampe->mgtk, sizeof(ampe->mgtk)); - /* todo parse mgtk expiration */ + /* TODO: Key Replay Counter[8] in Mesh Group Key Inform/Acknowledge + * frames */ + + /* + * GTKdata shall not be included in Mesh Peering Confirm. While the + * standard does not state the same about IGTKdata, that same constraint + * needs to apply for it. It makes no sense to include the keys in Mesh + * Peering Close frames either, so while the standard does not seem to + * have a shall statement for these, they are described without + * mentioning GTKdata. + * + * An earlier implementation used to add GTKdata to both Mesh Peering + * Open and Mesh Peering Confirm frames, so ignore the possibly present + * GTKdata frame without rejecting the frame as a backwards + * compatibility mechanism. + */ + if (cat[1] != PLINK_OPEN) { + if (end > pos) { + wpa_hexdump_key(MSG_DEBUG, + "mesh: Ignore unexpected GTKdata(etc.) fields in the end of AMPE element in Mesh Peering Confirm/Close", + pos, end - pos); + } + goto free; + } + + /* + * GTKdata[variable]: + * MGTK[variable] || Key RSC[8] || GTKExpirationTime[4] + */ + sta->mgtk_key_id = 1; /* FIX: Where to get Key ID? */ + key_len = wpa_cipher_key_len(wpa_s->mesh_rsn->group_cipher); + if ((int) key_len + WPA_KEY_RSC_LEN + 4 > end - pos) { + wpa_dbg(wpa_s, MSG_DEBUG, "mesh: Truncated AMPE element"); + ret = -1; + goto free; + } + sta->mgtk_len = key_len; + os_memcpy(sta->mgtk, pos, sta->mgtk_len); + wpa_hexdump_key(MSG_DEBUG, "mesh: GTKdata - MGTK", + sta->mgtk, sta->mgtk_len); + pos += sta->mgtk_len; + wpa_hexdump(MSG_DEBUG, "mesh: GTKdata - MGTK - Key RSC", + pos, WPA_KEY_RSC_LEN); + os_memcpy(sta->mgtk_rsc, pos, sizeof(sta->mgtk_rsc)); + pos += WPA_KEY_RSC_LEN; + wpa_printf(MSG_DEBUG, + "mesh: GTKdata - MGTK - GTKExpirationTime: %u seconds", + WPA_GET_LE32(pos)); + pos += 4; + +#ifdef CONFIG_IEEE80211W + /* + * IGTKdata[variable]: + * Key ID[2], IPN[6], IGTK[variable] + */ + key_len = wpa_cipher_key_len(wpa_s->mesh_rsn->mgmt_group_cipher); + if (end - pos >= (int) (2 + 6 + key_len)) { + sta->igtk_key_id = WPA_GET_LE16(pos); + wpa_printf(MSG_DEBUG, "mesh: IGTKdata - Key ID %u", + sta->igtk_key_id); + pos += 2; + os_memcpy(sta->igtk_rsc, pos, sizeof(sta->igtk_rsc)); + wpa_hexdump(MSG_DEBUG, "mesh: IGTKdata - IPN", + sta->igtk_rsc, sizeof(sta->igtk_rsc)); + pos += 6; + os_memcpy(sta->igtk, pos, key_len); + sta->igtk_len = key_len; + wpa_hexdump_key(MSG_DEBUG, "mesh: IGTKdata - IGTK", + sta->igtk, sta->igtk_len); + } +#endif /* CONFIG_IEEE80211W */ + free: os_free(crypt); return ret; diff --git a/contrib/wpa/wpa_supplicant/mesh_rsn.h b/contrib/wpa/wpa_supplicant/mesh_rsn.h index b1471b2de8ae..8775cedc3b27 100644 --- a/contrib/wpa/wpa_supplicant/mesh_rsn.h +++ b/contrib/wpa/wpa_supplicant/mesh_rsn.h @@ -12,7 +12,15 @@ struct mesh_rsn { struct wpa_supplicant *wpa_s; struct wpa_authenticator *auth; - u8 mgtk[16]; + unsigned int pairwise_cipher; + unsigned int group_cipher; + u8 mgtk[WPA_TK_MAX_LEN]; + size_t mgtk_len; + u8 mgtk_key_id; + unsigned int mgmt_group_cipher; + u8 igtk_key_id; + u8 igtk[WPA_TK_MAX_LEN]; + size_t igtk_len; #ifdef CONFIG_SAE struct wpabuf *sae_token; int sae_group_index; @@ -30,6 +38,7 @@ int mesh_rsn_protect_frame(struct mesh_rsn *rsn, struct sta_info *sta, const u8 *cat, struct wpabuf *buf); int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta, struct ieee802_11_elems *elems, const u8 *cat, + const u8 *chosen_pmk, const u8 *start, size_t elems_len); void mesh_auth_timer(void *eloop_ctx, void *user_data); diff --git a/contrib/wpa/wpa_supplicant/notify.c b/contrib/wpa/wpa_supplicant/notify.c index 45d06bf35744..67e36ae34cb8 100644 --- a/contrib/wpa/wpa_supplicant/notify.c +++ b/contrib/wpa/wpa_supplicant/notify.c @@ -13,6 +13,7 @@ #include "config.h" #include "wpa_supplicant_i.h" #include "wps_supplicant.h" +#include "binder/binder.h" #include "dbus/dbus_common.h" #include "dbus/dbus_old.h" #include "dbus/dbus_new.h" @@ -34,6 +35,12 @@ int wpas_notify_supplicant_initialized(struct wpa_global *global) } #endif /* CONFIG_DBUS */ +#ifdef CONFIG_BINDER + global->binder = wpas_binder_init(global); + if (!global->binder) + return -1; +#endif /* CONFIG_BINDER */ + return 0; } @@ -44,6 +51,11 @@ void wpas_notify_supplicant_deinitialized(struct wpa_global *global) if (global->dbus) wpas_dbus_deinit(global->dbus); #endif /* CONFIG_DBUS */ + +#ifdef CONFIG_BINDER + if (global->binder) + wpas_binder_deinit(global->binder); +#endif /* CONFIG_BINDER */ } @@ -128,6 +140,15 @@ void wpas_notify_disconnect_reason(struct wpa_supplicant *wpa_s) } +void wpas_notify_assoc_status_code(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->p2p_mgmt) + return; + + wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_ASSOC_STATUS_CODE); +} + + void wpas_notify_network_changed(struct wpa_supplicant *wpa_s) { if (wpa_s->p2p_mgmt) @@ -647,13 +668,13 @@ void wpas_notify_p2p_provision_discovery(struct wpa_supplicant *wpa_s, void wpas_notify_p2p_group_started(struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid, int network_id, + struct wpa_ssid *ssid, int persistent, int client) { /* Notify a group has been started */ wpas_dbus_register_p2p_group(wpa_s, ssid); - wpas_dbus_signal_p2p_group_started(wpa_s, ssid, client, network_id); + wpas_dbus_signal_p2p_group_started(wpa_s, client, persistent); } diff --git a/contrib/wpa/wpa_supplicant/notify.h b/contrib/wpa/wpa_supplicant/notify.h index d9f0f5a96732..8cce0f30c2a9 100644 --- a/contrib/wpa/wpa_supplicant/notify.h +++ b/contrib/wpa/wpa_supplicant/notify.h @@ -23,6 +23,7 @@ void wpas_notify_state_changed(struct wpa_supplicant *wpa_s, enum wpa_states new_state, enum wpa_states old_state); void wpas_notify_disconnect_reason(struct wpa_supplicant *wpa_s); +void wpas_notify_assoc_status_code(struct wpa_supplicant *wpa_s); void wpas_notify_network_changed(struct wpa_supplicant *wpa_s); void wpas_notify_ap_scan_changed(struct wpa_supplicant *wpa_s); void wpas_notify_bssid_changed(struct wpa_supplicant *wpa_s); @@ -112,7 +113,7 @@ void wpas_notify_p2p_provision_discovery(struct wpa_supplicant *wpa_s, u16 config_methods, unsigned int generated_pin); void wpas_notify_p2p_group_started(struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid, int network_id, + struct wpa_ssid *ssid, int persistent, int client); void wpas_notify_p2p_group_formation_failure(struct wpa_supplicant *wpa_s, const char *reason); diff --git a/contrib/wpa/wpa_supplicant/offchannel.c b/contrib/wpa/wpa_supplicant/offchannel.c index 63af83afe198..26d41a4ad5c6 100644 --- a/contrib/wpa/wpa_supplicant/offchannel.c +++ b/contrib/wpa/wpa_supplicant/offchannel.c @@ -23,8 +23,29 @@ wpas_get_tx_interface(struct wpa_supplicant *wpa_s, const u8 *src) { struct wpa_supplicant *iface; - if (os_memcmp(src, wpa_s->own_addr, ETH_ALEN) == 0) + if (os_memcmp(src, wpa_s->own_addr, ETH_ALEN) == 0) { +#ifdef CONFIG_P2P + if (wpa_s->p2p_mgmt && wpa_s != wpa_s->parent && + wpa_s->parent->ap_iface && + os_memcmp(wpa_s->parent->own_addr, + wpa_s->own_addr, ETH_ALEN) == 0 && + wpabuf_len(wpa_s->pending_action_tx) >= 2 && + *wpabuf_head_u8(wpa_s->pending_action_tx) != + WLAN_ACTION_PUBLIC) { + /* + * When P2P Device interface has same MAC address as + * the GO interface, make sure non-Public Action frames + * are sent through the GO interface. The P2P Device + * interface can only send Public Action frames. + */ + wpa_printf(MSG_DEBUG, + "P2P: Use GO interface %s instead of interface %s for Action TX", + wpa_s->parent->ifname, wpa_s->ifname); + return wpa_s->parent; + } +#endif /* CONFIG_P2P */ return wpa_s; + } /* * Try to find a group interface that matches with the source address. @@ -118,8 +139,9 @@ static void wpas_send_action_cb(void *eloop_ctx, void *timeout_ctx) } wpa_printf(MSG_DEBUG, "Off-channel: Sending pending Action frame to " - MACSTR " using interface %s", - MAC2STR(wpa_s->pending_action_dst), iface->ifname); + MACSTR " using interface %s (pending_action_tx=%p)", + MAC2STR(wpa_s->pending_action_dst), iface->ifname, + wpa_s->pending_action_tx); res = wpa_drv_send_action(iface, wpa_s->pending_action_freq, 0, wpa_s->pending_action_dst, wpa_s->pending_action_src, @@ -183,8 +205,12 @@ void offchannel_send_action_tx_status( return; } - wpa_printf(MSG_DEBUG, "Off-channel: Delete matching pending action frame"); - + wpa_printf(MSG_DEBUG, + "Off-channel: Delete matching pending action frame (dst=" + MACSTR " pending_action_tx=%p)", MAC2STR(dst), + wpa_s->pending_action_tx); + wpa_hexdump_buf(MSG_MSGDUMP, "Pending TX frame", + wpa_s->pending_action_tx); wpabuf_free(wpa_s->pending_action_tx); wpa_s->pending_action_tx = NULL; @@ -250,8 +276,11 @@ int offchannel_send_action(struct wpa_supplicant *wpa_s, unsigned int freq, if (wpa_s->pending_action_tx) { wpa_printf(MSG_DEBUG, "Off-channel: Dropped pending Action " - "frame TX to " MACSTR, - MAC2STR(wpa_s->pending_action_dst)); + "frame TX to " MACSTR " (pending_action_tx=%p)", + MAC2STR(wpa_s->pending_action_dst), + wpa_s->pending_action_tx); + wpa_hexdump_buf(MSG_MSGDUMP, "Pending TX frame", + wpa_s->pending_action_tx); wpabuf_free(wpa_s->pending_action_tx); } wpa_s->pending_action_tx_done = 0; @@ -268,6 +297,12 @@ int offchannel_send_action(struct wpa_supplicant *wpa_s, unsigned int freq, os_memcpy(wpa_s->pending_action_bssid, bssid, ETH_ALEN); wpa_s->pending_action_freq = freq; wpa_s->pending_action_no_cck = no_cck; + wpa_printf(MSG_DEBUG, + "Off-channel: Stored pending action frame (dst=" MACSTR + " pending_action_tx=%p)", + MAC2STR(dst), wpa_s->pending_action_tx); + wpa_hexdump_buf(MSG_MSGDUMP, "Pending TX frame", + wpa_s->pending_action_tx); if (freq != 0 && wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) { struct wpa_supplicant *iface; @@ -428,6 +463,9 @@ const void * offchannel_pending_action_tx(struct wpa_supplicant *wpa_s) */ void offchannel_clear_pending_action_tx(struct wpa_supplicant *wpa_s) { + wpa_printf(MSG_DEBUG, + "Off-channel: Clear pending Action frame TX (pending_action_tx=%p", + wpa_s->pending_action_tx); wpabuf_free(wpa_s->pending_action_tx); wpa_s->pending_action_tx = NULL; } diff --git a/contrib/wpa/wpa_supplicant/p2p_supplicant.c b/contrib/wpa/wpa_supplicant/p2p_supplicant.c index 78bdd0837e8b..b1fdc2837ff0 100644 --- a/contrib/wpa/wpa_supplicant/p2p_supplicant.c +++ b/contrib/wpa/wpa_supplicant/p2p_supplicant.c @@ -53,6 +53,13 @@ */ #define P2P_GO_FREQ_CHANGE_TIME 5 +/** + * Defines CSA parameters which are used when GO evacuates the no longer valid + * channel (and if the driver supports channel switch). + */ +#define P2P_GO_CSA_COUNT 7 +#define P2P_GO_CSA_BLOCK_TX 0 + #ifndef P2P_MAX_CLIENT_IDLE /* * How many seconds to try to reconnect to the GO when connection in P2P client @@ -117,6 +124,10 @@ wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated, int go); static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s, int freq, const u8 *ssid, size_t ssid_len); +static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq, + int *force_freq, int *pref_freq, int go, + unsigned int *pref_freq_list, + unsigned int *num_pref_freq); static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq, const u8 *ssid, size_t ssid_len); static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx); @@ -340,6 +351,7 @@ static int wpas_p2p_scan(void *ctx, enum p2p_scan_type type, int freq, int social_channels_freq[] = { 2412, 2437, 2462, 60480 }; size_t ielen; u8 *n, i; + unsigned int bands; if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; @@ -369,28 +381,6 @@ static int wpas_p2p_scan(void *ctx, enum p2p_scan_type type, int freq, if (wps_ie == NULL) goto fail; - ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p); - ies = wpabuf_alloc(wpabuf_len(wps_ie) + ielen); - if (ies == NULL) { - wpabuf_free(wps_ie); - goto fail; - } - wpabuf_put_buf(ies, wps_ie); - wpabuf_free(wps_ie); - - p2p_scan_ie(wpa_s->global->p2p, ies, dev_id); - - params->p2p_probe = 1; - n = os_malloc(wpabuf_len(ies)); - if (n == NULL) { - wpabuf_free(ies); - goto fail; - } - os_memcpy(n, wpabuf_head(ies), wpabuf_len(ies)); - params->extra_ies = n; - params->extra_ies_len = wpabuf_len(ies); - wpabuf_free(ies); - switch (type) { case P2P_SCAN_SOCIAL: params->freqs = os_calloc(ARRAY_SIZE(social_channels_freq) + 1, @@ -431,6 +421,29 @@ static int wpas_p2p_scan(void *ctx, enum p2p_scan_type type, int freq, break; } + ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p); + ies = wpabuf_alloc(wpabuf_len(wps_ie) + ielen); + if (ies == NULL) { + wpabuf_free(wps_ie); + goto fail; + } + wpabuf_put_buf(ies, wps_ie); + wpabuf_free(wps_ie); + + bands = wpas_get_bands(wpa_s, params->freqs); + p2p_scan_ie(wpa_s->global->p2p, ies, dev_id, bands); + + params->p2p_probe = 1; + n = os_malloc(wpabuf_len(ies)); + if (n == NULL) { + wpabuf_free(ies); + goto fail; + } + os_memcpy(n, wpabuf_head(ies), wpabuf_len(ies)); + params->extra_ies = n; + params->extra_ies_len = wpabuf_len(ies); + wpabuf_free(ies); + radio_remove_works(wpa_s, "p2p-scan", 0); if (radio_add_work(wpa_s, 0, "p2p-scan", 0, wpas_p2p_trigger_scan_cb, params) < 0) @@ -538,27 +551,39 @@ static unsigned int p2p_group_go_member_count(struct wpa_supplicant *wpa_s) } +static unsigned int p2p_is_active_persistent_group(struct wpa_supplicant *wpa_s) +{ + return !wpa_s->p2p_mgmt && wpa_s->current_ssid && + !wpa_s->current_ssid->disabled && + wpa_s->current_ssid->p2p_group && + wpa_s->current_ssid->p2p_persistent_group; +} + + +static unsigned int p2p_is_active_persistent_go(struct wpa_supplicant *wpa_s) +{ + return p2p_is_active_persistent_group(wpa_s) && + wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO; +} + + /* Find an interface for a P2P group where we are the GO */ static struct wpa_supplicant * wpas_p2p_get_go_group(struct wpa_supplicant *wpa_s) { struct wpa_supplicant *save = NULL; - struct wpa_ssid *s; if (!wpa_s) return NULL; for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) { - for (s = wpa_s->conf->ssid; s; s = s->next) { - if (s->disabled || !s->p2p_group || - s->mode != WPAS_MODE_P2P_GO) - continue; + if (!p2p_is_active_persistent_go(wpa_s)) + continue; - /* Prefer a group with connected clients */ - if (p2p_get_group_num_members(wpa_s->p2p_group)) - return wpa_s; - save = wpa_s; - } + /* Prefer a group with connected clients */ + if (p2p_get_group_num_members(wpa_s->p2p_group)) + return wpa_s; + save = wpa_s; } /* No group with connected clients, so pick the one without (if any) */ @@ -566,29 +591,23 @@ wpas_p2p_get_go_group(struct wpa_supplicant *wpa_s) } -/* Find an active P2P group where we are the GO */ -static struct wpa_ssid * wpas_p2p_group_go_ssid(struct wpa_supplicant *wpa_s, - u8 *bssid) +static unsigned int p2p_is_active_persistent_cli(struct wpa_supplicant *wpa_s) { - struct wpa_ssid *s, *empty = NULL; + return p2p_is_active_persistent_group(wpa_s) && + wpa_s->current_ssid->mode == WPAS_MODE_INFRA; +} - if (!wpa_s) - return 0; +/* Find an interface for a P2P group where we are the P2P Client */ +static struct wpa_supplicant * +wpas_p2p_get_cli_group(struct wpa_supplicant *wpa_s) +{ for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) { - for (s = wpa_s->conf->ssid; s; s = s->next) { - if (s->disabled || !s->p2p_group || - s->mode != WPAS_MODE_P2P_GO) - continue; - - os_memcpy(bssid, wpa_s->own_addr, ETH_ALEN); - if (p2p_get_group_num_members(wpa_s->p2p_group)) - return s; - empty = s; - } + if (p2p_is_active_persistent_cli(wpa_s)) + return wpa_s; } - return empty; + return NULL; } @@ -607,20 +626,34 @@ wpas_p2p_get_persistent_go(struct wpa_supplicant *wpa_s) } -static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role) +static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role, + unsigned int *force_freq, + unsigned int *pref_freq) { - struct wpa_supplicant *wpa_s = ctx, *tmp_wpa_s; + struct wpa_supplicant *wpa_s = ctx; struct wpa_ssid *s; u8 conncap = P2PS_SETUP_NONE; unsigned int owned_members = 0; - unsigned int owner = 0; - unsigned int client = 0; - struct wpa_supplicant *go_wpa_s; + struct wpa_supplicant *go_wpa_s, *cli_wpa_s; struct wpa_ssid *persistent_go; int p2p_no_group_iface; + unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size; wpa_printf(MSG_DEBUG, "P2P: Conncap - in:%d role:%d", incoming, role); + if (force_freq) + *force_freq = 0; + if (pref_freq) + *pref_freq = 0; + + size = P2P_MAX_PREF_CHANNELS; + if (force_freq && pref_freq && + !wpas_p2p_setup_freqs(wpa_s, 0, (int *) force_freq, + (int *) pref_freq, 0, pref_freq_list, &size)) + wpas_p2p_set_own_freq_preference(wpa_s, + *force_freq ? *force_freq : + *pref_freq); + /* * For non-concurrent capable devices: * If persistent_go, then no new. @@ -628,36 +661,21 @@ static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role) * If client, then no GO. */ go_wpa_s = wpas_p2p_get_go_group(wpa_s); + if (go_wpa_s) + owned_members = p2p_get_group_num_members(go_wpa_s->p2p_group); persistent_go = wpas_p2p_get_persistent_go(wpa_s); p2p_no_group_iface = !wpas_p2p_create_iface(wpa_s); + cli_wpa_s = wpas_p2p_get_cli_group(wpa_s); - wpa_printf(MSG_DEBUG, "P2P: GO(iface)=%p persistent(ssid)=%p", - go_wpa_s, persistent_go); - - for (tmp_wpa_s = wpa_s->global->ifaces; tmp_wpa_s; - tmp_wpa_s = tmp_wpa_s->next) { - for (s = tmp_wpa_s->conf->ssid; s; s = s->next) { - wpa_printf(MSG_DEBUG, - "P2P: sup:%p ssid:%p disabled:%d p2p:%d mode:%d", - tmp_wpa_s, s, s->disabled, - s->p2p_group, s->mode); - if (!s->disabled && s->p2p_group) { - if (s->mode == WPAS_MODE_P2P_GO) { - owned_members += - p2p_get_group_num_members( - tmp_wpa_s->p2p_group); - owner++; - } else - client++; - } - } - } + wpa_printf(MSG_DEBUG, + "P2P: GO(iface)=%p members=%u CLI(iface)=%p persistent(ssid)=%p", + go_wpa_s, owned_members, cli_wpa_s, persistent_go); /* If not concurrent, restrict our choices */ if (p2p_no_group_iface) { wpa_printf(MSG_DEBUG, "P2P: p2p_no_group_iface"); - if (client) + if (cli_wpa_s) return P2PS_SETUP_NONE; if (go_wpa_s) { @@ -689,10 +707,20 @@ static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role) /* If a required role has been specified, handle it here */ if (role && role != P2PS_SETUP_NEW) { switch (incoming) { + case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW: + case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT: + /* + * Peer has an active GO, so if the role allows it and + * we do not have any active roles, become client. + */ + if ((role & P2PS_SETUP_CLIENT) && !go_wpa_s && + !cli_wpa_s) + return P2PS_SETUP_CLIENT; + + /* fall through */ + case P2PS_SETUP_NONE: case P2PS_SETUP_NEW: - case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT: - case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW: conncap = role; goto grp_owner; @@ -701,7 +729,7 @@ static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role) * Must be a complimentary role - cannot be a client to * more than one peer. */ - if (incoming == role || client) + if (incoming == role || cli_wpa_s) return P2PS_SETUP_NONE; return P2PS_SETUP_CLIENT; @@ -727,7 +755,7 @@ static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role) switch (incoming) { case P2PS_SETUP_NONE: case P2PS_SETUP_NEW: - if (client) + if (cli_wpa_s) conncap = P2PS_SETUP_GROUP_OWNER; else if (!owned_members) conncap = P2PS_SETUP_NEW; @@ -742,13 +770,13 @@ static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role) break; case P2PS_SETUP_GROUP_OWNER: - if (!client) + if (!cli_wpa_s) conncap = P2PS_SETUP_CLIENT; break; case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW: case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT: - if (client) + if (cli_wpa_s) conncap = P2PS_SETUP_GROUP_OWNER; else { u8 r; @@ -770,15 +798,14 @@ grp_owner: (!incoming && (conncap & P2PS_SETUP_NEW))) { if (go_wpa_s && p2p_client_limit_reached(go_wpa_s->p2p_group)) conncap &= ~P2PS_SETUP_GROUP_OWNER; - wpa_printf(MSG_DEBUG, "P2P: GOs:%d members:%d conncap:%d", - owner, owned_members, conncap); s = wpas_p2p_get_persistent_go(wpa_s); - - if (!s && !owner && p2p_no_group_iface) { + if (!s && !go_wpa_s && p2p_no_group_iface) { p2p_set_intended_addr(wpa_s->global->p2p, + wpa_s->p2p_mgmt ? + wpa_s->parent->own_addr : wpa_s->own_addr); - } else if (!s && !owner) { + } else if (!s && !go_wpa_s) { if (wpas_p2p_add_group_interface(wpa_s, WPA_IF_P2P_GO) < 0) { wpa_printf(MSG_ERROR, @@ -850,7 +877,7 @@ static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, if (wpa_s->cross_connect_in_use) { wpa_s->cross_connect_in_use = 0; - wpa_msg_global(wpa_s->parent, MSG_INFO, + wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s", wpa_s->ifname, wpa_s->cross_connect_uplink); } @@ -881,7 +908,7 @@ static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, break; } if (removal_reason != P2P_GROUP_REMOVAL_SILENT) { - wpa_msg_global(wpa_s->parent, MSG_INFO, + wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_GROUP_REMOVED "%s %s%s", wpa_s->ifname, gtype, reason); } @@ -891,7 +918,7 @@ static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, if (eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL) > 0) wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group idle timeout"); if (eloop_cancel_timeout(wpas_p2p_group_formation_timeout, - wpa_s->parent, NULL) > 0) { + wpa_s->p2pdev, NULL) > 0) { wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group formation " "timeout"); wpa_s->p2p_in_provisioning = 0; @@ -926,6 +953,12 @@ static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, return 1; } + /* + * The primary interface was used for P2P group operations, so + * need to reset its p2pdev. + */ + wpa_s->p2pdev = wpa_s->parent; + if (!wpa_s->p2p_go_group_formation_completed) { wpa_s->global->p2p_group_formation = NULL; wpa_s->p2p_in_provisioning = 0; @@ -1043,7 +1076,7 @@ static int wpas_p2p_persistent_group(struct wpa_supplicant *wpa_s, "go_dev_addr=" MACSTR, MAC2STR(bssid), group_capab, MAC2STR(go_dev_addr)); - return group_capab & P2P_GROUP_CAPAB_PERSISTENT_GROUP; + return !!(group_capab & P2P_GROUP_CAPAB_PERSISTENT_GROUP); } @@ -1101,7 +1134,8 @@ static int wpas_p2p_store_persistent_group(struct wpa_supplicant *wpa_s, s->auth_alg = WPA_AUTH_ALG_OPEN; s->key_mgmt = WPA_KEY_MGMT_PSK; s->proto = WPA_PROTO_RSN; - s->pairwise_cipher = WPA_CIPHER_CCMP; + s->pbss = ssid->pbss; + s->pairwise_cipher = ssid->pbss ? WPA_CIPHER_GCMP : WPA_CIPHER_CCMP; s->export_keys = 1; if (ssid->passphrase) { os_free(s->passphrase); @@ -1241,7 +1275,7 @@ static void wpas_p2p_group_started(struct wpa_supplicant *wpa_s, * Include PSK/passphrase only in the control interface message and * leave it out from the debug log entry. */ - wpa_msg_global_ctrl(wpa_s->parent, MSG_INFO, + wpa_msg_global_ctrl(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_GROUP_STARTED "%s %s ssid=\"%s\" freq=%d%s%s%s%s%s go_dev_addr=" MACSTR "%s%s", @@ -1267,7 +1301,6 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s, int client; int persistent; u8 go_dev_addr[ETH_ALEN]; - int network_id = -1; /* * This callback is likely called for the main interface. Update wpa_s @@ -1284,7 +1317,7 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s, wpa_s->group_formation_reported = 1; if (!success) { - wpa_msg_global(wpa_s->parent, MSG_INFO, + wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_GROUP_FORMATION_FAILURE); wpas_notify_p2p_group_formation_failure(wpa_s, ""); if (already_deleted) @@ -1294,7 +1327,7 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s, return; } - wpa_msg_global(wpa_s->parent, MSG_INFO, + wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_GROUP_FORMATION_SUCCESS); ssid = wpa_s->current_ssid; @@ -1342,16 +1375,15 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s, } if (persistent) - network_id = wpas_p2p_store_persistent_group(wpa_s->parent, - ssid, go_dev_addr); + wpas_p2p_store_persistent_group(wpa_s->p2pdev, + ssid, go_dev_addr); else { os_free(wpa_s->global->add_psk); wpa_s->global->add_psk = NULL; } - if (network_id < 0 && ssid) - network_id = ssid->id; + if (!client) { - wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 0); + wpas_notify_p2p_group_started(wpa_s, ssid, persistent, 0); os_get_reltime(&wpa_s->global->p2p_go_wait_client); } } @@ -1368,6 +1400,25 @@ struct send_action_work { }; +static void wpas_p2p_free_send_action_work(struct wpa_supplicant *wpa_s) +{ + struct send_action_work *awork = wpa_s->p2p_send_action_work->ctx; + + wpa_printf(MSG_DEBUG, + "P2P: Free Action frame radio work @%p (freq=%u dst=" + MACSTR " src=" MACSTR " bssid=" MACSTR " wait_time=%u)", + wpa_s->p2p_send_action_work, awork->freq, + MAC2STR(awork->dst), MAC2STR(awork->src), + MAC2STR(awork->bssid), awork->wait_time); + wpa_hexdump(MSG_DEBUG, "P2P: Freeing pending Action frame", + awork->buf, awork->len); + os_free(awork); + wpa_s->p2p_send_action_work->ctx = NULL; + radio_work_done(wpa_s->p2p_send_action_work); + wpa_s->p2p_send_action_work = NULL; +} + + static void wpas_p2p_send_action_work_timeout(void *eloop_ctx, void *timeout_ctx) { @@ -1377,9 +1428,7 @@ static void wpas_p2p_send_action_work_timeout(void *eloop_ctx, return; wpa_printf(MSG_DEBUG, "P2P: Send Action frame radio work timed out"); - os_free(wpa_s->p2p_send_action_work->ctx); - radio_work_done(wpa_s->p2p_send_action_work); - wpa_s->p2p_send_action_work = NULL; + wpas_p2p_free_send_action_work(wpa_s); } @@ -1387,11 +1436,13 @@ static void wpas_p2p_action_tx_clear(struct wpa_supplicant *wpa_s) { if (wpa_s->p2p_send_action_work) { struct send_action_work *awork; + awork = wpa_s->p2p_send_action_work->ctx; + wpa_printf(MSG_DEBUG, + "P2P: Clear Action TX work @%p (wait_time=%u)", + wpa_s->p2p_send_action_work, awork->wait_time); if (awork->wait_time == 0) { - os_free(awork); - radio_work_done(wpa_s->p2p_send_action_work); - wpa_s->p2p_send_action_work = NULL; + wpas_p2p_free_send_action_work(wpa_s); } else { /* * In theory, this should not be needed, but number of @@ -1447,7 +1498,7 @@ static void wpas_p2p_send_action_tx_status(struct wpa_supplicant *wpa_s, wpa_s->pending_pd_before_join = 0; wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No ACK for PD Req " "during p2p_connect-auto"); - wpa_msg_global(wpa_s->parent, MSG_INFO, + wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_FALLBACK_TO_GO_NEG "reason=no-ACK-to-PD-Req"); wpas_p2p_fallback_to_go_neg(wpa_s, 0); @@ -1590,11 +1641,11 @@ static void wpas_start_wps_enrollee(struct wpa_supplicant *wpa_s, } else if (res->wps_method == WPS_NFC) { wpas_wps_start_nfc(wpa_s, res->peer_device_addr, res->peer_interface_addr, - wpa_s->parent->p2p_oob_dev_pw, - wpa_s->parent->p2p_oob_dev_pw_id, 1, - wpa_s->parent->p2p_oob_dev_pw_id == + wpa_s->p2pdev->p2p_oob_dev_pw, + wpa_s->p2pdev->p2p_oob_dev_pw_id, 1, + wpa_s->p2pdev->p2p_oob_dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER ? - wpa_s->parent->p2p_peer_oob_pubkey_hash : + wpa_s->p2pdev->p2p_peer_oob_pubkey_hash : NULL, NULL, 0, 0); #endif /* CONFIG_WPS_NFC */ @@ -1620,7 +1671,7 @@ static void wpas_p2p_add_psk_list(struct wpa_supplicant *wpa_s, if (!wpa_s->ap_iface) return; - persistent = wpas_p2p_get_persistent(wpa_s->parent, NULL, ssid->ssid, + persistent = wpas_p2p_get_persistent(wpa_s->p2pdev, NULL, ssid->ssid, ssid->ssid_len); if (persistent == NULL) return; @@ -1685,8 +1736,8 @@ static void p2p_go_save_group_common_freqs(struct wpa_supplicant *wpa_s, static void p2p_config_write(struct wpa_supplicant *wpa_s) { #ifndef CONFIG_NO_CONFIG_WRITE - if (wpa_s->parent->conf->update_config && - wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf)) + if (wpa_s->p2pdev->conf->update_config && + wpa_config_write(wpa_s->p2pdev->confname, wpa_s->p2pdev->conf)) wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration"); #endif /* CONFIG_NO_CONFIG_WRITE */ } @@ -1697,7 +1748,15 @@ static void p2p_go_configured(void *ctx, void *data) struct wpa_supplicant *wpa_s = ctx; struct p2p_go_neg_results *params = data; struct wpa_ssid *ssid; - int network_id = -1; + + wpa_s->ap_configured_cb = NULL; + wpa_s->ap_configured_cb_ctx = NULL; + wpa_s->ap_configured_cb_data = NULL; + if (!wpa_s->go_params) { + wpa_printf(MSG_ERROR, + "P2P: p2p_go_configured() called with wpa_s->go_params == NULL"); + return; + } p2p_go_save_group_common_freqs(wpa_s, params); p2p_go_dump_common_freqs(wpa_s); @@ -1715,8 +1774,8 @@ static void p2p_go_configured(void *ctx, void *data) params->persistent_group, ""); wpa_s->group_formation_reported = 1; - if (wpa_s->parent->p2ps_method_config_any) { - if (is_zero_ether_addr(wpa_s->parent->p2ps_join_addr)) { + if (wpa_s->p2pdev->p2ps_method_config_any) { + if (is_zero_ether_addr(wpa_s->p2pdev->p2ps_join_addr)) { wpa_dbg(wpa_s, MSG_DEBUG, "P2PS: Setting default PIN for ANY"); wpa_supplicant_ap_wps_pin(wpa_s, NULL, @@ -1725,24 +1784,24 @@ static void p2p_go_configured(void *ctx, void *data) } else { wpa_dbg(wpa_s, MSG_DEBUG, "P2PS: Setting default PIN for " MACSTR, - MAC2STR(wpa_s->parent->p2ps_join_addr)); + MAC2STR(wpa_s->p2pdev->p2ps_join_addr)); wpa_supplicant_ap_wps_pin( - wpa_s, wpa_s->parent->p2ps_join_addr, + wpa_s, wpa_s->p2pdev->p2ps_join_addr, "12345670", NULL, 0, 0); } - wpa_s->parent->p2ps_method_config_any = 0; + wpa_s->p2pdev->p2ps_method_config_any = 0; } os_get_reltime(&wpa_s->global->p2p_go_wait_client); if (params->persistent_group) { - network_id = wpas_p2p_store_persistent_group( - wpa_s->parent, ssid, + wpas_p2p_store_persistent_group( + wpa_s->p2pdev, ssid, wpa_s->global->p2p_dev_addr); wpas_p2p_add_psk_list(wpa_s, ssid); } - if (network_id < 0) - network_id = ssid->id; - wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 0); + + wpas_notify_p2p_group_started(wpa_s, ssid, + params->persistent_group, 0); wpas_p2p_cross_connect_setup(wpa_s); wpas_p2p_set_group_idle_timeout(wpa_s); @@ -1753,11 +1812,11 @@ static void p2p_go_configured(void *ctx, void *data) wpa_s->p2p_go_group_formation_completed = 0; wpa_s->global->p2p_group_formation = wpa_s; eloop_cancel_timeout(wpas_p2p_group_formation_timeout, - wpa_s->parent, NULL); + wpa_s->p2pdev, NULL); eloop_register_timeout( wpa_s->p2p_first_connection_timeout, 0, wpas_p2p_group_formation_timeout, - wpa_s->parent, NULL); + wpa_s->p2pdev, NULL); } return; @@ -1775,17 +1834,17 @@ static void p2p_go_configured(void *ctx, void *data) params->peer_device_addr); #ifdef CONFIG_WPS_NFC } else if (params->wps_method == WPS_NFC) { - if (wpa_s->parent->p2p_oob_dev_pw_id != + if (wpa_s->p2pdev->p2p_oob_dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER && - !wpa_s->parent->p2p_oob_dev_pw) { + !wpa_s->p2pdev->p2p_oob_dev_pw) { wpa_printf(MSG_DEBUG, "P2P: No NFC Dev Pw known"); return; } wpas_ap_wps_add_nfc_pw( - wpa_s, wpa_s->parent->p2p_oob_dev_pw_id, - wpa_s->parent->p2p_oob_dev_pw, - wpa_s->parent->p2p_peer_oob_pk_hash_known ? - wpa_s->parent->p2p_peer_oob_pubkey_hash : NULL); + wpa_s, wpa_s->p2pdev->p2p_oob_dev_pw_id, + wpa_s->p2pdev->p2p_oob_dev_pw, + wpa_s->p2pdev->p2p_peer_oob_pk_hash_known ? + wpa_s->p2pdev->p2p_peer_oob_pubkey_hash : NULL); #endif /* CONFIG_WPS_NFC */ } else if (wpa_s->p2p_pin[0]) wpa_supplicant_ap_wps_pin(wpa_s, params->peer_interface_addr, @@ -1822,12 +1881,14 @@ static void wpas_start_wps_go(struct wpa_supplicant *wpa_s, wpa_config_set_network_defaults(ssid); ssid->temporary = 1; ssid->p2p_group = 1; - ssid->p2p_persistent_group = params->persistent_group; + ssid->p2p_persistent_group = !!params->persistent_group; ssid->mode = group_formation ? WPAS_MODE_P2P_GROUP_FORMATION : WPAS_MODE_P2P_GO; ssid->frequency = params->freq; ssid->ht40 = params->ht40; ssid->vht = params->vht; + ssid->max_oper_chwidth = params->max_oper_chwidth; + ssid->vht_center_freq2 = params->vht_center_freq2; ssid->ssid = os_zalloc(params->ssid_len + 1); if (ssid->ssid) { os_memcpy(ssid->ssid, params->ssid, params->ssid_len); @@ -1845,6 +1906,8 @@ static void wpas_start_wps_go(struct wpa_supplicant *wpa_s, */ ssid->pairwise_cipher = WPA_CIPHER_GCMP; ssid->group_cipher = WPA_CIPHER_GCMP; + /* P2P GO in 60 GHz is always a PCP (PBSS) */ + ssid->pbss = 1; } if (os_strlen(params->passphrase) > 0) { ssid->passphrase = os_strdup(params->passphrase); @@ -1861,7 +1924,7 @@ static void wpas_start_wps_go(struct wpa_supplicant *wpa_s, os_memcpy(ssid->psk, params->psk, sizeof(ssid->psk)); else if (ssid->passphrase) wpa_config_update_psk(ssid); - ssid->ap_max_inactivity = wpa_s->parent->conf->p2p_go_max_inactivity; + ssid->ap_max_inactivity = wpa_s->p2pdev->conf->p2p_go_max_inactivity; wpa_s->ap_configured_cb = p2p_go_configured; wpa_s->ap_configured_cb_ctx = wpa_s; @@ -1885,7 +1948,12 @@ static void wpas_p2p_clone_config(struct wpa_supplicant *dst, d = dst->conf; s = src->conf; -#define C(n) if (s->n) d->n = os_strdup(s->n) +#define C(n) \ +do { \ + if (s->n && !d->n) \ + d->n = os_strdup(s->n); \ +} while (0) + C(device_name); C(manufacturer); C(model_name); @@ -1913,7 +1981,10 @@ static void wpas_p2p_clone_config(struct wpa_supplicant *dst, d->disable_scan_offload = s->disable_scan_offload; d->passive_scan = s->passive_scan; - if (s->wps_nfc_dh_privkey && s->wps_nfc_dh_pubkey) { + if (s->wps_nfc_dh_privkey && s->wps_nfc_dh_pubkey && + !d->wps_nfc_pw_from_config) { + wpabuf_free(d->wps_nfc_dh_privkey); + wpabuf_free(d->wps_nfc_dh_pubkey); d->wps_nfc_dh_privkey = wpabuf_dup(s->wps_nfc_dh_privkey); d->wps_nfc_dh_pubkey = wpabuf_dup(s->wps_nfc_dh_pubkey); } @@ -2071,7 +2142,7 @@ static void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s, int already_deleted) { eloop_cancel_timeout(wpas_p2p_group_formation_timeout, - wpa_s->parent, NULL); + wpa_s->p2pdev, NULL); if (wpa_s->global->p2p) p2p_group_formation_failed(wpa_s->global->p2p); wpas_group_formation_completed(wpa_s, 0, already_deleted); @@ -2082,9 +2153,9 @@ static void wpas_p2p_grpform_fail_after_wps(struct wpa_supplicant *wpa_s) { wpa_printf(MSG_DEBUG, "P2P: Reject group formation due to WPS provisioning failure"); eloop_cancel_timeout(wpas_p2p_group_formation_timeout, - wpa_s->parent, NULL); + wpa_s->p2pdev, NULL); eloop_register_timeout(0, 0, wpas_p2p_group_formation_timeout, - wpa_s->parent, NULL); + wpa_s->p2pdev, NULL); wpa_s->global->p2p_fail_on_wps_complete = 0; } @@ -2095,15 +2166,16 @@ void wpas_p2p_ap_setup_failed(struct wpa_supplicant *wpa_s) return; /* Speed up group formation timeout since this cannot succeed */ eloop_cancel_timeout(wpas_p2p_group_formation_timeout, - wpa_s->parent, NULL); + wpa_s->p2pdev, NULL); eloop_register_timeout(0, 0, wpas_p2p_group_formation_timeout, - wpa_s->parent, NULL); + wpa_s->p2pdev, NULL); } static void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res) { struct wpa_supplicant *wpa_s = ctx; + struct wpa_supplicant *group_wpa_s; if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) { wpa_drv_cancel_remain_on_channel(wpa_s); @@ -2129,6 +2201,8 @@ static void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res) res->ht40 = 1; if (wpa_s->p2p_go_vht) res->vht = 1; + res->max_oper_chwidth = wpa_s->p2p_go_max_oper_chwidth; + res->vht_center_freq2 = wpa_s->p2p_go_vht_center_freq2; wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_SUCCESS "role=%s " "freq=%d ht40=%d peer_dev=" MACSTR " peer_iface=" MACSTR @@ -2154,7 +2228,7 @@ static void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res) } if (wpa_s->create_p2p_iface) { - struct wpa_supplicant *group_wpa_s = + group_wpa_s = wpas_p2p_init_group_interface(wpa_s, res->role_go); if (group_wpa_s == NULL) { wpas_p2p_remove_pending_group_interface(wpa_s); @@ -2163,31 +2237,27 @@ static void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res) wpas_p2p_group_formation_failed(wpa_s, 1); return; } - if (group_wpa_s != wpa_s) { - os_memcpy(group_wpa_s->p2p_pin, wpa_s->p2p_pin, - sizeof(group_wpa_s->p2p_pin)); - group_wpa_s->p2p_wps_method = wpa_s->p2p_wps_method; - } os_memset(wpa_s->pending_interface_addr, 0, ETH_ALEN); wpa_s->pending_interface_name[0] = '\0'; - group_wpa_s->p2p_in_provisioning = 1; - - if (res->role_go) { - wpas_start_wps_go(group_wpa_s, res, 1); - } else { - os_get_reltime(&group_wpa_s->scan_min_time); - wpas_start_wps_enrollee(group_wpa_s, res); - } } else { - wpa_s->p2p_in_provisioning = 1; - wpa_s->global->p2p_group_formation = wpa_s; + group_wpa_s = wpa_s->parent; + wpa_s->global->p2p_group_formation = group_wpa_s; + if (group_wpa_s != wpa_s) + wpas_p2p_clone_config(group_wpa_s, wpa_s); + } - if (res->role_go) { - wpas_start_wps_go(wpa_s, res, 1); - } else { - os_get_reltime(&wpa_s->scan_min_time); - wpas_start_wps_enrollee(ctx, res); - } + group_wpa_s->p2p_in_provisioning = 1; + group_wpa_s->p2pdev = wpa_s; + if (group_wpa_s != wpa_s) { + os_memcpy(group_wpa_s->p2p_pin, wpa_s->p2p_pin, + sizeof(group_wpa_s->p2p_pin)); + group_wpa_s->p2p_wps_method = wpa_s->p2p_wps_method; + } + if (res->role_go) { + wpas_start_wps_go(group_wpa_s, res, 1); + } else { + os_get_reltime(&group_wpa_s->scan_min_time); + wpas_start_wps_enrollee(group_wpa_s, res); } wpa_s->p2p_long_listen = 0; @@ -2308,6 +2378,10 @@ static void wpas_dev_lost(void *ctx, const u8 *dev_addr) static void wpas_find_stopped(void *ctx) { struct wpa_supplicant *wpa_s = ctx; + + if (wpa_s->p2p_scan_work && wpas_abort_ongoing_scan(wpa_s) < 0) + wpa_printf(MSG_DEBUG, "P2P: Abort ongoing scan failed"); + wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_FIND_STOPPED); wpas_notify_p2p_find_stopped(wpa_s); } @@ -2521,7 +2595,13 @@ static void wpas_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods, params[sizeof(params) - 1] = '\0'; if (config_methods & WPS_CONFIG_DISPLAY) { - generated_pin = wps_generate_pin(); + if (wps_generate_pin(&generated_pin) < 0) { + wpa_printf(MSG_DEBUG, "P2P: Could not generate PIN"); + wpas_notify_p2p_provision_discovery( + wpa_s, peer, 0 /* response */, + P2P_PROV_DISC_INFO_UNAVAILABLE, 0, 0); + return; + } wpas_prov_disc_local_display(wpa_s, peer, params, generated_pin); } else if (config_methods & WPS_CONFIG_KEYPAD) @@ -2566,7 +2646,13 @@ static void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods) if (config_methods & WPS_CONFIG_DISPLAY) wpas_prov_disc_local_keypad(wpa_s, peer, params); else if (config_methods & WPS_CONFIG_KEYPAD) { - generated_pin = wps_generate_pin(); + if (wps_generate_pin(&generated_pin) < 0) { + wpa_printf(MSG_DEBUG, "P2P: Could not generate PIN"); + wpas_notify_p2p_provision_discovery( + wpa_s, peer, 0 /* response */, + P2P_PROV_DISC_INFO_UNAVAILABLE, 0, 0); + return; + } wpas_prov_disc_local_display(wpa_s, peer, params, generated_pin); } else if (config_methods & WPS_CONFIG_PUSHBUTTON) @@ -2589,7 +2675,7 @@ static void wpas_prov_disc_fail(void *ctx, const u8 *peer, if (wpa_s->p2p_fallback_to_go_neg) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: PD for p2p_connect-auto " "failed - fall back to GO Negotiation"); - wpa_msg_global(wpa_s->parent, MSG_INFO, + wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_FALLBACK_TO_GO_NEG "reason=PD-failed"); wpas_p2p_fallback_to_go_neg(wpa_s, 0); @@ -2685,6 +2771,29 @@ static int wpas_p2p_go_is_peer_freq(struct wpa_supplicant *wpa_s, int freq) } +static int wpas_sta_check_ecsa(struct hostapd_data *hapd, + struct sta_info *sta, void *ctx) +{ + int *ecsa_support = ctx; + + *ecsa_support &= sta->ecsa_supported; + + return 0; +} + + +/* Check if all the peers support eCSA */ +static int wpas_p2p_go_clients_support_ecsa(struct wpa_supplicant *wpa_s) +{ + int ecsa_support = 1; + + ap_for_each_sta(wpa_s->ap_iface->bss[0], wpas_sta_check_ecsa, + &ecsa_support); + + return ecsa_support; +} + + /** * Pick the best frequency to use from all the currently used frequencies. */ @@ -2811,7 +2920,11 @@ static u8 wpas_invitation_process(void *ctx, const u8 *sa, const u8 *bssid, "invitation"); return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; } - os_memcpy(group_bssid, wpa_s->own_addr, ETH_ALEN); + if (wpa_s->p2p_mgmt) + os_memcpy(group_bssid, wpa_s->parent->own_addr, + ETH_ALEN); + else + os_memcpy(group_bssid, wpa_s->own_addr, ETH_ALEN); } else if (s->mode == WPAS_MODE_P2P_GO) { *go = 1; if (wpas_p2p_add_group_interface(wpa_s, WPA_IF_P2P_GO) < 0) @@ -2893,12 +3006,31 @@ static void wpas_invitation_received(void *ctx, const u8 *sa, const u8 *bssid, MAC2STR(sa), op_freq, wpa_ssid_txt(ssid, ssid_len)); if (s) { int go = s->mode == WPAS_MODE_P2P_GO; + if (go) { + wpa_msg_global(wpa_s, MSG_INFO, + P2P_EVENT_INVITATION_ACCEPTED + "sa=" MACSTR + " persistent=%d freq=%d", + MAC2STR(sa), s->id, op_freq); + } else { + wpa_msg_global(wpa_s, MSG_INFO, + P2P_EVENT_INVITATION_ACCEPTED + "sa=" MACSTR + " persistent=%d", + MAC2STR(sa), s->id); + } wpas_p2p_group_add_persistent( - wpa_s, s, go, 0, op_freq, 0, 0, NULL, + wpa_s, s, go, 0, op_freq, 0, 0, 0, 0, NULL, go ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0, 1); } else if (bssid) { wpa_s->user_initiated_pd = 0; + wpa_msg_global(wpa_s, MSG_INFO, + P2P_EVENT_INVITATION_ACCEPTED + "sa=" MACSTR " go_dev_addr=" MACSTR + " bssid=" MACSTR " unknown-network", + MAC2STR(sa), MAC2STR(go_dev_addr), + MAC2STR(bssid)); wpas_p2p_join(wpa_s, bssid, go_dev_addr, wpa_s->p2p_wps_method, 0, op_freq, ssid, ssid_len); @@ -2999,7 +3131,7 @@ static void wpas_remove_persistent_client(struct wpa_supplicant *wpa_s, if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GO || !ssid->p2p_persistent_group) return; /* Not operating as a GO in persistent group */ - ssid = wpas_p2p_get_persistent(wpa_s->parent, peer, + ssid = wpas_p2p_get_persistent(wpa_s->p2pdev, peer, ssid->ssid, ssid->ssid_len); wpas_remove_persistent_peer(wpa_s, ssid, peer, 1); } @@ -3027,9 +3159,37 @@ static void wpas_invitation_result(void *ctx, int status, const u8 *bssid, wpa_printf(MSG_DEBUG, "P2P: Invitation result - status=%d peer=" MACSTR, status, MAC2STR(peer)); if (wpa_s->pending_invite_ssid_id == -1) { + struct wpa_supplicant *group_if = + wpa_s->global->p2p_invite_group; + if (status == P2P_SC_FAIL_UNKNOWN_GROUP) wpas_remove_persistent_client(wpa_s, peer); - return; /* Invitation to active group */ + + /* + * Invitation to an active group. If this is successful and we + * are the GO, set the client wait to postpone some concurrent + * operations and to allow provisioning and connection to happen + * more quickly. + */ + if (status == P2P_SC_SUCCESS && + group_if && group_if->current_ssid && + group_if->current_ssid->mode == WPAS_MODE_P2P_GO) { + os_get_reltime(&wpa_s->global->p2p_go_wait_client); +#ifdef CONFIG_TESTING_OPTIONS + if (group_if->p2p_go_csa_on_inv) { + wpa_printf(MSG_DEBUG, + "Testing: force P2P GO CSA after invitation"); + eloop_cancel_timeout( + wpas_p2p_reconsider_moving_go, + wpa_s, NULL); + eloop_register_timeout( + 0, 50000, + wpas_p2p_reconsider_moving_go, + wpa_s, NULL); + } +#endif /* CONFIG_TESTING_OPTIONS */ + } + return; } if (status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) { @@ -3083,7 +3243,9 @@ static void wpas_invitation_result(void *ctx, int status, const u8 *bssid, ssid->mode == WPAS_MODE_P2P_GO, wpa_s->p2p_persistent_go_freq, freq, + wpa_s->p2p_go_vht_center_freq2, wpa_s->p2p_go_ht40, wpa_s->p2p_go_vht, + wpa_s->p2p_go_max_oper_chwidth, channels, ssid->mode == WPAS_MODE_P2P_GO ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : @@ -3169,21 +3331,6 @@ static int wpas_p2p_default_channels(struct wpa_supplicant *wpa_s, } -static struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes, - u16 num_modes, - enum hostapd_hw_mode mode) -{ - u16 i; - - for (i = 0; i < num_modes; i++) { - if (modes[i].mode == mode) - return &modes[i]; - } - - return NULL; -} - - enum chan_allowed { NOT_ALLOWED, NO_IR, ALLOWED }; @@ -3217,49 +3364,12 @@ static int has_channel(struct wpa_global *global, } -struct p2p_oper_class_map { - enum hostapd_hw_mode mode; - u8 op_class; - u8 min_chan; - u8 max_chan; - u8 inc; - enum { BW20, BW40PLUS, BW40MINUS, BW80, BW2160 } bw; -}; - -static const struct p2p_oper_class_map op_class[] = { - { HOSTAPD_MODE_IEEE80211G, 81, 1, 13, 1, BW20 }, -#if 0 /* Do not enable HT40 on 2 GHz for now */ - { HOSTAPD_MODE_IEEE80211G, 83, 1, 9, 1, BW40PLUS }, - { HOSTAPD_MODE_IEEE80211G, 84, 5, 13, 1, BW40MINUS }, -#endif - { HOSTAPD_MODE_IEEE80211A, 115, 36, 48, 4, BW20 }, - { HOSTAPD_MODE_IEEE80211A, 124, 149, 161, 4, BW20 }, - { HOSTAPD_MODE_IEEE80211A, 125, 149, 169, 4, BW20 }, - { HOSTAPD_MODE_IEEE80211A, 116, 36, 44, 8, BW40PLUS }, - { HOSTAPD_MODE_IEEE80211A, 117, 40, 48, 8, BW40MINUS }, - { HOSTAPD_MODE_IEEE80211A, 126, 149, 157, 8, BW40PLUS }, - { HOSTAPD_MODE_IEEE80211A, 127, 153, 161, 8, BW40MINUS }, - - /* - * IEEE P802.11ac/D7.0 Table E-4 actually talks about channel center - * frequency index 42, 58, 106, 122, 138, 155 with channel spacing of - * 80 MHz, but currently use the following definition for simplicity - * (these center frequencies are not actual channels, which makes - * has_channel() fail). wpas_p2p_verify_80mhz() should take care of - * removing invalid channels. - */ - { HOSTAPD_MODE_IEEE80211A, 128, 36, 161, 4, BW80 }, - { HOSTAPD_MODE_IEEE80211AD, 180, 1, 4, 1, BW2160 }, - { -1, 0, 0, 0, 0, BW20 } -}; - - static int wpas_p2p_get_center_80mhz(struct wpa_supplicant *wpa_s, struct hostapd_hw_modes *mode, u8 channel) { u8 center_channels[] = { 42, 58, 106, 122, 138, 155 }; - unsigned int i; + size_t i; if (mode->mode != HOSTAPD_MODE_IEEE80211A) return 0; @@ -3315,6 +3425,75 @@ static enum chan_allowed wpas_p2p_verify_80mhz(struct wpa_supplicant *wpa_s, } +static int wpas_p2p_get_center_160mhz(struct wpa_supplicant *wpa_s, + struct hostapd_hw_modes *mode, + u8 channel) +{ + u8 center_channels[] = { 50, 114 }; + unsigned int i; + + if (mode->mode != HOSTAPD_MODE_IEEE80211A) + return 0; + + for (i = 0; i < ARRAY_SIZE(center_channels); i++) + /* + * In 160 MHz, the bandwidth "spans" 28 channels (e.g., 36-64), + * so the center channel is 14 channels away from the start/end. + */ + if (channel >= center_channels[i] - 14 && + channel <= center_channels[i] + 14) + return center_channels[i]; + + return 0; +} + + +static enum chan_allowed wpas_p2p_verify_160mhz(struct wpa_supplicant *wpa_s, + struct hostapd_hw_modes *mode, + u8 channel, u8 bw) +{ + u8 center_chan; + int i, flags; + enum chan_allowed res, ret = ALLOWED; + + center_chan = wpas_p2p_get_center_160mhz(wpa_s, mode, channel); + if (!center_chan) + return NOT_ALLOWED; + /* VHT 160 MHz uses DFS channels in most countries. */ + + /* Check all the channels are available */ + for (i = 0; i < 8; i++) { + int adj_chan = center_chan - 14 + i * 4; + + res = has_channel(wpa_s->global, mode, adj_chan, &flags); + if (res == NOT_ALLOWED) + return NOT_ALLOWED; + + if (res == NO_IR) + ret = NO_IR; + + if (i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_150)) + return NOT_ALLOWED; + if (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_130)) + return NOT_ALLOWED; + if (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_110)) + return NOT_ALLOWED; + if (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_90)) + return NOT_ALLOWED; + if (i == 4 && !(flags & HOSTAPD_CHAN_VHT_90_70)) + return NOT_ALLOWED; + if (i == 5 && !(flags & HOSTAPD_CHAN_VHT_110_50)) + return NOT_ALLOWED; + if (i == 6 && !(flags & HOSTAPD_CHAN_VHT_130_30)) + return NOT_ALLOWED; + if (i == 7 && !(flags & HOSTAPD_CHAN_VHT_150_10)) + return NOT_ALLOWED; + } + + return ret; +} + + static enum chan_allowed wpas_p2p_verify_channel(struct wpa_supplicant *wpa_s, struct hostapd_hw_modes *mode, u8 channel, u8 bw) @@ -3333,6 +3512,8 @@ static enum chan_allowed wpas_p2p_verify_channel(struct wpa_supplicant *wpa_s, res2 = has_channel(wpa_s->global, mode, channel + 4, NULL); } else if (bw == BW80) { res2 = wpas_p2p_verify_80mhz(wpa_s, mode, channel, bw); + } else if (bw == BW160) { + res2 = wpas_p2p_verify_160mhz(wpa_s, mode, channel, bw); } if (res == NOT_ALLOWED || res2 == NOT_ALLOWED) @@ -3359,11 +3540,14 @@ static int wpas_p2p_setup_channels(struct wpa_supplicant *wpa_s, cla = cli_cla = 0; - for (op = 0; op_class[op].op_class; op++) { - const struct p2p_oper_class_map *o = &op_class[op]; + for (op = 0; global_op_class[op].op_class; op++) { + const struct oper_class_map *o = &global_op_class[op]; u8 ch; struct p2p_reg_class *reg = NULL, *cli_reg = NULL; + if (o->p2p == NO_P2P_SUPP) + continue; + mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, o->mode); if (mode == NULL) continue; @@ -3418,10 +3602,13 @@ int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s, int op; enum chan_allowed ret; - for (op = 0; op_class[op].op_class; op++) { - const struct p2p_oper_class_map *o = &op_class[op]; + for (op = 0; global_op_class[op].op_class; op++) { + const struct oper_class_map *o = &global_op_class[op]; u8 ch; + if (o->p2p == NO_P2P_SUPP) + continue; + for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) { if (o->mode != HOSTAPD_MODE_IEEE80211A || (o->bw != BW40PLUS && o->bw != BW40MINUS) || @@ -3446,6 +3633,15 @@ int wpas_p2p_get_vht80_center(struct wpa_supplicant *wpa_s, } +int wpas_p2p_get_vht160_center(struct wpa_supplicant *wpa_s, + struct hostapd_hw_modes *mode, u8 channel) +{ + if (!wpas_p2p_verify_channel(wpa_s, mode, channel, BW160)) + return 0; + return wpas_p2p_get_center_160mhz(wpa_s, mode, channel); +} + + static int wpas_get_noa(void *ctx, const u8 *interface_addr, u8 *buf, size_t buf_len) { @@ -3577,6 +3773,7 @@ int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s, return -1; } + p2pdev_wpa_s->p2pdev = p2pdev_wpa_s; wpa_s->pending_interface_name[0] = '\0'; return 0; } @@ -3638,11 +3835,12 @@ static int wpas_get_persistent_group(void *ctx, const u8 *addr, const u8 *ssid, static int wpas_get_go_info(void *ctx, u8 *intended_addr, - u8 *ssid, size_t *ssid_len, int *group_iface) + u8 *ssid, size_t *ssid_len, int *group_iface, + unsigned int *freq) { struct wpa_supplicant *wpa_s = ctx; + struct wpa_supplicant *go; struct wpa_ssid *s; - u8 bssid[ETH_ALEN]; /* * group_iface will be set to 1 only if a dedicated interface for P2P @@ -3652,17 +3850,25 @@ static int wpas_get_go_info(void *ctx, u8 *intended_addr, * that the pending interface should be used. */ *group_iface = 0; - s = wpas_p2p_group_go_ssid(wpa_s, bssid); - if (!s) { + + if (freq) + *freq = 0; + + go = wpas_p2p_get_go_group(wpa_s); + if (!go) { s = wpas_p2p_get_persistent_go(wpa_s); *group_iface = wpas_p2p_create_iface(wpa_s); if (s) - os_memcpy(bssid, s->bssid, ETH_ALEN); + os_memcpy(intended_addr, s->bssid, ETH_ALEN); else return 0; + } else { + s = go->current_ssid; + os_memcpy(intended_addr, go->own_addr, ETH_ALEN); + if (freq) + *freq = go->assoc_freq; } - os_memcpy(intended_addr, bssid, ETH_ALEN); os_memcpy(ssid, s->ssid, s->ssid_len); *ssid_len = s->ssid_len; @@ -3750,11 +3956,13 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, const u8 *persist_ssid, size_t persist_ssid_size, int response_done, int prov_start, const char *session_info, - const u8 *feat_cap, size_t feat_cap_len) + const u8 *feat_cap, size_t feat_cap_len, + unsigned int freq, + const u8 *group_ssid, size_t group_ssid_len) { struct wpa_supplicant *wpa_s = ctx; u8 mac[ETH_ALEN]; - struct wpa_ssid *persistent_go, *stale, *s; + struct wpa_ssid *persistent_go, *stale, *s = NULL; int save_config = 0; struct wpa_supplicant *go_wpa_s; char feat_cap_str[256]; @@ -3825,8 +4033,9 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, } /* Clean up stale persistent groups with this device */ - s = wpas_p2p_get_persistent(wpa_s, dev, persist_ssid, - persist_ssid_size); + if (persist_ssid && persist_ssid_size) + s = wpas_p2p_get_persistent(wpa_s, dev, persist_ssid, + persist_ssid_size); if (persist_ssid && s && s->mode != WPAS_MODE_P2P_GO && is_zero_ether_addr(grp_mac)) { @@ -3908,6 +4117,7 @@ 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 (!wpas_p2p_create_iface(wpa_s)) os_memcpy(go_ifname, wpa_s->ifname, @@ -3922,7 +4132,8 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, wpa_s, P2P_SC_FAIL_UNKNOWN_GROUP, dev, adv_mac, ses_mac, grp_mac, adv_id, ses_id, 0, 0, - NULL, 0, 0, 0, NULL, NULL, 0); + NULL, 0, 0, 0, NULL, NULL, 0, 0, + NULL, 0); return; } @@ -3930,13 +4141,13 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, if (response_done && persistent_go) { wpas_p2p_group_add_persistent( wpa_s, persistent_go, - 0, 0, 0, 0, 0, NULL, + 0, 0, freq, 0, 0, 0, 0, NULL, persistent_go->mode == WPAS_MODE_P2P_GO ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0, 0); } else if (response_done) { - wpas_p2p_group_add(wpa_s, 1, 0, 0, 0); + wpas_p2p_group_add(wpa_s, 1, freq, 0, 0, 0, 0); } if (passwd_id == DEV_PW_P2PS_DEFAULT) { @@ -3989,16 +4200,24 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, } if (conncap == P2PS_SETUP_CLIENT) { + char ssid_hex[32 * 2 + 1]; + + if (group_ssid) + wpa_snprintf_hex(ssid_hex, sizeof(ssid_hex), + group_ssid, group_ssid_len); + else + ssid_hex[0] = '\0'; wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_P2PS_PROVISION_DONE MACSTR " status=%d conncap=%x" " adv_id=%x adv_mac=" MACSTR " session=%x mac=" MACSTR - " dev_passwd_id=%d join=" MACSTR "%s", + " dev_passwd_id=%d join=" MACSTR "%s%s%s", MAC2STR(dev), status, conncap, adv_id, MAC2STR(adv_mac), ses_id, MAC2STR(ses_mac), - passwd_id, MAC2STR(grp_mac), feat_cap_str); + passwd_id, MAC2STR(grp_mac), feat_cap_str, + group_ssid ? " group_ssid=" : "", ssid_hex); } else { wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_P2PS_PROVISION_DONE MACSTR @@ -4025,10 +4244,13 @@ static int wpas_prov_disc_resp_cb(void *ctx) { struct wpa_supplicant *wpa_s = ctx; struct wpa_ssid *persistent_go; + unsigned int freq; if (!wpa_s->global->pending_p2ps_group) return 0; + freq = wpa_s->global->pending_p2ps_group_freq; + wpa_s->global->pending_p2ps_group_freq = 0; wpa_s->global->pending_p2ps_group = 0; if (wpas_p2p_get_go_group(wpa_s)) @@ -4037,11 +4259,11 @@ static int wpas_prov_disc_resp_cb(void *ctx) if (persistent_go) { wpas_p2p_group_add_persistent( - wpa_s, persistent_go, 0, 0, 0, 0, 0, NULL, + wpa_s, persistent_go, 0, 0, 0, 0, 0, 0, 0, NULL, persistent_go->mode == WPAS_MODE_P2P_GO ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0, 0); } else { - wpas_p2p_group_add(wpa_s, 1, 0, 0, 0); + wpas_p2p_group_add(wpa_s, 1, freq, 0, 0, 0, 0); } return 1; @@ -4333,8 +4555,7 @@ static void wpas_p2p_deinit_global(struct wpa_global *global) static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s) { - if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) && - wpa_s->conf->p2p_no_group_iface) + if (wpa_s->conf->p2p_no_group_iface) return 0; /* separate interface disabled per configuration */ if (wpa_s->drv_flags & (WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE | @@ -4415,7 +4636,7 @@ static void wpas_p2p_check_join_scan_limit(struct wpa_supplicant *wpa_s) MAC2STR(wpa_s->pending_join_dev_addr)); return; } - wpa_msg_global(wpa_s->parent, MSG_INFO, + wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_GROUP_FORMATION_FAILURE); wpas_notify_p2p_group_formation_failure(wpa_s, ""); } @@ -4551,7 +4772,7 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, if (join < 0) { wpa_printf(MSG_DEBUG, "P2P: Peer was not found to be " "running a GO -> use GO Negotiation"); - wpa_msg_global(wpa_s->parent, MSG_INFO, + wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_FALLBACK_TO_GO_NEG "reason=peer-not-running-GO"); wpas_p2p_connect(wpa_s, wpa_s->pending_join_dev_addr, @@ -4559,10 +4780,13 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, wpa_s->p2p_persistent_group, 0, 0, 0, wpa_s->p2p_go_intent, wpa_s->p2p_connect_freq, + wpa_s->p2p_go_vht_center_freq2, wpa_s->p2p_persistent_id, wpa_s->p2p_pd_before_go_neg, wpa_s->p2p_go_ht40, - wpa_s->p2p_go_vht); + wpa_s->p2p_go_vht, + wpa_s->p2p_go_max_oper_chwidth, + NULL, 0); return; } @@ -4570,7 +4794,7 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, "try to join the group", join ? "" : " in older scan"); if (!join) { - wpa_msg_global(wpa_s->parent, MSG_INFO, + wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_FALLBACK_TO_GO_NEG_ENABLED); wpa_s->p2p_fallback_to_go_neg = 1; } @@ -4608,8 +4832,7 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, bss = wpa_bss_get(wpa_s, wpa_s->pending_join_iface_addr, wpa_s->p2p_join_ssid, wpa_s->p2p_join_ssid_len); - } - if (!bss) { + } else if (!bss) { wpa_printf(MSG_DEBUG, "P2P: Trying to find target GO BSS entry based on BSSID " MACSTR, MAC2STR(wpa_s->pending_join_iface_addr)); bss = wpa_bss_get_bssid_latest(wpa_s, @@ -4640,7 +4863,7 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, u16 method; if (wpas_check_freq_conflict(wpa_s, freq) > 0) { - wpa_msg_global(wpa_s->parent, MSG_INFO, + wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_GROUP_FORMATION_FAILURE "reason=FREQ_CONFLICT"); wpas_notify_p2p_group_formation_failure( @@ -4708,7 +4931,8 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, start: /* Start join operation immediately */ - wpas_p2p_join_start(wpa_s, 0, NULL, 0); + wpas_p2p_join_start(wpa_s, 0, wpa_s->p2p_join_ssid, + wpa_s->p2p_join_ssid_len); } @@ -4720,6 +4944,7 @@ static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq, struct wpabuf *wps_ie, *ies; size_t ielen; int freqs[2] = { 0, 0 }; + unsigned int bands; os_memset(¶ms, 0, sizeof(params)); @@ -4745,22 +4970,6 @@ static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq, return; } - ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p); - ies = wpabuf_alloc(wpabuf_len(wps_ie) + ielen); - if (ies == NULL) { - wpabuf_free(wps_ie); - wpas_p2p_scan_res_join(wpa_s, NULL); - return; - } - wpabuf_put_buf(ies, wps_ie); - wpabuf_free(wps_ie); - - p2p_scan_ie(wpa_s->global->p2p, ies, NULL); - - params.p2p_probe = 1; - params.extra_ies = wpabuf_head(ies); - params.extra_ies_len = wpabuf_len(ies); - if (!freq) { int oper_freq; /* @@ -4777,6 +4986,23 @@ static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq, params.freqs = freqs; } + ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p); + ies = wpabuf_alloc(wpabuf_len(wps_ie) + ielen); + if (ies == NULL) { + wpabuf_free(wps_ie); + wpas_p2p_scan_res_join(wpa_s, NULL); + return; + } + wpabuf_put_buf(ies, wps_ie); + wpabuf_free(wps_ie); + + bands = wpas_get_bands(wpa_s, freqs); + p2p_scan_ie(wpa_s->global->p2p, ies, NULL, bands); + + params.p2p_probe = 1; + params.extra_ies = wpabuf_head(ies); + params.extra_ies_len = wpabuf_len(ies); + /* * Run a scan to update BSS table and start Provision Discovery once * the new scan results become available. @@ -4874,8 +5100,13 @@ static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s, int freq, res.ssid_len = ssid_len; os_memcpy(res.ssid, ssid, ssid_len); } else { - bss = wpa_bss_get_bssid_latest(wpa_s, - wpa_s->pending_join_iface_addr); + if (ssid && ssid_len) { + bss = wpa_bss_get(wpa_s, wpa_s->pending_join_iface_addr, + ssid, ssid_len); + } else { + bss = wpa_bss_get_bssid_latest( + wpa_s, wpa_s->pending_join_iface_addr); + } if (bss) { res.freq = bss->freq; res.ssid_len = bss->ssid_len; @@ -4883,6 +5114,11 @@ static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s, int freq, wpa_printf(MSG_DEBUG, "P2P: Join target GO operating frequency from BSS table: %d MHz (SSID %s)", bss->freq, wpa_ssid_txt(bss->ssid, bss->ssid_len)); + } else if (ssid && ssid_len) { + res.ssid_len = ssid_len; + os_memcpy(res.ssid, ssid, ssid_len); + wpa_printf(MSG_DEBUG, "P2P: Join target GO (SSID %s)", + wpa_ssid_txt(ssid, ssid_len)); } } @@ -5067,12 +5303,17 @@ exit_free: * initiating Group Owner negotiation * @go_intent: GO Intent or -1 to use default * @freq: Frequency for the group or 0 for auto-selection + * @freq2: Center frequency of segment 1 for the GO operating in VHT 80P80 mode * @persistent_id: Persistent group credentials to use for forcing GO * parameters or -1 to generate new values (SSID/passphrase) * @pd: Whether to send Provision Discovery prior to GO Negotiation as an * interoperability workaround when initiating group formation * @ht40: Start GO with 40 MHz channel width * @vht: Start GO with VHT support + * @vht_chwidth: Channel width supported by GO operating with VHT support + * (VHT_CHANWIDTH_*). + * @group_ssid: Specific Group SSID for join or %NULL if not set + * @group_ssid_len: Length of @group_ssid in octets * Returns: 0 or new PIN (if pin was %NULL) on success, -1 on unspecified * failure, -2 on failure due to channel not currently available, * -3 if forced channel is not supported @@ -5080,8 +5321,10 @@ exit_free: int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, const char *pin, enum p2p_wps_method wps_method, int persistent_group, int auto_join, int join, int auth, - int go_intent, int freq, int persistent_id, int pd, - int ht40, int vht) + int go_intent, int freq, unsigned int vht_center_freq2, + int persistent_id, int pd, int ht40, int vht, + unsigned int vht_chwidth, const u8 *group_ssid, + size_t group_ssid_len) { int force_freq = 0, pref_freq = 0; int ret = 0, res; @@ -5105,6 +5348,7 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, wpa_s->global->p2p_fail_on_wps_complete = 0; wpa_s->global->pending_p2ps_group = 0; + wpa_s->global->pending_p2ps_group_freq = 0; wpa_s->p2ps_method_config_any = 0; if (go_intent < 0) @@ -5122,17 +5366,23 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, wpa_s->p2p_pd_before_go_neg = !!pd; wpa_s->p2p_go_ht40 = !!ht40; wpa_s->p2p_go_vht = !!vht; + wpa_s->p2p_go_vht_center_freq2 = vht_center_freq2; + wpa_s->p2p_go_max_oper_chwidth = vht_chwidth; if (pin) os_strlcpy(wpa_s->p2p_pin, pin, sizeof(wpa_s->p2p_pin)); else if (wps_method == WPS_PIN_DISPLAY) { - ret = wps_generate_pin(); + if (wps_generate_pin((unsigned int *) &ret) < 0) + return -1; res = os_snprintf(wpa_s->p2p_pin, sizeof(wpa_s->p2p_pin), "%08d", ret); if (os_snprintf_error(sizeof(wpa_s->p2p_pin), res)) wpa_s->p2p_pin[sizeof(wpa_s->p2p_pin) - 1] = '\0'; wpa_printf(MSG_DEBUG, "P2P: Randomly generated PIN: %s", wpa_s->p2p_pin); + } else if (wps_method == WPS_P2PS) { + /* Force the P2Ps default PIN to be used */ + os_strlcpy(wpa_s->p2p_pin, "12345670", sizeof(wpa_s->p2p_pin)); } else wpa_s->p2p_pin[0] = '\0'; @@ -5161,7 +5411,8 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, } wpa_s->user_initiated_pd = 1; if (wpas_p2p_join(wpa_s, iface_addr, dev_addr, wps_method, - auto_join, freq, NULL, 0) < 0) + auto_join, freq, + group_ssid, group_ssid_len) < 0) return -1; return ret; } @@ -5191,7 +5442,10 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, if_addr = wpa_s->pending_interface_addr; } else { - if_addr = wpa_s->own_addr; + if (wpa_s->p2p_mgmt) + if_addr = wpa_s->parent->own_addr; + else + if_addr = wpa_s->own_addr; os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN); } @@ -5520,29 +5774,51 @@ out: static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s, struct p2p_go_neg_results *params, - int freq, int ht40, int vht, + int freq, int vht_center_freq2, int ht40, + int vht, int max_oper_chwidth, const struct p2p_channels *channels) { struct wpa_used_freq_data *freqs; unsigned int cand; unsigned int num, i; + int ignore_no_freqs = 0; + int unused_channels = wpas_p2p_num_unused_channels(wpa_s) > 0; os_memset(params, 0, sizeof(*params)); params->role_go = 1; params->ht40 = ht40; params->vht = vht; - - if (wpa_s->p2p_group_common_freqs_num) - wpa_printf(MSG_DEBUG, "P2P: %s called for an active GO", - __func__); + params->max_oper_chwidth = max_oper_chwidth; + params->vht_center_freq2 = vht_center_freq2; freqs = os_calloc(wpa_s->num_multichan_concurrent, sizeof(struct wpa_used_freq_data)); if (!freqs) return -1; - num = wpas_p2p_valid_oper_freqs(wpa_s, freqs, - wpa_s->num_multichan_concurrent); + num = get_shared_radio_freqs_data(wpa_s, freqs, + wpa_s->num_multichan_concurrent); + + if (wpa_s->current_ssid && + wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO && + wpa_s->wpa_state == WPA_COMPLETED) { + wpa_printf(MSG_DEBUG, "P2P: %s called for an active GO", + __func__); + + /* + * If the frequency selection is done for an active P2P GO that + * is not sharing a frequency, allow to select a new frequency + * even if there are no unused frequencies as we are about to + * move the P2P GO so its frequency can be re-used. + */ + for (i = 0; i < num; i++) { + if (freqs[i].freq == wpa_s->current_ssid->frequency && + freqs[i].flags == 0) { + ignore_no_freqs = 1; + break; + } + } + } /* try using the forced freq */ if (freq) { @@ -5563,7 +5839,7 @@ static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s, } } - if (wpas_p2p_num_unused_channels(wpa_s) <= 0) { + if (!ignore_no_freqs && !unused_channels) { wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on freq (%d MHz) as all the channels are in use", freq); @@ -5578,12 +5854,13 @@ static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s, } /* consider using one of the shared frequencies */ - if (num) { + if (num && + (!wpa_s->conf->p2p_ignore_shared_freq || !unused_channels)) { cand = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num); if (wpas_p2p_supported_freq_go(wpa_s, channels, cand)) { wpa_printf(MSG_DEBUG, "P2P: Use shared freq (%d MHz) for GO", - freq); + cand); params->freq = cand; goto success; } @@ -5594,14 +5871,14 @@ static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s, freqs[i].freq)) { wpa_printf(MSG_DEBUG, "P2P: Use shared freq (%d MHz) for GO", - freq); + freqs[i].freq); params->freq = freqs[i].freq; goto success; } } } - if (wpas_p2p_num_unused_channels(wpa_s) <= 0) { + if (!ignore_no_freqs && !unused_channels) { wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on any of the channels we are already using"); goto fail; @@ -5714,9 +5991,20 @@ wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated, struct wpa_supplicant *group_wpa_s; if (!wpas_p2p_create_iface(wpa_s)) { - wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use same interface for group " - "operations"); + if (wpa_s->p2p_mgmt) { + /* + * We may be called on the p2p_dev interface which + * cannot be used for group operations, so always use + * the primary interface. + */ + wpa_s->parent->p2pdev = wpa_s; + wpa_s = wpa_s->parent; + } + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Use primary interface for group operations"); wpa_s->p2p_first_connection_timeout = 0; + if (wpa_s != wpa_s->p2pdev) + wpas_p2p_clone_config(wpa_s, wpa_s->p2pdev); return wpa_s; } @@ -5746,15 +6034,18 @@ wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated, * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface() * @persistent_group: Whether to create a persistent group * @freq: Frequency for the group or 0 to indicate no hardcoding + * @vht_center_freq2: segment_1 center frequency for GO operating in VHT 80P80 * @ht40: Start GO with 40 MHz channel width * @vht: Start GO with VHT support + * @vht_chwidth: channel bandwidth for GO operating with VHT support * Returns: 0 on success, -1 on failure * * This function creates a new P2P group with the local end as the Group Owner, * i.e., without using Group Owner Negotiation. */ int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group, - int freq, int ht40, int vht) + int freq, int vht_center_freq2, int ht40, int vht, + int max_oper_chwidth) { struct p2p_go_neg_results params; @@ -5772,7 +6063,8 @@ int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group, if (freq < 0) return -1; - if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq, ht40, vht, NULL)) + if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq, vht_center_freq2, + ht40, vht, max_oper_chwidth, NULL)) return -1; if (params.freq && !p2p_supported_freq_go(wpa_s->global->p2p, params.freq)) { @@ -5826,8 +6118,10 @@ static int wpas_start_p2p_client(struct wpa_supplicant *wpa_s, wpa_config_set_network_defaults(ssid); ssid->temporary = 1; ssid->proto = WPA_PROTO_RSN; - ssid->pairwise_cipher = WPA_CIPHER_CCMP; - ssid->group_cipher = WPA_CIPHER_CCMP; + ssid->pbss = params->pbss; + ssid->pairwise_cipher = params->pbss ? WPA_CIPHER_GCMP : + WPA_CIPHER_CCMP; + ssid->group_cipher = params->pbss ? WPA_CIPHER_GCMP : WPA_CIPHER_CCMP; ssid->key_mgmt = WPA_KEY_MGMT_PSK; ssid->ssid = os_malloc(params->ssid_len); if (ssid->ssid == NULL) { @@ -5848,12 +6142,14 @@ static int wpas_start_p2p_client(struct wpa_supplicant *wpa_s, wpa_s->show_group_started = 1; wpa_s->p2p_in_invitation = 1; wpa_s->p2p_invite_go_freq = freq; + wpa_s->p2p_go_group_formation_completed = 0; + wpa_s->global->p2p_group_formation = wpa_s; - eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->parent, + eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->p2pdev, NULL); eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0, wpas_p2p_group_formation_timeout, - wpa_s->parent, NULL); + wpa_s->p2pdev, NULL); wpa_supplicant_select_network(wpa_s, ssid); return 0; @@ -5862,8 +6158,10 @@ static int wpas_start_p2p_client(struct wpa_supplicant *wpa_s, int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, int addr_allocated, - int force_freq, int neg_freq, int ht40, - int vht, const struct p2p_channels *channels, + int force_freq, int neg_freq, + int vht_center_freq2, int ht40, + int vht, int max_oper_chwidth, + const struct p2p_channels *channels, int connection_timeout, int force_scan) { struct p2p_go_neg_results params; @@ -5878,7 +6176,7 @@ int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, "already running"); if (go == 0 && eloop_cancel_timeout(wpas_p2p_group_formation_timeout, - wpa_s->parent, NULL)) { + wpa_s->p2pdev, NULL)) { /* * This can happen if Invitation Response frame was lost * and the peer (GO of a persistent group) tries to @@ -5891,7 +6189,7 @@ int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, "P2P: Reschedule group formation timeout since peer is still trying to invite us"); eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0, wpas_p2p_group_formation_timeout, - wpa_s->parent, NULL); + wpa_s->p2pdev, NULL); } return 0; } @@ -5937,7 +6235,8 @@ int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, return -1; } - if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq, ht40, vht, channels)) + if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq, vht_center_freq2, + ht40, vht, max_oper_chwidth, channels)) return -1; params.role_go = 1; @@ -6019,7 +6318,8 @@ struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s, struct p2p_group *group; struct p2p_group_config *cfg; - if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL || + !ssid->p2p_group) return NULL; cfg = os_zalloc(sizeof(*cfg)); @@ -6042,6 +6342,8 @@ struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s, cfg->cb_ctx = wpa_s; cfg->ie_update = wpas_p2p_ie_update; cfg->idle_update = wpas_p2p_idle_update; + cfg->ip_addr_alloc = WPA_GET_BE32(wpa_s->p2pdev->conf->ip_addr_start) + != 0; group = p2p_group_init(wpa_s->global->p2p, cfg); if (group == NULL) @@ -6073,7 +6375,7 @@ void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr, p2p_clear_provisioning_info(wpa_s->global->p2p, go_dev_addr); } - eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->parent, + eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->p2pdev, NULL); wpa_s->p2p_go_group_formation_completed = 1; if (ssid && ssid->mode == WPAS_MODE_INFRA) { @@ -6088,7 +6390,9 @@ void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr, P2P_MAX_INITIAL_CONN_WAIT); eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0, wpas_p2p_group_formation_timeout, - wpa_s->parent, NULL); + wpa_s->p2pdev, NULL); + /* Complete group formation on successful data connection. */ + wpa_s->p2p_go_group_formation_completed = 0; } else if (ssid) { /* * Use a separate timeout for initial data connection to @@ -6100,7 +6404,7 @@ void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr, P2P_MAX_INITIAL_CONN_WAIT_GO); eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT_GO, 0, wpas_p2p_group_formation_timeout, - wpa_s->parent, NULL); + wpa_s->p2pdev, NULL); /* * Complete group formation on first successful data connection */ @@ -6139,7 +6443,7 @@ void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s, wpa_s->global->p2p_fail_on_wps_complete = 1; eloop_deplete_timeout(0, 50000, wpas_p2p_group_formation_timeout, - wpa_s->parent, NULL); + wpa_s->p2pdev, NULL); } } @@ -6164,11 +6468,14 @@ int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr, u16 config_methods; wpa_s->global->pending_p2ps_group = 0; + wpa_s->global->pending_p2ps_group_freq = 0; wpa_s->p2p_fallback_to_go_neg = 0; wpa_s->pending_pd_use = NORMAL_PD; if (p2ps_prov && use == WPAS_P2P_PD_FOR_ASP) { p2ps_prov->conncap = p2ps_group_capability( - wpa_s, P2PS_SETUP_NONE, p2ps_prov->role); + wpa_s, P2PS_SETUP_NONE, p2ps_prov->role, + &p2ps_prov->force_freq, &p2ps_prov->pref_freq); + wpa_printf(MSG_DEBUG, "P2P: %s conncap: %d - ASP parsed: %x %x %d %s", __func__, p2ps_prov->conncap, @@ -6229,7 +6536,12 @@ static void wpas_p2p_clear_pending_action_tx(struct wpa_supplicant *wpa_s) if (!offchannel_pending_action_tx(wpa_s)) return; - wpas_p2p_action_tx_clear(wpa_s); + if (wpa_s->p2p_send_action_work) { + wpas_p2p_free_send_action_work(wpa_s); + eloop_cancel_timeout(wpas_p2p_send_action_work_timeout, + wpa_s, NULL); + offchannel_send_action_done(wpa_s); + } wpa_printf(MSG_DEBUG, "P2P: Drop pending Action TX due to new " "operation request"); @@ -6320,6 +6632,12 @@ int wpas_p2p_listen(struct wpa_supplicant *wpa_s, unsigned int timeout) if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; + if (wpa_s->p2p_lo_started) { + wpa_printf(MSG_DEBUG, + "P2P: Cannot start P2P listen, it is offloaded"); + return -1; + } + wpa_supplicant_cancel_sched_scan(wpa_s); wpas_p2p_clear_pending_action_tx(wpa_s); @@ -6393,7 +6711,7 @@ int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr, return 0; switch (p2p_probe_req_rx(wpa_s->global->p2p, addr, dst, bssid, - ie, ie_len, rx_freq)) { + ie, ie_len, rx_freq, wpa_s->p2p_lo_started)) { case P2P_PREQ_NOT_P2P: wpas_notify_preq(wpa_s, addr, dst, bssid, ie, ie_len, ssi_signal); @@ -6425,12 +6743,15 @@ void wpas_p2p_rx_action(struct wpa_supplicant *wpa_s, const u8 *da, void wpas_p2p_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ies) { + unsigned int bands; + if (wpa_s->global->p2p_disabled) return; if (wpa_s->global->p2p == NULL) return; - p2p_scan_ie(wpa_s->global->p2p, ies, NULL); + bands = wpas_get_bands(wpa_s, NULL); + p2p_scan_ie(wpa_s->global->p2p, ies, NULL, bands); } @@ -6460,7 +6781,8 @@ int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr) /* Invite to reinvoke a persistent group */ int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr, struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq, - int ht40, int vht, int pref_freq) + int vht_center_freq2, int ht40, int vht, int max_chwidth, + int pref_freq) { enum p2p_invite_role role; u8 *bssid = NULL; @@ -6477,6 +6799,9 @@ int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr, wpa_s->p2p_persistent_go_freq = freq; wpa_s->p2p_go_ht40 = !!ht40; + wpa_s->p2p_go_vht = !!vht; + wpa_s->p2p_go_max_oper_chwidth = max_chwidth; + wpa_s->p2p_go_vht_center_freq2 = vht_center_freq2; if (ssid->mode == WPAS_MODE_P2P_GO) { role = P2P_INVITE_ROLE_GO; if (peer_addr == NULL) { @@ -6493,7 +6818,9 @@ int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr, return -1; } bssid = wpa_s->pending_interface_addr; - } else + } else if (wpa_s->p2p_mgmt) + bssid = wpa_s->parent->own_addr; + else bssid = wpa_s->own_addr; } else { role = P2P_INVITE_ROLE_CLIENT; @@ -6507,11 +6834,12 @@ int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr, pref_freq_list, &size); if (res) return res; - p2p_set_own_pref_freq_list(wpa_s->global->p2p, pref_freq_list, size); if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; + p2p_set_own_pref_freq_list(wpa_s->global->p2p, pref_freq_list, size); + if (wpa_s->parent->conf->p2p_ignore_shared_freq && no_pref_freq_given && pref_freq > 0 && wpa_s->num_multichan_concurrent > 1 && @@ -6549,6 +6877,8 @@ int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname, wpa_s->p2p_persistent_go_freq = 0; wpa_s->p2p_go_ht40 = 0; wpa_s->p2p_go_vht = 0; + wpa_s->p2p_go_vht_center_freq2 = 0; + wpa_s->p2p_go_max_oper_chwidth = 0; for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { if (os_strcmp(wpa_s->ifname, ifname) == 0) @@ -6568,7 +6898,7 @@ int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname, wpa_s->global->p2p_invite_group = wpa_s; persistent = ssid->p2p_persistent_group && - wpas_p2p_get_persistent(wpa_s->parent, peer_addr, + wpas_p2p_get_persistent(wpa_s->p2pdev, peer_addr, ssid->ssid, ssid->ssid_len); if (ssid->mode == WPAS_MODE_P2P_GO) { @@ -6591,7 +6921,7 @@ int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname, freq = wpa_s->current_bss ? wpa_s->current_bss->freq : (int) wpa_s->assoc_freq; } - wpa_s->parent->pending_invite_ssid_id = -1; + wpa_s->p2pdev->pending_invite_ssid_id = -1; if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; @@ -6614,7 +6944,6 @@ void wpas_p2p_completed(struct wpa_supplicant *wpa_s) { struct wpa_ssid *ssid = wpa_s->current_ssid; u8 go_dev_addr[ETH_ALEN]; - int network_id = -1; int persistent; int freq; u8 ip[3 * 4]; @@ -6622,13 +6951,22 @@ void wpas_p2p_completed(struct wpa_supplicant *wpa_s) if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION) { eloop_cancel_timeout(wpas_p2p_group_formation_timeout, - wpa_s->parent, NULL); + wpa_s->p2pdev, NULL); } if (!wpa_s->show_group_started || !ssid) return; wpa_s->show_group_started = 0; + if (!wpa_s->p2p_go_group_formation_completed && + wpa_s->global->p2p_group_formation == wpa_s) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Marking group formation completed on client on data connection"); + wpa_s->p2p_go_group_formation_completed = 1; + wpa_s->global->p2p_group_formation = NULL; + wpa_s->p2p_in_provisioning = 0; + wpa_s->p2p_in_invitation = 0; + } os_memset(go_dev_addr, 0, ETH_ALEN); if (ssid->bssid_set) @@ -6664,11 +7002,10 @@ void wpas_p2p_completed(struct wpa_supplicant *wpa_s) ip_addr); if (persistent) - network_id = wpas_p2p_store_persistent_group(wpa_s->parent, - ssid, go_dev_addr); - if (network_id < 0) - network_id = ssid->id; - wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 1); + wpas_p2p_store_persistent_group(wpa_s->p2pdev, + ssid, go_dev_addr); + + wpas_notify_p2p_group_started(wpa_s, ssid, persistent, 1); } @@ -7001,7 +7338,7 @@ int wpas_p2p_set_cross_connect(struct wpa_supplicant *wpa_s, int enabled) iface->cross_connect_enabled = 0; iface->cross_connect_in_use = 0; - wpa_msg_global(iface->parent, MSG_INFO, + wpa_msg_global(iface->p2pdev, MSG_INFO, P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s", iface->ifname, iface->cross_connect_uplink); @@ -7031,7 +7368,7 @@ static void wpas_p2p_enable_cross_connect(struct wpa_supplicant *uplink) continue; iface->cross_connect_in_use = 1; - wpa_msg_global(iface->parent, MSG_INFO, + wpa_msg_global(iface->p2pdev, MSG_INFO, P2P_EVENT_CROSS_CONNECT_ENABLE "%s %s", iface->ifname, iface->cross_connect_uplink); } @@ -7051,7 +7388,7 @@ static void wpas_p2p_disable_cross_connect(struct wpa_supplicant *uplink) if (!iface->cross_connect_in_use) continue; - wpa_msg_global(iface->parent, MSG_INFO, + wpa_msg_global(iface->p2pdev, MSG_INFO, P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s", iface->ifname, iface->cross_connect_uplink); iface->cross_connect_in_use = 0; @@ -7114,7 +7451,7 @@ static void wpas_p2p_cross_connect_setup(struct wpa_supplicant *wpa_s) break; wpa_s->cross_connect_in_use = 1; - wpa_msg_global(wpa_s->parent, MSG_INFO, + wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_CROSS_CONNECT_ENABLE "%s %s", wpa_s->ifname, wpa_s->cross_connect_uplink); break; @@ -7130,8 +7467,8 @@ int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s) wpa_printf(MSG_DEBUG, "P2P: Terminate connection due to WPS PBC " "session overlap"); - if (wpa_s != wpa_s->parent) - wpa_msg_ctrl(wpa_s->parent, MSG_INFO, WPS_EVENT_OVERLAP); + if (wpa_s != wpa_s->p2pdev) + wpa_msg_ctrl(wpa_s->p2pdev, MSG_INFO, WPS_EVENT_OVERLAP); wpas_p2p_group_formation_failed(wpa_s, 0); return 1; } @@ -7238,7 +7575,7 @@ int wpas_p2p_cancel(struct wpa_supplicant *wpa_s) wpa_s->ifname); found = 1; eloop_cancel_timeout(wpas_p2p_group_formation_timeout, - wpa_s->parent, NULL); + wpa_s->p2pdev, NULL); if (wpa_s->p2p_in_provisioning) { wpas_group_formation_completed(wpa_s, 0, 0); break; @@ -7251,6 +7588,7 @@ int wpas_p2p_cancel(struct wpa_supplicant *wpa_s) wpa_s->ifname); found = 1; wpas_p2p_group_formation_failed(wpa_s, 0); + break; } } @@ -7367,7 +7705,7 @@ void wpas_p2p_network_removed(struct wpa_supplicant *wpa_s, { if (wpa_s->p2p_in_provisioning && ssid->p2p_group && eloop_cancel_timeout(wpas_p2p_group_formation_timeout, - wpa_s->parent, NULL) > 0) { + wpa_s->p2pdev, NULL) > 0) { /** * Remove the network by scheduling the group formation * timeout to happen immediately. The teardown code @@ -7379,7 +7717,7 @@ void wpas_p2p_network_removed(struct wpa_supplicant *wpa_s, wpa_printf(MSG_DEBUG, "P2P: Canceled group formation due to " "P2P group network getting removed"); eloop_register_timeout(0, 0, wpas_p2p_group_formation_timeout, - wpa_s->parent, NULL); + wpa_s->p2pdev, NULL); } } @@ -7423,7 +7761,7 @@ void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s, const u8 *addr) { if (eloop_cancel_timeout(wpas_p2p_group_formation_timeout, - wpa_s->parent, NULL) > 0) { + wpa_s->p2pdev, NULL) > 0) { /* * This can happen if WPS provisioning step is not terminated * cleanly (e.g., P2P Client does not send WSC_Done). Since the @@ -7479,10 +7817,12 @@ static int wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s, wpas_p2p_connect(wpa_s, wpa_s->pending_join_dev_addr, wpa_s->p2p_pin, wpa_s->p2p_wps_method, wpa_s->p2p_persistent_group, 0, 0, 0, wpa_s->p2p_go_intent, wpa_s->p2p_connect_freq, + wpa_s->p2p_go_vht_center_freq2, wpa_s->p2p_persistent_id, wpa_s->p2p_pd_before_go_neg, wpa_s->p2p_go_ht40, - wpa_s->p2p_go_vht); + wpa_s->p2p_go_vht, + wpa_s->p2p_go_max_oper_chwidth, NULL, 0); return ret; } @@ -7500,7 +7840,7 @@ int wpas_p2p_scan_no_go_seen(struct wpa_supplicant *wpa_s) wpa_dbg(wpa_s, MSG_DEBUG, "P2P: GO not found for p2p_connect-auto - " "fallback to GO Negotiation"); - wpa_msg_global(wpa_s->parent, MSG_INFO, P2P_EVENT_FALLBACK_TO_GO_NEG + wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_FALLBACK_TO_GO_NEG "reason=GO-not-found"); res = wpas_p2p_fallback_to_go_neg(wpa_s, 1); @@ -7609,7 +7949,7 @@ void wpas_p2p_new_psk_cb(struct wpa_supplicant *wpa_s, const u8 *mac_addr, return; } - persistent = wpas_p2p_get_persistent(wpa_s->parent, NULL, ssid->ssid, + persistent = wpas_p2p_get_persistent(wpa_s->p2pdev, NULL, ssid->ssid, ssid->ssid_len); if (!persistent) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Could not find persistent group information to store the new PSK"); @@ -7638,7 +7978,7 @@ void wpas_p2p_new_psk_cb(struct wpa_supplicant *wpa_s, const u8 *mac_addr, os_free(last); } - wpas_p2p_remove_psk_entry(wpa_s->parent, persistent, + wpas_p2p_remove_psk_entry(wpa_s->p2pdev, persistent, p2p_dev_addr ? p2p_dev_addr : mac_addr, p2p_dev_addr == NULL); if (p2p_dev_addr) { @@ -7650,8 +7990,8 @@ void wpas_p2p_new_psk_cb(struct wpa_supplicant *wpa_s, const u8 *mac_addr, } dl_list_add(&persistent->psk_list, &p->list); - if (wpa_s->parent->conf->update_config && - wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf)) + if (wpa_s->p2pdev->conf->update_config && + wpa_config_write(wpa_s->p2pdev->confname, wpa_s->p2pdev->conf)) wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration"); } @@ -7830,14 +8170,14 @@ int wpas_p2p_4way_hs_failed(struct wpa_supplicant *wpa_s) wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Two 4-way handshake failures for a P2P group - go_dev_addr=" MACSTR, MAC2STR(go_dev_addr)); - persistent = wpas_p2p_get_persistent(wpa_s->parent, go_dev_addr, + persistent = wpas_p2p_get_persistent(wpa_s->p2pdev, go_dev_addr, ssid->ssid, ssid->ssid_len); if (persistent == NULL || persistent->mode != WPAS_MODE_INFRA) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No matching persistent group stored"); goto disconnect; } - wpa_msg_global(wpa_s->parent, MSG_INFO, + wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_PERSISTENT_PSK_FAIL "%d", persistent->id); disconnect: @@ -8016,7 +8356,10 @@ static int wpas_p2p_nfc_join_group(struct wpa_supplicant *wpa_s, return wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL, WPS_NFC, 0, 0, 1, 0, wpa_s->conf->p2p_go_intent, - params->go_freq, -1, 0, 1, 1); + params->go_freq, wpa_s->p2p_go_vht_center_freq2, + -1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth, + params->go_ssid_len ? params->go_ssid : NULL, + params->go_ssid_len); } @@ -8043,17 +8386,17 @@ static int wpas_p2p_nfc_auth_join(struct wpa_supplicant *wpa_s, return -1; } - if (wpa_s->parent->p2p_oob_dev_pw_id != + if (wpa_s->p2pdev->p2p_oob_dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER && - !wpa_s->parent->p2p_oob_dev_pw) { + !wpa_s->p2pdev->p2p_oob_dev_pw) { wpa_printf(MSG_DEBUG, "P2P: No NFC Dev Pw known"); return -1; } res = wpas_ap_wps_add_nfc_pw( - wpa_s, wpa_s->parent->p2p_oob_dev_pw_id, - wpa_s->parent->p2p_oob_dev_pw, - wpa_s->parent->p2p_peer_oob_pk_hash_known ? - wpa_s->parent->p2p_peer_oob_pubkey_hash : NULL); + wpa_s, wpa_s->p2pdev->p2p_oob_dev_pw_id, + wpa_s->p2pdev->p2p_oob_dev_pw, + wpa_s->p2pdev->p2p_peer_oob_pk_hash_known ? + wpa_s->p2pdev->p2p_peer_oob_pubkey_hash : NULL); if (res) return res; @@ -8071,16 +8414,16 @@ static int wpas_p2p_nfc_auth_join(struct wpa_supplicant *wpa_s, wpa_s->global->p2p_invite_group = wpa_s; persistent = ssid->p2p_persistent_group && - wpas_p2p_get_persistent(wpa_s->parent, + wpas_p2p_get_persistent(wpa_s->p2pdev, params->peer->p2p_device_addr, ssid->ssid, ssid->ssid_len); - wpa_s->parent->pending_invite_ssid_id = -1; + wpa_s->p2pdev->pending_invite_ssid_id = -1; return p2p_invite(wpa_s->global->p2p, params->peer->p2p_device_addr, P2P_INVITE_ROLE_ACTIVE_GO, wpa_s->own_addr, ssid->ssid, ssid->ssid_len, ssid->frequency, wpa_s->global->p2p_dev_addr, persistent, 0, - wpa_s->parent->p2p_oob_dev_pw_id); + wpa_s->p2pdev->p2p_oob_dev_pw_id); } @@ -8092,7 +8435,9 @@ static int wpas_p2p_nfc_init_go_neg(struct wpa_supplicant *wpa_s, "connection handover"); return wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL, WPS_NFC, 0, 0, 0, 0, wpa_s->conf->p2p_go_intent, - forced_freq, -1, 0, 1, 1); + forced_freq, wpa_s->p2p_go_vht_center_freq2, + -1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth, + NULL, 0); } @@ -8106,7 +8451,9 @@ static int wpas_p2p_nfc_resp_go_neg(struct wpa_supplicant *wpa_s, "connection handover"); res = wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL, WPS_NFC, 0, 0, 0, 1, wpa_s->conf->p2p_go_intent, - forced_freq, -1, 0, 1, 1); + forced_freq, wpa_s->p2p_go_vht_center_freq2, + -1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth, + NULL, 0); if (res) return res; @@ -8397,7 +8744,9 @@ int wpas_p2p_nfc_tag_enabled(struct wpa_supplicant *wpa_s, int enabled) } if_addr = wpa_s->pending_interface_addr; - } else + } else if (wpa_s->p2p_mgmt) + if_addr = wpa_s->parent->own_addr; + else if_addr = wpa_s->own_addr; wpa_s->p2p_nfc_tag_enabled = enabled; @@ -8473,14 +8822,115 @@ static void wpas_p2p_optimize_listen_channel(struct wpa_supplicant *wpa_s, static int wpas_p2p_move_go_csa(struct wpa_supplicant *wpa_s) { + struct hostapd_config *conf; + struct p2p_go_neg_results params; + struct csa_settings csa_settings; + struct wpa_ssid *current_ssid = wpa_s->current_ssid; + int old_freq = current_ssid->frequency; + int ret; + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP_CSA)) { wpa_dbg(wpa_s, MSG_DEBUG, "CSA is not enabled"); return -1; } - /* TODO: Add CSA support */ - wpa_dbg(wpa_s, MSG_DEBUG, "Moving GO with CSA is not implemented"); - return -1; + /* + * TODO: This function may not always work correctly. For example, + * when we have a running GO and a BSS on a DFS channel. + */ + if (wpas_p2p_init_go_params(wpa_s, ¶ms, 0, 0, 0, 0, 0, NULL)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P CSA: Failed to select new frequency for GO"); + return -1; + } + + if (current_ssid->frequency == params.freq) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P CSA: Selected same frequency - not moving GO"); + return 0; + } + + conf = hostapd_config_defaults(); + if (!conf) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P CSA: Failed to allocate default config"); + return -1; + } + + current_ssid->frequency = params.freq; + if (wpa_supplicant_conf_ap_ht(wpa_s, current_ssid, conf)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P CSA: Failed to create new GO config"); + ret = -1; + goto out; + } + + if (conf->hw_mode != wpa_s->ap_iface->current_mode->mode) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P CSA: CSA to a different band is not supported"); + ret = -1; + goto out; + } + + os_memset(&csa_settings, 0, sizeof(csa_settings)); + csa_settings.cs_count = P2P_GO_CSA_COUNT; + csa_settings.block_tx = P2P_GO_CSA_BLOCK_TX; + csa_settings.freq_params.freq = params.freq; + csa_settings.freq_params.sec_channel_offset = conf->secondary_channel; + csa_settings.freq_params.ht_enabled = conf->ieee80211n; + csa_settings.freq_params.bandwidth = conf->secondary_channel ? 40 : 20; + + if (conf->ieee80211ac) { + int freq1 = 0, freq2 = 0; + u8 chan, opclass; + + if (ieee80211_freq_to_channel_ext(params.freq, + conf->secondary_channel, + conf->vht_oper_chwidth, + &opclass, &chan) == + NUM_HOSTAPD_MODES) { + wpa_printf(MSG_ERROR, "P2P CSA: Bad freq"); + ret = -1; + goto out; + } + + if (conf->vht_oper_centr_freq_seg0_idx) + freq1 = ieee80211_chan_to_freq( + NULL, opclass, + conf->vht_oper_centr_freq_seg0_idx); + + if (conf->vht_oper_centr_freq_seg1_idx) + freq2 = ieee80211_chan_to_freq( + NULL, opclass, + conf->vht_oper_centr_freq_seg1_idx); + + if (freq1 < 0 || freq2 < 0) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P CSA: Selected invalid VHT center freqs"); + ret = -1; + goto out; + } + + csa_settings.freq_params.vht_enabled = conf->ieee80211ac; + csa_settings.freq_params.center_freq1 = freq1; + csa_settings.freq_params.center_freq2 = freq2; + + switch (conf->vht_oper_chwidth) { + case VHT_CHANWIDTH_80MHZ: + case VHT_CHANWIDTH_80P80MHZ: + csa_settings.freq_params.bandwidth = 80; + break; + case VHT_CHANWIDTH_160MHZ: + csa_settings.freq_params.bandwidth = 160; + break; + } + } + + ret = ap_switch_channel(wpa_s, &csa_settings); +out: + current_ssid->frequency = old_freq; + hostapd_config_free(conf); + return ret; } @@ -8500,7 +8950,7 @@ static void wpas_p2p_move_go_no_csa(struct wpa_supplicant *wpa_s) wpa_supplicant_ap_deinit(wpa_s); /* Reselect the GO frequency */ - if (wpas_p2p_init_go_params(wpa_s, ¶ms, 0, 0, 0, NULL)) { + if (wpas_p2p_init_go_params(wpa_s, ¶ms, 0, 0, 0, 0, 0, NULL)) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Failed to reselect freq"); wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_GO_LEAVE_CHANNEL); @@ -8537,6 +8987,13 @@ static void wpas_p2p_move_go(void *eloop_ctx, void *timeout_ctx) wpas_p2p_go_update_common_freqs(wpa_s); + /* Do not move GO in the middle of a CSA */ + if (hostapd_csa_in_progress(wpa_s->ap_iface)) { + wpa_printf(MSG_DEBUG, + "P2P: CSA is in progress - not moving GO"); + return; + } + /* * First, try a channel switch flow. If it is not supported or fails, * take down the GO and bring it up again. @@ -8613,6 +9070,25 @@ static void wpas_p2p_consider_moving_one_go(struct wpa_supplicant *wpa_s, P2P_GO_FREQ_MOVE_SCM_PEER_SUPPORTS && wpas_p2p_go_is_peer_freq(wpa_s, freqs[i].freq)) { policy_move = 1; + } else if ((wpa_s->conf->p2p_go_freq_change_policy == + P2P_GO_FREQ_MOVE_SCM_ECSA) && + wpas_p2p_go_is_peer_freq(wpa_s, freqs[i].freq)) { + if (!p2p_get_group_num_members(wpa_s->p2p_group)) { + policy_move = 1; + } else if ((wpa_s->drv_flags & + WPA_DRIVER_FLAGS_AP_CSA) && + wpas_p2p_go_clients_support_ecsa(wpa_s)) { + u8 chan; + + /* + * We do not support CSA between bands, so move + * GO only within the same band. + */ + if (wpa_s->ap_iface->current_mode->mode == + ieee80211_freq_to_chan(freqs[i].freq, + &chan)) + policy_move = 1; + } } } @@ -8647,6 +9123,16 @@ static void wpas_p2p_consider_moving_one_go(struct wpa_supplicant *wpa_s, return; } + /* + * Do not consider moving GO if it is in the middle of a CSA. When the + * CSA is finished this flow should be retriggered. + */ + if (hostapd_csa_in_progress(wpa_s->ap_iface)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Not initiating a GO frequency change - CSA is in progress"); + return; + } + if (invalid_freq && !wpas_p2p_disallowed_freq(wpa_s->global, freq)) timeout = P2P_GO_FREQ_CHANGE_TIME; else @@ -8726,3 +9212,86 @@ void wpas_p2p_ap_deinit(struct wpa_supplicant *wpa_s) wpa_s->ap_iface->bss[0]->p2p_group = NULL; wpas_p2p_group_deinit(wpa_s); } + + +int wpas_p2p_lo_start(struct wpa_supplicant *wpa_s, unsigned int freq, + unsigned int period, unsigned int interval, + unsigned int count) +{ + struct p2p_data *p2p = wpa_s->global->p2p; + u8 *device_types; + size_t dev_types_len; + struct wpabuf *buf; + int ret; + + if (wpa_s->p2p_lo_started) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P Listen offload is already started"); + return 0; + } + + if (wpa_s->global->p2p == NULL || + !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD)) { + wpa_printf(MSG_DEBUG, "P2P: Listen offload not supported"); + return -1; + } + + if (!p2p_supported_freq(wpa_s->global->p2p, freq)) { + wpa_printf(MSG_ERROR, "P2P: Input channel not supported: %u", + freq); + return -1; + } + + /* Get device type */ + dev_types_len = (wpa_s->conf->num_sec_device_types + 1) * + WPS_DEV_TYPE_LEN; + device_types = os_malloc(dev_types_len); + if (!device_types) + return -1; + os_memcpy(device_types, wpa_s->conf->device_type, WPS_DEV_TYPE_LEN); + os_memcpy(&device_types[WPS_DEV_TYPE_LEN], wpa_s->conf->sec_device_type, + wpa_s->conf->num_sec_device_types * WPS_DEV_TYPE_LEN); + + /* Get Probe Response IE(s) */ + buf = p2p_build_probe_resp_template(p2p, freq); + if (!buf) { + os_free(device_types); + return -1; + } + + ret = wpa_drv_p2p_lo_start(wpa_s, freq, period, interval, count, + device_types, dev_types_len, + wpabuf_mhead_u8(buf), wpabuf_len(buf)); + if (ret < 0) + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Failed to start P2P listen offload"); + + os_free(device_types); + wpabuf_free(buf); + + if (ret == 0) { + wpa_s->p2p_lo_started = 1; + + /* Stop current P2P listen if any */ + wpas_stop_listen(wpa_s); + } + + return ret; +} + + +int wpas_p2p_lo_stop(struct wpa_supplicant *wpa_s) +{ + int ret; + + if (!wpa_s->p2p_lo_started) + return 0; + + ret = wpa_drv_p2p_lo_stop(wpa_s); + if (ret < 0) + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Failed to stop P2P listen offload"); + + wpa_s->p2p_lo_started = 0; + return ret; +} diff --git a/contrib/wpa/wpa_supplicant/p2p_supplicant.h b/contrib/wpa/wpa_supplicant/p2p_supplicant.h index 56e683498d66..63910d1c268e 100644 --- a/contrib/wpa/wpa_supplicant/p2p_supplicant.h +++ b/contrib/wpa/wpa_supplicant/p2p_supplicant.h @@ -34,17 +34,22 @@ struct wpa_supplicant * wpas_get_p2p_client_iface(struct wpa_supplicant *wpa_s, const u8 *peer_dev_addr); int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, const char *pin, enum p2p_wps_method wps_method, - int persistent_group, int auto_join, int join, - int auth, int go_intent, int freq, int persistent_id, - int pd, int ht40, int vht); + int persistent_group, int auto_join, int join, int auth, + int go_intent, int freq, unsigned int vht_center_freq2, + int persistent_id, int pd, int ht40, int vht, + unsigned int vht_chwidth, const u8 *group_ssid, + size_t group_ssid_len); int wpas_p2p_handle_frequency_conflicts(struct wpa_supplicant *wpa_s, int freq, struct wpa_ssid *ssid); int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group, - int freq, int ht40, int vht); + int freq, int vht_center_freq2, int ht40, int vht, + int max_oper_chwidth); int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, int addr_allocated, - int force_freq, int neg_freq, int ht40, - int vht, const struct p2p_channels *channels, + int force_freq, int neg_freq, + int vht_center_freq2, int ht40, + int vht, int max_oper_chwidth, + const struct p2p_channels *channels, int connection_timeout, int force_scan); struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); @@ -111,7 +116,8 @@ void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic, int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr); int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr, struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq, - int ht40, int vht, int pref_freq); + int vht_center_freq2, int ht40, int vht, + int max_oper_chwidth, int pref_freq); int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname, const u8 *peer_addr, const u8 *go_dev_addr); int wpas_p2p_presence_req(struct wpa_supplicant *wpa_s, u32 duration1, @@ -140,6 +146,8 @@ int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s, struct hostapd_hw_modes *mode, u8 channel); int wpas_p2p_get_vht80_center(struct wpa_supplicant *wpa_s, struct hostapd_hw_modes *mode, u8 channel); +int wpas_p2p_get_vht160_center(struct wpa_supplicant *wpa_s, + struct hostapd_hw_modes *mode, u8 channel); unsigned int wpas_p2p_search_delay(struct wpa_supplicant *wpa_s); void wpas_p2p_new_psk_cb(struct wpa_supplicant *wpa_s, const u8 *mac_addr, const u8 *p2p_dev_addr, @@ -199,6 +207,10 @@ int wpas_p2p_wps_eapol_cb(struct wpa_supplicant *wpa_s); void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s, struct wps_event_fail *fail); int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname); +int wpas_p2p_lo_start(struct wpa_supplicant *wpa_s, unsigned int freq, + unsigned int period, unsigned int interval, + unsigned int count); +int wpas_p2p_lo_stop(struct wpa_supplicant *wpa_s); #else /* CONFIG_P2P */ diff --git a/contrib/wpa/wpa_supplicant/p2p_supplicant_sd.c b/contrib/wpa/wpa_supplicant/p2p_supplicant_sd.c index fc07b07462f5..f8675e68bec4 100644 --- a/contrib/wpa/wpa_supplicant/p2p_supplicant_sd.c +++ b/contrib/wpa/wpa_supplicant/p2p_supplicant_sd.c @@ -48,7 +48,7 @@ static int p2p_sd_dns_uncompress_label(char **upos, char *uend, u8 *start, u8 *spos_tmp; /* Offset */ - if (*spos + 2 > end) { + if (end - *spos < 2) { wpa_printf(MSG_DEBUG, "P2P: No room for full " "DNS offset field"); return -1; @@ -74,14 +74,14 @@ static int p2p_sd_dns_uncompress_label(char **upos, char *uend, u8 *start, return 0; (*spos)++; - if (*spos + len > end) { + if (len > end - *spos) { wpa_printf(MSG_DEBUG, "P2P: Invalid domain name " "sequence - no room for label with length " "%u", len); return -1; } - if (*upos + len + 2 > uend) + if (len + 2 > uend - *upos) return -2; os_memcpy(*upos, *spos, len); @@ -722,11 +722,11 @@ void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token, if (resp == NULL) return; - while (pos + 1 < end) { + while (end - pos > 1) { wpa_printf(MSG_DEBUG, "P2P: Service Request TLV"); slen = WPA_GET_LE16(pos); pos += 2; - if (pos + slen > end || slen < 2) { + if (slen > end - pos || slen < 2) { wpa_printf(MSG_DEBUG, "P2P: Unexpected Query Data " "length"); wpabuf_free(resp); @@ -827,10 +827,10 @@ static void wpas_sd_p2ps_serv_response(struct wpa_supplicant *wpa_s, u8 svc_len; /* Sanity check fixed length+svc_str */ - if (pos + 6 >= tlv_end) + if (6 >= tlv_end - pos) break; svc_len = pos[6]; - if (pos + svc_len + 10 > tlv_end) + if (svc_len + 10 > tlv_end - pos) break; /* Advertisement ID */ @@ -917,13 +917,13 @@ void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic, } } - while (pos < end) { + while (end - pos >= 2) { u8 srv_proto, srv_trans_id, status; wpa_printf(MSG_DEBUG, "P2P: Service Response TLV"); slen = WPA_GET_LE16(pos); pos += 2; - if (pos + slen > end || slen < 3) { + if (slen > end - pos || slen < 3) { wpa_printf(MSG_DEBUG, "P2P: Unexpected Response Data " "length"); return; diff --git a/contrib/wpa/wpa_supplicant/scan.c b/contrib/wpa/wpa_supplicant/scan.c index d7049a1a8164..fb8ebdf2ecc1 100644 --- a/contrib/wpa/wpa_supplicant/scan.c +++ b/contrib/wpa/wpa_supplicant/scan.c @@ -36,8 +36,7 @@ static void wpa_supplicant_gen_assoc_event(struct wpa_supplicant *wpa_s) if (wpa_s->current_ssid == NULL) { wpa_s->current_ssid = ssid; - if (wpa_s->current_ssid != NULL) - wpas_notify_network_changed(wpa_s); + wpas_notify_network_changed(wpa_s); } wpa_supplicant_initiate_eapol(wpa_s); wpa_dbg(wpa_s, MSG_DEBUG, "Already associated with a configured " @@ -60,10 +59,7 @@ static int wpas_wps_in_use(struct wpa_supplicant *wpa_s, wps = 1; *req_type = wpas_wps_get_req_type(ssid); - if (!ssid->eap.phase1) - continue; - - if (os_strstr(ssid->eap.phase1, "pbc=1")) + if (ssid->eap.phase1 && os_strstr(ssid->eap.phase1, "pbc=1")) return 2; } @@ -166,6 +162,8 @@ static void wpas_trigger_scan_cb(struct wpa_radio_work *work, int deinit) if (wpas_update_random_addr_disassoc(wpa_s) < 0) { wpa_msg(wpa_s, MSG_INFO, "Failed to assign random MAC address for a scan"); + wpa_scan_free_params(params); + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_FAILED "ret=-1"); radio_work_done(work); return; } @@ -229,12 +227,11 @@ int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s, } ctx = wpa_scan_clone_params(params); - if (ctx == NULL) - return -1; - - if (radio_add_work(wpa_s, 0, "scan", 0, wpas_trigger_scan_cb, ctx) < 0) + if (!ctx || + radio_add_work(wpa_s, 0, "scan", 0, wpas_trigger_scan_cb, ctx) < 0) { wpa_scan_free_params(ctx); + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_FAILED "ret=-1"); return -1; } @@ -266,14 +263,14 @@ wpa_supplicant_sched_scan_timeout(void *eloop_ctx, void *timeout_ctx) } -int wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s, - struct wpa_driver_scan_params *params, - int interval) +static int +wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s, + struct wpa_driver_scan_params *params) { int ret; wpa_supplicant_notify_scanning(wpa_s, 1); - ret = wpa_drv_sched_scan(wpa_s, params, interval * 1000); + ret = wpa_drv_sched_scan(wpa_s, params); if (ret) wpa_supplicant_notify_scanning(wpa_s, 0); else @@ -283,7 +280,7 @@ int wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s, } -int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s) +static int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s) { int ret; @@ -429,6 +426,39 @@ static void wpas_add_interworking_elements(struct wpa_supplicant *wpa_s, #endif /* CONFIG_INTERWORKING */ +void wpa_supplicant_set_default_scan_ies(struct wpa_supplicant *wpa_s) +{ + struct wpabuf *default_ies = NULL; + u8 ext_capab[18]; + int ext_capab_len; + enum wpa_driver_if_type type = WPA_IF_STATION; + +#ifdef CONFIG_P2P + if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT) + type = WPA_IF_P2P_CLIENT; +#endif /* CONFIG_P2P */ + + wpa_drv_get_ext_capa(wpa_s, type); + + ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab, + sizeof(ext_capab)); + if (ext_capab_len > 0 && + wpabuf_resize(&default_ies, ext_capab_len) == 0) + wpabuf_put_data(default_ies, ext_capab, ext_capab_len); + +#ifdef CONFIG_MBO + /* Send cellular capabilities for potential MBO STAs */ + if (wpabuf_resize(&default_ies, 9) == 0) + wpas_mbo_scan_ie(wpa_s, default_ies); +#endif /* CONFIG_MBO */ + + if (default_ies) + wpa_drv_set_default_scan_ies(wpa_s, wpabuf_head(default_ies), + wpabuf_len(default_ies)); + wpabuf_free(default_ies); +} + + static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s) { struct wpabuf *extra_ie = NULL; @@ -439,6 +469,13 @@ static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s) enum wps_request_type req_type = WPS_REQ_ENROLLEE_INFO; #endif /* CONFIG_WPS */ +#ifdef CONFIG_P2P + if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT) + wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_CLIENT); + else +#endif /* CONFIG_P2P */ + wpa_drv_get_ext_capa(wpa_s, WPA_IF_STATION); + ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab, sizeof(ext_capab)); if (ext_capab_len > 0 && @@ -491,6 +528,19 @@ static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s) wpabuf_put_buf(extra_ie, wpa_s->fst_ies); #endif /* CONFIG_FST */ +#ifdef CONFIG_MBO + /* Send cellular capabilities for potential MBO STAs */ + if (wpabuf_resize(&extra_ie, 9) == 0) + wpas_mbo_scan_ie(wpa_s, extra_ie); +#endif /* CONFIG_MBO */ + + if (wpa_s->vendor_elem[VENDOR_ELEM_PROBE_REQ]) { + struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_PROBE_REQ]; + + if (wpabuf_resize(&extra_ie, wpabuf_len(buf)) == 0) + wpabuf_put_buf(extra_ie, buf); + } + return extra_ie; } @@ -522,21 +572,6 @@ static int non_p2p_network_enabled(struct wpa_supplicant *wpa_s) #endif /* CONFIG_P2P */ -static struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes, - u16 num_modes, - enum hostapd_hw_mode mode) -{ - u16 i; - - for (i = 0; i < num_modes; i++) { - if (modes[i].mode == mode) - return &modes[i]; - } - - return NULL; -} - - static void wpa_setband_scan_freqs_list(struct wpa_supplicant *wpa_s, enum hostapd_hw_mode band, struct wpa_driver_scan_params *params) @@ -586,6 +621,12 @@ static void wpa_set_scan_ssids(struct wpa_supplicant *wpa_s, unsigned int i; struct wpa_ssid *ssid; + /* + * For devices with max_ssids greater than 1, leave the last slot empty + * for adding the wildcard scan entry. + */ + max_ssids = max_ssids > 1 ? max_ssids - 1 : max_ssids; + for (i = 0; i < wpa_s->scan_id_count; i++) { unsigned int j; @@ -840,12 +881,10 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) * slot for the zero-terminator. */ params.freqs = os_malloc(sizeof(int) * 2); - if (params.freqs == NULL) { - wpa_dbg(wpa_s, MSG_ERROR, "Memory allocation failed"); - return; + if (params.freqs) { + params.freqs[0] = wpa_s->assoc_freq; + params.freqs[1] = 0; } - params.freqs[0] = wpa_s->assoc_freq; - params.freqs[1] = 0; /* * Reset the reattach flag so that we fall back to full scan if @@ -1016,6 +1055,27 @@ ssid_list_set: } } + if (!is_zero_ether_addr(wpa_s->next_scan_bssid)) { + struct wpa_bss *bss; + + params.bssid = wpa_s->next_scan_bssid; + bss = wpa_bss_get_bssid_latest(wpa_s, params.bssid); + if (bss && bss->ssid_len && params.num_ssids == 1 && + params.ssids[0].ssid_len == 0) { + params.ssids[0].ssid = bss->ssid; + params.ssids[0].ssid_len = bss->ssid_len; + wpa_dbg(wpa_s, MSG_DEBUG, + "Scan a previously specified BSSID " MACSTR + " and SSID %s", + MAC2STR(params.bssid), + wpa_ssid_txt(bss->ssid, bss->ssid_len)); + } else { + wpa_dbg(wpa_s, MSG_DEBUG, + "Scan a previously specified BSSID " MACSTR, + MAC2STR(params.bssid)); + } + } + scan_params = ¶ms; scan: @@ -1076,6 +1136,8 @@ scan: #ifdef CONFIG_INTERWORKING wpa_s->interworking_fast_assoc_tried = 0; #endif /* CONFIG_INTERWORKING */ + if (params.bssid) + os_memset(wpa_s->next_scan_bssid, 0, ETH_ALEN); } } @@ -1182,6 +1244,7 @@ int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s) unsigned int max_sched_scan_ssids; int wildcard = 0; int need_ssids; + struct sched_scan_plan scan_plan; if (!wpa_s->sched_scan_supported) return -1; @@ -1193,6 +1256,8 @@ int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s) if (max_sched_scan_ssids < 1 || wpa_s->conf->disable_scan_offload) return -1; + wpa_s->sched_scan_stop_req = 0; + if (wpa_s->sched_scanning) { wpa_dbg(wpa_s, MSG_DEBUG, "Already sched scanning"); return 0; @@ -1271,11 +1336,6 @@ int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s) if (!ssid || !wpa_s->prev_sched_ssid) { wpa_dbg(wpa_s, MSG_DEBUG, "Beginning of SSID list"); - if (wpa_s->conf->sched_scan_interval) - wpa_s->sched_scan_interval = - wpa_s->conf->sched_scan_interval; - if (wpa_s->sched_scan_interval == 0) - wpa_s->sched_scan_interval = 10; wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2; wpa_s->first_sched_scan = 1; ssid = wpa_s->conf->ssid; @@ -1360,14 +1420,51 @@ int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s) scan_params = ¶ms; scan: + wpa_s->sched_scan_timed_out = 0; + + /* + * We cannot support multiple scan plans if the scan request includes + * too many SSID's, so in this case use only the last scan plan and make + * it run infinitely. It will be stopped by the timeout. + */ + if (wpa_s->sched_scan_plans_num == 1 || + (wpa_s->sched_scan_plans_num && !ssid && wpa_s->first_sched_scan)) { + params.sched_scan_plans = wpa_s->sched_scan_plans; + params.sched_scan_plans_num = wpa_s->sched_scan_plans_num; + } else if (wpa_s->sched_scan_plans_num > 1) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Too many SSIDs. Default to using single scheduled_scan plan"); + params.sched_scan_plans = + &wpa_s->sched_scan_plans[wpa_s->sched_scan_plans_num - + 1]; + params.sched_scan_plans_num = 1; + } else { + if (wpa_s->conf->sched_scan_interval) + scan_plan.interval = wpa_s->conf->sched_scan_interval; + else + scan_plan.interval = 10; + + if (scan_plan.interval > wpa_s->max_sched_scan_plan_interval) { + wpa_printf(MSG_WARNING, + "Scan interval too long(%u), use the maximum allowed(%u)", + scan_plan.interval, + wpa_s->max_sched_scan_plan_interval); + scan_plan.interval = + wpa_s->max_sched_scan_plan_interval; + } + + scan_plan.iterations = 0; + params.sched_scan_plans = &scan_plan; + params.sched_scan_plans_num = 1; + } + if (ssid || !wpa_s->first_sched_scan) { wpa_dbg(wpa_s, MSG_DEBUG, - "Starting sched scan: interval %d timeout %d", - wpa_s->sched_scan_interval, wpa_s->sched_scan_timeout); + "Starting sched scan: interval %u timeout %d", + params.sched_scan_plans[0].interval, + wpa_s->sched_scan_timeout); } else { - wpa_dbg(wpa_s, MSG_DEBUG, - "Starting sched scan: interval %d (no timeout)", - wpa_s->sched_scan_interval); + wpa_dbg(wpa_s, MSG_DEBUG, "Starting sched scan (no timeout)"); } wpa_setband_scan_freqs(wpa_s, scan_params); @@ -1381,8 +1478,7 @@ scan: } } - ret = wpa_supplicant_start_sched_scan(wpa_s, scan_params, - wpa_s->sched_scan_interval); + ret = wpa_supplicant_start_sched_scan(wpa_s, scan_params); wpabuf_free(extra_ie); os_free(params.filter_ssids); if (ret) { @@ -1400,9 +1496,12 @@ scan: wpa_s, NULL); wpa_s->first_sched_scan = 0; wpa_s->sched_scan_timeout /= 2; - wpa_s->sched_scan_interval *= 2; - if (wpa_s->sched_scan_timeout < wpa_s->sched_scan_interval) { - wpa_s->sched_scan_interval = 10; + params.sched_scan_plans[0].interval *= 2; + if ((unsigned int) wpa_s->sched_scan_timeout < + params.sched_scan_plans[0].interval || + params.sched_scan_plans[0].interval > + wpa_s->max_sched_scan_plan_interval) { + params.sched_scan_plans[0].interval = 10; wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2; } } @@ -1457,6 +1556,9 @@ void wpa_supplicant_cancel_sched_scan(struct wpa_supplicant *wpa_s) if (!wpa_s->sched_scanning) return; + if (wpa_s->sched_scanning) + wpa_s->sched_scan_stop_req = 1; + wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling sched scan"); eloop_cancel_timeout(wpa_supplicant_sched_scan_timeout, wpa_s, NULL); wpa_supplicant_stop_sched_scan(wpa_s); @@ -1516,20 +1618,7 @@ static int wpa_scan_get_max_rate(const struct wpa_scan_res *res) */ const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie) { - const u8 *end, *pos; - - pos = (const u8 *) (res + 1); - end = pos + res->ie_len; - - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) - break; - if (pos[0] == ie) - return pos; - pos += 2 + pos[1]; - } - - return NULL; + return get_ie((const u8 *) (res + 1), res->ie_len, ie); } @@ -1550,8 +1639,8 @@ const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res, pos = (const u8 *) (res + 1); end = pos + res->ie_len; - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) + while (end - pos > 1) { + if (2 + pos[1] > end - pos) break; if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && vendor_type == WPA_GET_BE32(&pos[2])) @@ -1587,8 +1676,8 @@ const u8 * wpa_scan_get_vendor_ie_beacon(const struct wpa_scan_res *res, pos += res->ie_len; end = pos + res->beacon_ie_len; - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) + while (end - pos > 1) { + if (2 + pos[1] > end - pos) break; if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && vendor_type == WPA_GET_BE32(&pos[2])) @@ -1623,8 +1712,8 @@ struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res, pos = (const u8 *) (res + 1); end = pos + res->ie_len; - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) + while (end - pos > 1) { + if (2 + pos[1] > end - pos) break; if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && vendor_type == WPA_GET_BE32(&pos[2])) @@ -1832,8 +1921,8 @@ int wpa_supplicant_filter_bssid_match(struct wpa_supplicant *wpa_s, } -static void filter_scan_res(struct wpa_supplicant *wpa_s, - struct wpa_scan_results *res) +void filter_scan_res(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *res) { size_t i, j; @@ -1860,13 +1949,13 @@ static void filter_scan_res(struct wpa_supplicant *wpa_s, /* * Noise floor values to use when we have signal strength - * measurements, but no noise floor measurments. These values were + * measurements, but no noise floor measurements. These values were * measured in an office environment with many APs. */ #define DEFAULT_NOISE_FLOOR_2GHZ (-89) #define DEFAULT_NOISE_FLOOR_5GHZ (-92) -static void scan_snr(struct wpa_scan_res *res) +void scan_snr(struct wpa_scan_res *res) { if (res->flags & WPA_SCAN_NOISE_INVALID) { res->noise = IS_5GHZ(res->freq) ? @@ -1950,8 +2039,8 @@ static unsigned int max_vht80_rate(int snr) } -static void scan_est_throughput(struct wpa_supplicant *wpa_s, - struct wpa_scan_res *res) +void scan_est_throughput(struct wpa_supplicant *wpa_s, + struct wpa_scan_res *res) { enum local_hw_capab capab = wpa_s->hw_capab; int rate; /* max legacy rate in 500 kb/s units */ @@ -2148,6 +2237,9 @@ void scan_only_handler(struct wpa_supplicant *wpa_s, wpa_s->scan_work = NULL; radio_work_done(work); } + + if (wpa_s->wpa_state == WPA_SCANNING) + wpa_supplicant_set_state(wpa_s, wpa_s->scan_prev_wpa_state); } @@ -2214,6 +2306,19 @@ wpa_scan_clone_params(const struct wpa_driver_scan_params *src) params->only_new_results = src->only_new_results; params->low_priority = src->low_priority; + if (src->sched_scan_plans_num > 0) { + params->sched_scan_plans = + os_malloc(sizeof(*src->sched_scan_plans) * + src->sched_scan_plans_num); + if (!params->sched_scan_plans) + goto failed; + + os_memcpy(params->sched_scan_plans, src->sched_scan_plans, + sizeof(*src->sched_scan_plans) * + src->sched_scan_plans_num); + params->sched_scan_plans_num = src->sched_scan_plans_num; + } + if (src->mac_addr_rand) { params->mac_addr_rand = src->mac_addr_rand; @@ -2231,6 +2336,17 @@ wpa_scan_clone_params(const struct wpa_driver_scan_params *src) params->mac_addr_mask = mac_addr + ETH_ALEN; } } + + if (src->bssid) { + u8 *bssid; + + bssid = os_malloc(ETH_ALEN); + if (!bssid) + goto failed; + os_memcpy(bssid, src->bssid, ETH_ALEN); + params->bssid = bssid; + } + return params; failed: @@ -2251,6 +2367,7 @@ void wpa_scan_free_params(struct wpa_driver_scan_params *params) os_free((u8 *) params->extra_ies); os_free(params->freqs); os_free(params->filter_ssids); + os_free(params->sched_scan_plans); /* * Note: params->mac_addr_mask points to same memory allocation and @@ -2258,20 +2375,31 @@ void wpa_scan_free_params(struct wpa_driver_scan_params *params) */ os_free((u8 *) params->mac_addr); + os_free((u8 *) params->bssid); + os_free(params); } int wpas_start_pno(struct wpa_supplicant *wpa_s) { - int ret, interval, prio; + int ret, prio; size_t i, num_ssid, num_match_ssid; struct wpa_ssid *ssid; struct wpa_driver_scan_params params; + struct sched_scan_plan scan_plan; + unsigned int max_sched_scan_ssids; if (!wpa_s->sched_scan_supported) return -1; + if (wpa_s->max_sched_scan_ssids > WPAS_MAX_SCAN_SSIDS) + max_sched_scan_ssids = WPAS_MAX_SCAN_SSIDS; + else + max_sched_scan_ssids = wpa_s->max_sched_scan_ssids; + if (max_sched_scan_ssids < 1) + return -1; + if (wpa_s->pno || wpa_s->pno_sched_pending) return 0; @@ -2292,6 +2420,13 @@ int wpas_start_pno(struct wpa_supplicant *wpa_s) } } + if (wpa_s->sched_scan_stop_req) { + wpa_printf(MSG_DEBUG, + "Schedule PNO after previous sched scan has stopped"); + wpa_s->pno_sched_pending = 1; + return 0; + } + os_memset(¶ms, 0, sizeof(params)); num_ssid = num_match_ssid = 0; @@ -2315,10 +2450,10 @@ int wpas_start_pno(struct wpa_supplicant *wpa_s) num_ssid++; } - if (num_ssid > WPAS_MAX_SCAN_SSIDS) { + if (num_ssid > max_sched_scan_ssids) { wpa_printf(MSG_DEBUG, "PNO: Use only the first %u SSIDs from " - "%u", WPAS_MAX_SCAN_SSIDS, (unsigned int) num_ssid); - num_ssid = WPAS_MAX_SCAN_SSIDS; + "%u", max_sched_scan_ssids, (unsigned int) num_ssid); + num_ssid = max_sched_scan_ssids; } if (num_match_ssid > wpa_s->max_match_sets) { @@ -2361,8 +2496,20 @@ int wpas_start_pno(struct wpa_supplicant *wpa_s) if (wpa_s->conf->filter_rssi) params.filter_rssi = wpa_s->conf->filter_rssi; - interval = wpa_s->conf->sched_scan_interval ? - wpa_s->conf->sched_scan_interval : 10; + if (wpa_s->sched_scan_plans_num) { + params.sched_scan_plans = wpa_s->sched_scan_plans; + params.sched_scan_plans_num = wpa_s->sched_scan_plans_num; + } else { + /* Set one scan plan that will run infinitely */ + if (wpa_s->conf->sched_scan_interval) + scan_plan.interval = wpa_s->conf->sched_scan_interval; + else + scan_plan.interval = 10; + + scan_plan.iterations = 0; + params.sched_scan_plans = &scan_plan; + params.sched_scan_plans_num = 1; + } if (params.freqs == NULL && wpa_s->manual_sched_scan_freqs) { wpa_dbg(wpa_s, MSG_DEBUG, "Limit sched scan to specified channels"); @@ -2377,7 +2524,7 @@ int wpas_start_pno(struct wpa_supplicant *wpa_s) } } - ret = wpa_supplicant_start_sched_scan(wpa_s, ¶ms, interval); + ret = wpa_supplicant_start_sched_scan(wpa_s, ¶ms); os_free(params.filter_ssids); if (ret == 0) wpa_s->pno = 1; @@ -2395,6 +2542,7 @@ int wpas_stop_pno(struct wpa_supplicant *wpa_s) return 0; ret = wpa_supplicant_stop_sched_scan(wpa_s); + wpa_s->sched_scan_stop_req = 1; wpa_s->pno = 0; wpa_s->pno_sched_pending = 0; @@ -2462,3 +2610,160 @@ int wpas_mac_addr_rand_scan_set(struct wpa_supplicant *wpa_s, wpa_s->mac_addr_rand_enable |= type; return 0; } + + +int wpas_abort_ongoing_scan(struct wpa_supplicant *wpa_s) +{ + int scan_work = !!wpa_s->scan_work; + +#ifdef CONFIG_P2P + scan_work |= !!wpa_s->p2p_scan_work; +#endif /* CONFIG_P2P */ + + if (scan_work && wpa_s->own_scan_running) { + wpa_dbg(wpa_s, MSG_DEBUG, "Abort an ongoing scan"); + return wpa_drv_abort_scan(wpa_s); + } + + return 0; +} + + +int wpas_sched_scan_plans_set(struct wpa_supplicant *wpa_s, const char *cmd) +{ + struct sched_scan_plan *scan_plans = NULL; + const char *token, *context = NULL; + unsigned int num = 0; + + if (!cmd) + return -1; + + if (!cmd[0]) { + wpa_printf(MSG_DEBUG, "Clear sched scan plans"); + os_free(wpa_s->sched_scan_plans); + wpa_s->sched_scan_plans = NULL; + wpa_s->sched_scan_plans_num = 0; + return 0; + } + + while ((token = cstr_token(cmd, " ", &context))) { + int ret; + struct sched_scan_plan *scan_plan, *n; + + n = os_realloc_array(scan_plans, num + 1, sizeof(*scan_plans)); + if (!n) + goto fail; + + scan_plans = n; + scan_plan = &scan_plans[num]; + num++; + + ret = sscanf(token, "%u:%u", &scan_plan->interval, + &scan_plan->iterations); + if (ret <= 0 || ret > 2 || !scan_plan->interval) { + wpa_printf(MSG_ERROR, + "Invalid sched scan plan input: %s", token); + goto fail; + } + + if (scan_plan->interval > wpa_s->max_sched_scan_plan_interval) { + wpa_printf(MSG_WARNING, + "scan plan %u: Scan interval too long(%u), use the maximum allowed(%u)", + num, scan_plan->interval, + wpa_s->max_sched_scan_plan_interval); + scan_plan->interval = + wpa_s->max_sched_scan_plan_interval; + } + + if (ret == 1) { + scan_plan->iterations = 0; + break; + } + + if (!scan_plan->iterations) { + wpa_printf(MSG_ERROR, + "scan plan %u: Number of iterations cannot be zero", + num); + goto fail; + } + + if (scan_plan->iterations > + wpa_s->max_sched_scan_plan_iterations) { + wpa_printf(MSG_WARNING, + "scan plan %u: Too many iterations(%u), use the maximum allowed(%u)", + num, scan_plan->iterations, + wpa_s->max_sched_scan_plan_iterations); + scan_plan->iterations = + wpa_s->max_sched_scan_plan_iterations; + } + + wpa_printf(MSG_DEBUG, + "scan plan %u: interval=%u iterations=%u", + num, scan_plan->interval, scan_plan->iterations); + } + + if (!scan_plans) { + wpa_printf(MSG_ERROR, "Invalid scan plans entry"); + goto fail; + } + + if (cstr_token(cmd, " ", &context) || scan_plans[num - 1].iterations) { + wpa_printf(MSG_ERROR, + "All scan plans but the last must specify a number of iterations"); + goto fail; + } + + wpa_printf(MSG_DEBUG, "scan plan %u (last plan): interval=%u", + num, scan_plans[num - 1].interval); + + if (num > wpa_s->max_sched_scan_plans) { + wpa_printf(MSG_WARNING, + "Too many scheduled scan plans (only %u supported)", + wpa_s->max_sched_scan_plans); + wpa_printf(MSG_WARNING, + "Use only the first %u scan plans, and the last one (in infinite loop)", + wpa_s->max_sched_scan_plans - 1); + os_memcpy(&scan_plans[wpa_s->max_sched_scan_plans - 1], + &scan_plans[num - 1], sizeof(*scan_plans)); + num = wpa_s->max_sched_scan_plans; + } + + os_free(wpa_s->sched_scan_plans); + wpa_s->sched_scan_plans = scan_plans; + wpa_s->sched_scan_plans_num = num; + + return 0; + +fail: + os_free(scan_plans); + wpa_printf(MSG_ERROR, "invalid scan plans list"); + return -1; +} + + +/** + * wpas_scan_reset_sched_scan - Reset sched_scan state + * @wpa_s: Pointer to wpa_supplicant data + * + * This function is used to cancel a running scheduled scan and to reset an + * internal scan state to continue with a regular scan on the following + * wpa_supplicant_req_scan() calls. + */ +void wpas_scan_reset_sched_scan(struct wpa_supplicant *wpa_s) +{ + wpa_s->normal_scans = 0; + if (wpa_s->sched_scanning) { + wpa_s->sched_scan_timed_out = 0; + wpa_s->prev_sched_ssid = NULL; + wpa_supplicant_cancel_sched_scan(wpa_s); + } +} + + +void wpas_scan_restart_sched_scan(struct wpa_supplicant *wpa_s) +{ + /* simulate timeout to restart the sched scan */ + wpa_s->sched_scan_timed_out = 1; + wpa_s->prev_sched_ssid = NULL; + wpa_supplicant_cancel_sched_scan(wpa_s); +} diff --git a/contrib/wpa/wpa_supplicant/scan.h b/contrib/wpa/wpa_supplicant/scan.h index 7650f5a25095..2aa0a8be0e4d 100644 --- a/contrib/wpa/wpa_supplicant/scan.h +++ b/contrib/wpa/wpa_supplicant/scan.h @@ -39,20 +39,25 @@ void wpa_supplicant_update_scan_int(struct wpa_supplicant *wpa_s, int sec); void scan_only_handler(struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res); int wpas_scan_scheduled(struct wpa_supplicant *wpa_s); -int wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s, - struct wpa_driver_scan_params *params, - int interval); -int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s); struct wpa_driver_scan_params * wpa_scan_clone_params(const struct wpa_driver_scan_params *src); void wpa_scan_free_params(struct wpa_driver_scan_params *params); int wpas_start_pno(struct wpa_supplicant *wpa_s); int wpas_stop_pno(struct wpa_supplicant *wpa_s); +void wpas_scan_reset_sched_scan(struct wpa_supplicant *wpa_s); +void wpas_scan_restart_sched_scan(struct wpa_supplicant *wpa_s); void wpas_mac_addr_rand_scan_clear(struct wpa_supplicant *wpa_s, unsigned int type); int wpas_mac_addr_rand_scan_set(struct wpa_supplicant *wpa_s, unsigned int type, const u8 *addr, const u8 *mask); +int wpas_abort_ongoing_scan(struct wpa_supplicant *wpa_s); +void filter_scan_res(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *res); +void scan_snr(struct wpa_scan_res *res); +void scan_est_throughput(struct wpa_supplicant *wpa_s, + struct wpa_scan_res *res); +void wpa_supplicant_set_default_scan_ies(struct wpa_supplicant *wpa_s); #endif /* SCAN_H */ diff --git a/contrib/wpa/wpa_supplicant/sme.c b/contrib/wpa/wpa_supplicant/sme.c index f2e5a43b978f..61fd3b24549c 100644 --- a/contrib/wpa/wpa_supplicant/sme.c +++ b/contrib/wpa/wpa_supplicant/sme.c @@ -161,9 +161,10 @@ static void sme_auth_handle_rrm(struct wpa_supplicant *wpa_s, return; } - if (!(wpa_s->drv_rrm_flags & - WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES) || - !(wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_QUIET)) { + if (!((wpa_s->drv_rrm_flags & + WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES) && + (wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_QUIET)) && + !(wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_SUPPORT_RRM)) { wpa_printf(MSG_DEBUG, "RRM: Insufficient RRM support in driver - do not use RRM"); return; @@ -186,6 +187,9 @@ static void sme_auth_handle_rrm(struct wpa_supplicant *wpa_s, if (wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION) *pos |= WLAN_RRM_CAPS_LINK_MEASUREMENT; + if (wpa_s->lci) + pos[1] |= WLAN_RRM_CAPS_LCI_MEASUREMENT; + wpa_s->sme.assoc_req_ie_len += rrm_ie_len + 2; wpa_s->rrm.rrm_used = 1; } @@ -208,6 +212,9 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, u8 ext_capab[18]; int ext_capab_len; int skip_auth; +#ifdef CONFIG_MBO + const u8 *mbo; +#endif /* CONFIG_MBO */ if (bss == NULL) { wpa_msg(wpa_s, MSG_ERROR, "SME: No scan result available for " @@ -416,9 +423,60 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_P2P */ +#ifdef CONFIG_FST + if (wpa_s->fst_ies) { + int fst_ies_len = wpabuf_len(wpa_s->fst_ies); + + if (wpa_s->sme.assoc_req_ie_len + fst_ies_len <= + sizeof(wpa_s->sme.assoc_req_ie)) { + os_memcpy(wpa_s->sme.assoc_req_ie + + wpa_s->sme.assoc_req_ie_len, + wpabuf_head(wpa_s->fst_ies), + fst_ies_len); + wpa_s->sme.assoc_req_ie_len += fst_ies_len; + } + } +#endif /* CONFIG_FST */ + + sme_auth_handle_rrm(wpa_s, bss); + +#ifdef CONFIG_MBO + mbo = wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE); + if (mbo) { + int len; + + len = wpas_mbo_supp_op_class_ie( + wpa_s, bss->freq, + wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len, + sizeof(wpa_s->sme.assoc_req_ie) - + wpa_s->sme.assoc_req_ie_len); + if (len > 0) + wpa_s->sme.assoc_req_ie_len += len; + } +#endif /* CONFIG_MBO */ + + if (params.p2p) + wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_CLIENT); + else + wpa_drv_get_ext_capa(wpa_s, WPA_IF_STATION); + + ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab, + sizeof(ext_capab)); + if (ext_capab_len > 0) { + u8 *pos = wpa_s->sme.assoc_req_ie; + if (wpa_s->sme.assoc_req_ie_len > 0 && pos[0] == WLAN_EID_RSN) + pos += 2 + pos[1]; + os_memmove(pos + ext_capab_len, pos, + wpa_s->sme.assoc_req_ie_len - + (pos - wpa_s->sme.assoc_req_ie)); + wpa_s->sme.assoc_req_ie_len += ext_capab_len; + os_memcpy(pos, ext_capab, ext_capab_len); + } + #ifdef CONFIG_HS20 if (is_hs20_network(wpa_s, ssid, bss)) { struct wpabuf *hs20; + hs20 = wpabuf_alloc(20); if (hs20) { int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid); @@ -438,34 +496,6 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_HS20 */ -#ifdef CONFIG_FST - if (wpa_s->fst_ies) { - int fst_ies_len = wpabuf_len(wpa_s->fst_ies); - - if (wpa_s->sme.assoc_req_ie_len + fst_ies_len <= - sizeof(wpa_s->sme.assoc_req_ie)) { - os_memcpy(wpa_s->sme.assoc_req_ie + - wpa_s->sme.assoc_req_ie_len, - wpabuf_head(wpa_s->fst_ies), - fst_ies_len); - wpa_s->sme.assoc_req_ie_len += fst_ies_len; - } - } -#endif /* CONFIG_FST */ - - ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab, - sizeof(ext_capab)); - if (ext_capab_len > 0) { - u8 *pos = wpa_s->sme.assoc_req_ie; - if (wpa_s->sme.assoc_req_ie_len > 0 && pos[0] == WLAN_EID_RSN) - pos += 2 + pos[1]; - os_memmove(pos + ext_capab_len, pos, - wpa_s->sme.assoc_req_ie_len - - (pos - wpa_s->sme.assoc_req_ie)); - wpa_s->sme.assoc_req_ie_len += ext_capab_len; - os_memcpy(pos, ext_capab, ext_capab_len); - } - if (wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]) { struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]; size_t len; @@ -480,7 +510,18 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, } } - sme_auth_handle_rrm(wpa_s, bss); +#ifdef CONFIG_MBO + if (mbo) { + int len; + + len = wpas_mbo_ie(wpa_s, wpa_s->sme.assoc_req_ie + + wpa_s->sme.assoc_req_ie_len, + sizeof(wpa_s->sme.assoc_req_ie) - + wpa_s->sme.assoc_req_ie_len); + if (len >= 0) + wpa_s->sme.assoc_req_ie_len += len; + } +#endif /* CONFIG_MBO */ #ifdef CONFIG_SAE if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE && @@ -524,6 +565,10 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, if (old_ssid != wpa_s->current_ssid) wpas_notify_network_changed(wpa_s); +#ifdef CONFIG_HS20 + hs20_configure_frame_filters(wpa_s); +#endif /* CONFIG_HS20 */ + #ifdef CONFIG_P2P /* * If multi-channel concurrency is not supported, check for any @@ -632,6 +677,8 @@ void sme_authenticate(struct wpa_supplicant *wpa_s, radio_remove_works(wpa_s, "sme-connect", 0); } + wpas_abort_ongoing_scan(wpa_s); + cwork = os_zalloc(sizeof(*cwork)); if (cwork == NULL) return; @@ -812,7 +859,7 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) wpa_printf(MSG_DEBUG, "SME: SAE completed - setting PMK for " "4-way handshake"); wpa_sm_set_pmk(wpa_s->wpa, wpa_s->sme.sae.pmk, PMK_LEN, - wpa_s->pending_bssid); + wpa_s->sme.sae.pmkid, wpa_s->pending_bssid); } #endif /* CONFIG_SAE */ @@ -975,8 +1022,8 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode, if (wpa_s->current_ssid && wpa_s->current_ssid->p2p_group) params.p2p = 1; - if (wpa_s->parent->set_sta_uapsd) - params.uapsd = wpa_s->parent->sta_uapsd; + if (wpa_s->p2pdev->set_sta_uapsd) + params.uapsd = wpa_s->p2pdev->sta_uapsd; else params.uapsd = -1; @@ -1320,21 +1367,6 @@ int sme_proc_obss_scan(struct wpa_supplicant *wpa_s) } -static struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes, - u16 num_modes, - enum hostapd_hw_mode mode) -{ - u16 i; - - for (i = 0; i < num_modes; i++) { - if (modes[i].mode == mode) - return &modes[i]; - } - - return NULL; -} - - static void wpa_obss_scan_freqs_list(struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params) { @@ -1553,8 +1585,10 @@ static void sme_sa_query_timer(void *eloop_ctx, void *timeout_ctx) nbuf = os_realloc_array(wpa_s->sme.sa_query_trans_id, wpa_s->sme.sa_query_count + 1, WLAN_SA_QUERY_TR_ID_LEN); - if (nbuf == NULL) + if (nbuf == NULL) { + sme_stop_sa_query(wpa_s); return; + } if (wpa_s->sme.sa_query_count == 0) { /* Starting a new SA Query procedure */ os_get_reltime(&wpa_s->sme.sa_query_start); @@ -1565,6 +1599,7 @@ static void sme_sa_query_timer(void *eloop_ctx, void *timeout_ctx) if (os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN) < 0) { wpa_printf(MSG_DEBUG, "Could not generate SA Query ID"); + sme_stop_sa_query(wpa_s); return; } diff --git a/contrib/wpa/wpa_supplicant/systemd/wpa_supplicant-nl80211.service.arg.in b/contrib/wpa/wpa_supplicant/systemd/wpa_supplicant-nl80211.service.arg.in new file mode 100644 index 000000000000..03ac50705995 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/systemd/wpa_supplicant-nl80211.service.arg.in @@ -0,0 +1,15 @@ +[Unit] +Description=WPA supplicant daemon (interface- and nl80211 driver-specific version) +Requires=sys-subsystem-net-devices-%i.device +After=sys-subsystem-net-devices-%i.device +Before=network.target +Wants=network.target + +# NetworkManager users will probably want the dbus version instead. + +[Service] +Type=simple +ExecStart=@BINDIR@/wpa_supplicant -c/etc/wpa_supplicant/wpa_supplicant-nl80211-%I.conf -Dnl80211 -i%I + +[Install] +Alias=multi-user.target.wants/wpa_supplicant-nl80211@%i.service diff --git a/contrib/wpa/wpa_supplicant/systemd/wpa_supplicant-wired.service.arg.in b/contrib/wpa/wpa_supplicant/systemd/wpa_supplicant-wired.service.arg.in new file mode 100644 index 000000000000..c8a744d6e138 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/systemd/wpa_supplicant-wired.service.arg.in @@ -0,0 +1,15 @@ +[Unit] +Description=WPA supplicant daemon (interface- and wired driver-specific version) +Requires=sys-subsystem-net-devices-%i.device +After=sys-subsystem-net-devices-%i.device +Before=network.target +Wants=network.target + +# NetworkManager users will probably want the dbus version instead. + +[Service] +Type=simple +ExecStart=@BINDIR@/wpa_supplicant -c/etc/wpa_supplicant/wpa_supplicant-wired-%I.conf -Dwired -i%I + +[Install] +Alias=multi-user.target.wants/wpa_supplicant-wired@%i.service diff --git a/contrib/wpa/wpa_supplicant/systemd/wpa_supplicant.service.arg.in b/contrib/wpa/wpa_supplicant/systemd/wpa_supplicant.service.arg.in new file mode 100644 index 000000000000..7788b380c4a2 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/systemd/wpa_supplicant.service.arg.in @@ -0,0 +1,15 @@ +[Unit] +Description=WPA supplicant daemon (interface-specific version) +Requires=sys-subsystem-net-devices-%i.device +After=sys-subsystem-net-devices-%i.device +Before=network.target +Wants=network.target + +# NetworkManager users will probably want the dbus version instead. + +[Service] +Type=simple +ExecStart=@BINDIR@/wpa_supplicant -c/etc/wpa_supplicant/wpa_supplicant-%I.conf -i%I + +[Install] +Alias=multi-user.target.wants/wpa_supplicant@%i.service diff --git a/contrib/wpa/wpa_supplicant/systemd/wpa_supplicant.service.in b/contrib/wpa/wpa_supplicant/systemd/wpa_supplicant.service.in new file mode 100644 index 000000000000..bc5d49af8655 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/systemd/wpa_supplicant.service.in @@ -0,0 +1,13 @@ +[Unit] +Description=WPA supplicant +Before=network.target +Wants=network.target + +[Service] +Type=dbus +BusName=@DBUS_INTERFACE@ +ExecStart=@BINDIR@/wpa_supplicant -u + +[Install] +WantedBy=multi-user.target +Alias=dbus-@DBUS_INTERFACE@.service diff --git a/contrib/wpa/wpa_supplicant/tests/link_test.c b/contrib/wpa/wpa_supplicant/tests/link_test.c deleted file mode 100644 index 3bfbed577d8c..000000000000 --- a/contrib/wpa/wpa_supplicant/tests/link_test.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Dummy functions to allow link_test to be linked. The need for these - * functions should be removed to allow IEEE 802.1X/EAPOL authenticator to - * be built outside hostapd. - */ - -#include "includes.h" - -#include "common.h" - - -struct hostapd_data; -struct sta_info; -struct rsn_pmksa_cache_entry; -struct eapol_state_machine; -struct hostapd_eap_user; -struct hostapd_bss_config; -struct hostapd_vlan; - - -struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta) -{ - return NULL; -} - - -int ap_for_each_sta(struct hostapd_data *hapd, - int (*cb)(struct hostapd_data *hapd, struct sta_info *sta, - void *ctx), - void *ctx) -{ - return 0; -} - - -void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta, - u32 session_timeout) -{ -} - - -int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, - int old_vlanid) -{ - return 0; -} - - -void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta, - int success) -{ -} - - -void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta, - u8 *buf, size_t len) -{ -} - - -void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta) -{ -} - - -void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry, - struct eapol_state_machine *eapol) -{ -} - - -const struct hostapd_eap_user * -hostapd_get_eap_user(const struct hostapd_bss_config *conf, const u8 *identity, - size_t identity_len, int phase2) -{ - return NULL; -} - - -const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id) -{ - return NULL; -} diff --git a/contrib/wpa/wpa_supplicant/tests/test_eap_sim_common.c b/contrib/wpa/wpa_supplicant/tests/test_eap_sim_common.c deleted file mode 100644 index f60b1821e259..000000000000 --- a/contrib/wpa/wpa_supplicant/tests/test_eap_sim_common.c +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Test program for EAP-SIM PRF - * Copyright (c) 2004-2006, Jouni Malinen - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -#include "eap_common/eap_sim_common.c" - - -static int test_eap_sim_prf(void) -{ - /* http://csrc.nist.gov/encryption/dss/Examples-1024bit.pdf */ - u8 xkey[] = { - 0xbd, 0x02, 0x9b, 0xbe, 0x7f, 0x51, 0x96, 0x0b, - 0xcf, 0x9e, 0xdb, 0x2b, 0x61, 0xf0, 0x6f, 0x0f, - 0xeb, 0x5a, 0x38, 0xb6 - }; - u8 w[] = { - 0x20, 0x70, 0xb3, 0x22, 0x3d, 0xba, 0x37, 0x2f, - 0xde, 0x1c, 0x0f, 0xfc, 0x7b, 0x2e, 0x3b, 0x49, - 0x8b, 0x26, 0x06, 0x14, 0x3c, 0x6c, 0x18, 0xba, - 0xcb, 0x0f, 0x6c, 0x55, 0xba, 0xbb, 0x13, 0x78, - 0x8e, 0x20, 0xd7, 0x37, 0xa3, 0x27, 0x51, 0x16 - }; - u8 buf[40]; - - printf("Testing EAP-SIM PRF (FIPS 186-2 + change notice 1)\n"); - eap_sim_prf(xkey, buf, sizeof(buf)); - if (memcmp(w, buf, sizeof(w)) != 0) { - printf("eap_sim_prf failed\n"); - return 1; - } - - return 0; -} - - -int main(int argc, char *argv[]) -{ - int errors = 0; - - errors += test_eap_sim_prf(); - - return errors; -} diff --git a/contrib/wpa/wpa_supplicant/tests/test_wpa.c b/contrib/wpa/wpa_supplicant/tests/test_wpa.c deleted file mode 100644 index 39971f285de3..000000000000 --- a/contrib/wpa/wpa_supplicant/tests/test_wpa.c +++ /dev/null @@ -1,369 +0,0 @@ -/* - * Test program for combined WPA authenticator/supplicant - * Copyright (c) 2006-2007, Jouni Malinen - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -#include "includes.h" - -#include "common.h" -#include "eloop.h" -#include "common/ieee802_11_defs.h" -#include "../config.h" -#include "rsn_supp/wpa.h" -#include "rsn_supp/wpa_ie.h" -#include "ap/wpa_auth.h" - - -struct wpa { - u8 auth_addr[ETH_ALEN]; - u8 supp_addr[ETH_ALEN]; - u8 psk[PMK_LEN]; - - /* from authenticator */ - u8 auth_eapol_dst[ETH_ALEN]; - u8 *auth_eapol; - size_t auth_eapol_len; - - /* from supplicant */ - u8 *supp_eapol; - size_t supp_eapol_len; - - struct wpa_sm *supp; - struct wpa_authenticator *auth_group; - struct wpa_state_machine *auth; - - struct wpa_ssid ssid; - u8 supp_ie[80]; - size_t supp_ie_len; -}; - - -static int supp_get_bssid(void *ctx, u8 *bssid) -{ - struct wpa *wpa = ctx; - wpa_printf(MSG_DEBUG, "SUPP: %s", __func__); - os_memcpy(bssid, wpa->auth_addr, ETH_ALEN); - return 0; -} - - -static void supp_set_state(void *ctx, enum wpa_states state) -{ - wpa_printf(MSG_DEBUG, "SUPP: %s(state=%d)", __func__, state); -} - - -static void auth_eapol_rx(void *eloop_data, void *user_ctx) -{ - struct wpa *wpa = eloop_data; - - wpa_printf(MSG_DEBUG, "AUTH: RX EAPOL frame"); - wpa_receive(wpa->auth_group, wpa->auth, wpa->supp_eapol, - wpa->supp_eapol_len); -} - - -static int supp_ether_send(void *ctx, const u8 *dest, u16 proto, const u8 *buf, - size_t len) -{ - struct wpa *wpa = ctx; - - wpa_printf(MSG_DEBUG, "SUPP: %s(dest=" MACSTR " proto=0x%04x " - "len=%lu)", - __func__, MAC2STR(dest), proto, (unsigned long) len); - - os_free(wpa->supp_eapol); - wpa->supp_eapol = os_malloc(len); - if (wpa->supp_eapol == NULL) - return -1; - os_memcpy(wpa->supp_eapol, buf, len); - wpa->supp_eapol_len = len; - eloop_register_timeout(0, 0, auth_eapol_rx, wpa, NULL); - - return 0; -} - - -static u8 * supp_alloc_eapol(void *ctx, u8 type, const void *data, - u16 data_len, size_t *msg_len, void **data_pos) -{ - struct ieee802_1x_hdr *hdr; - - wpa_printf(MSG_DEBUG, "SUPP: %s(type=%d data_len=%d)", - __func__, type, data_len); - - *msg_len = sizeof(*hdr) + data_len; - hdr = os_malloc(*msg_len); - if (hdr == NULL) - return NULL; - - hdr->version = 2; - hdr->type = type; - hdr->length = host_to_be16(data_len); - - if (data) - os_memcpy(hdr + 1, data, data_len); - else - os_memset(hdr + 1, 0, data_len); - - if (data_pos) - *data_pos = hdr + 1; - - return (u8 *) hdr; -} - - -static int supp_get_beacon_ie(void *ctx) -{ - struct wpa *wpa = ctx; - const u8 *ie; - size_t ielen; - - wpa_printf(MSG_DEBUG, "SUPP: %s", __func__); - - ie = wpa_auth_get_wpa_ie(wpa->auth_group, &ielen); - if (ie == NULL || ielen < 1) - return -1; - if (ie[0] == WLAN_EID_RSN) - return wpa_sm_set_ap_rsn_ie(wpa->supp, ie, 2 + ie[1]); - return wpa_sm_set_ap_wpa_ie(wpa->supp, ie, 2 + ie[1]); -} - - -static int supp_set_key(void *ctx, enum wpa_alg alg, - const u8 *addr, int key_idx, int set_tx, - const u8 *seq, size_t seq_len, - const u8 *key, size_t key_len) -{ - wpa_printf(MSG_DEBUG, "SUPP: %s(alg=%d addr=" MACSTR " key_idx=%d " - "set_tx=%d)", - __func__, alg, MAC2STR(addr), key_idx, set_tx); - wpa_hexdump(MSG_DEBUG, "SUPP: set_key - seq", seq, seq_len); - wpa_hexdump(MSG_DEBUG, "SUPP: set_key - key", key, key_len); - return 0; -} - - -static int supp_mlme_setprotection(void *ctx, const u8 *addr, - int protection_type, int key_type) -{ - wpa_printf(MSG_DEBUG, "SUPP: %s(addr=" MACSTR " protection_type=%d " - "key_type=%d)", - __func__, MAC2STR(addr), protection_type, key_type); - return 0; -} - - -static void supp_cancel_auth_timeout(void *ctx) -{ - wpa_printf(MSG_DEBUG, "SUPP: %s", __func__); -} - - -static int supp_init(struct wpa *wpa) -{ - struct wpa_sm_ctx *ctx = os_zalloc(sizeof(*ctx)); - if (ctx == NULL) - return -1; - - ctx->ctx = wpa; - ctx->msg_ctx = wpa; - ctx->set_state = supp_set_state; - ctx->get_bssid = supp_get_bssid; - ctx->ether_send = supp_ether_send; - ctx->get_beacon_ie = supp_get_beacon_ie; - ctx->alloc_eapol = supp_alloc_eapol; - ctx->set_key = supp_set_key; - ctx->mlme_setprotection = supp_mlme_setprotection; - ctx->cancel_auth_timeout = supp_cancel_auth_timeout; - wpa->supp = wpa_sm_init(ctx); - if (wpa->supp == NULL) { - wpa_printf(MSG_DEBUG, "SUPP: wpa_sm_init() failed"); - return -1; - } - - wpa_sm_set_own_addr(wpa->supp, wpa->supp_addr); - wpa_sm_set_param(wpa->supp, WPA_PARAM_RSN_ENABLED, 1); - wpa_sm_set_param(wpa->supp, WPA_PARAM_PROTO, WPA_PROTO_RSN); - wpa_sm_set_param(wpa->supp, WPA_PARAM_PAIRWISE, WPA_CIPHER_CCMP); - wpa_sm_set_param(wpa->supp, WPA_PARAM_GROUP, WPA_CIPHER_CCMP); - wpa_sm_set_param(wpa->supp, WPA_PARAM_KEY_MGMT, WPA_KEY_MGMT_PSK); - wpa_sm_set_pmk(wpa->supp, wpa->psk, PMK_LEN); - - wpa->supp_ie_len = sizeof(wpa->supp_ie); - if (wpa_sm_set_assoc_wpa_ie_default(wpa->supp, wpa->supp_ie, - &wpa->supp_ie_len) < 0) { - wpa_printf(MSG_DEBUG, "SUPP: wpa_sm_set_assoc_wpa_ie_default()" - " failed"); - return -1; - } - - wpa_sm_notify_assoc(wpa->supp, wpa->auth_addr); - - return 0; -} - - -static void auth_logger(void *ctx, const u8 *addr, logger_level level, - const char *txt) -{ - if (addr) - wpa_printf(MSG_DEBUG, "AUTH: " MACSTR " - %s", - MAC2STR(addr), txt); - else - wpa_printf(MSG_DEBUG, "AUTH: %s", txt); -} - - -static void supp_eapol_rx(void *eloop_data, void *user_ctx) -{ - struct wpa *wpa = eloop_data; - - wpa_printf(MSG_DEBUG, "SUPP: RX EAPOL frame"); - wpa_sm_rx_eapol(wpa->supp, wpa->auth_addr, wpa->auth_eapol, - wpa->auth_eapol_len); -} - - -static int auth_send_eapol(void *ctx, const u8 *addr, const u8 *data, - size_t data_len, int encrypt) -{ - struct wpa *wpa = ctx; - - wpa_printf(MSG_DEBUG, "AUTH: %s(addr=" MACSTR " data_len=%lu " - "encrypt=%d)", - __func__, MAC2STR(addr), (unsigned long) data_len, encrypt); - - os_free(wpa->auth_eapol); - wpa->auth_eapol = os_malloc(data_len); - if (wpa->auth_eapol == NULL) - return -1; - os_memcpy(wpa->auth_eapol_dst, addr, ETH_ALEN); - os_memcpy(wpa->auth_eapol, data, data_len); - wpa->auth_eapol_len = data_len; - eloop_register_timeout(0, 0, supp_eapol_rx, wpa, NULL); - - return 0; -} - - -static const u8 * auth_get_psk(void *ctx, const u8 *addr, const u8 *prev_psk) -{ - struct wpa *wpa = ctx; - wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)", - __func__, MAC2STR(addr), prev_psk); - if (prev_psk) - return NULL; - return wpa->psk; -} - - -static int auth_init_group(struct wpa *wpa) -{ - struct wpa_auth_config conf; - struct wpa_auth_callbacks cb; - - wpa_printf(MSG_DEBUG, "AUTH: Initializing group state machine"); - - os_memset(&conf, 0, sizeof(conf)); - conf.wpa = 2; - conf.wpa_key_mgmt = WPA_KEY_MGMT_PSK; - conf.wpa_pairwise = WPA_CIPHER_CCMP; - conf.rsn_pairwise = WPA_CIPHER_CCMP; - conf.wpa_group = WPA_CIPHER_CCMP; - conf.eapol_version = 2; - - os_memset(&cb, 0, sizeof(cb)); - cb.ctx = wpa; - cb.logger = auth_logger; - cb.send_eapol = auth_send_eapol; - cb.get_psk = auth_get_psk; - - wpa->auth_group = wpa_init(wpa->auth_addr, &conf, &cb); - if (wpa->auth_group == NULL) { - wpa_printf(MSG_DEBUG, "AUTH: wpa_init() failed"); - return -1; - } - - return 0; -} - - -static int auth_init(struct wpa *wpa) -{ - wpa->auth = wpa_auth_sta_init(wpa->auth_group, wpa->supp_addr, NULL); - if (wpa->auth == NULL) { - wpa_printf(MSG_DEBUG, "AUTH: wpa_auth_sta_init() failed"); - return -1; - } - - if (wpa_validate_wpa_ie(wpa->auth_group, wpa->auth, wpa->supp_ie, - wpa->supp_ie_len, NULL, 0) != WPA_IE_OK) { - wpa_printf(MSG_DEBUG, "AUTH: wpa_validate_wpa_ie() failed"); - return -1; - } - - wpa_auth_sm_event(wpa->auth, WPA_ASSOC); - - wpa_auth_sta_associated(wpa->auth_group, wpa->auth); - - return 0; -} - - -static void deinit(struct wpa *wpa) -{ - wpa_auth_sta_deinit(wpa->auth); - wpa_sm_deinit(wpa->supp); - wpa_deinit(wpa->auth_group); - os_free(wpa->auth_eapol); - wpa->auth_eapol = NULL; - os_free(wpa->supp_eapol); - wpa->supp_eapol = NULL; -} - - -int main(int argc, char *argv[]) -{ - struct wpa wpa; - - if (os_program_init()) - return -1; - - os_memset(&wpa, 0, sizeof(wpa)); - os_memset(wpa.auth_addr, 0x12, ETH_ALEN); - os_memset(wpa.supp_addr, 0x32, ETH_ALEN); - os_memset(wpa.psk, 0x44, PMK_LEN); - - wpa_debug_level = 0; - wpa_debug_show_keys = 1; - - if (eloop_init()) { - wpa_printf(MSG_ERROR, "Failed to initialize event loop"); - return -1; - } - - if (auth_init_group(&wpa) < 0) - return -1; - - if (supp_init(&wpa) < 0) - return -1; - - if (auth_init(&wpa) < 0) - return -1; - - wpa_printf(MSG_DEBUG, "Starting eloop"); - eloop_run(); - wpa_printf(MSG_DEBUG, "eloop done"); - - deinit(&wpa); - - eloop_destroy(); - - os_program_deinit(); - - return 0; -} diff --git a/contrib/wpa/wpa_supplicant/vs2005/eapol_test/eapol_test.vcproj b/contrib/wpa/wpa_supplicant/vs2005/eapol_test/eapol_test.vcproj new file mode 100755 index 000000000000..af7b3fe9ceb0 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/vs2005/eapol_test/eapol_test.vcproj @@ -0,0 +1,473 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contrib/wpa/wpa_supplicant/vs2005/win_if_list/win_if_list.vcproj b/contrib/wpa/wpa_supplicant/vs2005/win_if_list/win_if_list.vcproj new file mode 100755 index 000000000000..e79fc0f4666f --- /dev/null +++ b/contrib/wpa/wpa_supplicant/vs2005/win_if_list/win_if_list.vcproj @@ -0,0 +1,203 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contrib/wpa/wpa_supplicant/vs2005/wpa_cli/wpa_cli.vcproj b/contrib/wpa/wpa_supplicant/vs2005/wpa_cli/wpa_cli.vcproj new file mode 100755 index 000000000000..d2de768e7cdc --- /dev/null +++ b/contrib/wpa/wpa_supplicant/vs2005/wpa_cli/wpa_cli.vcproj @@ -0,0 +1,215 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contrib/wpa/wpa_supplicant/vs2005/wpa_passphrase/wpa_passphrase.vcproj b/contrib/wpa/wpa_supplicant/vs2005/wpa_passphrase/wpa_passphrase.vcproj new file mode 100755 index 000000000000..97aa2c5aecb5 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/vs2005/wpa_passphrase/wpa_passphrase.vcproj @@ -0,0 +1,236 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contrib/wpa/wpa_supplicant/vs2005/wpa_supplicant.sln b/contrib/wpa/wpa_supplicant/vs2005/wpa_supplicant.sln new file mode 100755 index 000000000000..df89e3198d2f --- /dev/null +++ b/contrib/wpa/wpa_supplicant/vs2005/wpa_supplicant.sln @@ -0,0 +1,52 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wpa_supplicant", "wpa_supplicant\wpa_supplicant.vcproj", "{8BCFDA77-AEDC-4168-8897-5B73105BBB87}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wpa_cli", "wpa_cli\wpa_cli.vcproj", "{E3A7B181-22CC-4DA3-8410-6AD69879A9EC}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wpasvc", "wpasvc\wpasvc.vcproj", "{E2A4A85F-CA77-406D-8ABF-63EF94545ACC}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wpa_passphrase", "wpa_passphrase\wpa_passphrase.vcproj", "{ADBE4EA8-F0C5-40C2-AE89-C56D0F2EC1DF}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "win_if_list", "win_if_list\win_if_list.vcproj", "{9E87CD9C-60CE-4533-85CF-85CA3A9BF26A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "eapol_test", "eapol_test\eapol_test.vcproj", "{0E3F2C6D-1372-48D6-BCAB-E584917C4DE3}" +EndProject +Global + GlobalSection(DPCodeReviewSolutionGUID) = preSolution + DPCodeReviewSolutionGUID = {00000000-0000-0000-0000-000000000000} + EndGlobalSection + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8BCFDA77-AEDC-4168-8897-5B73105BBB87}.Debug|Win32.ActiveCfg = Debug|Win32 + {8BCFDA77-AEDC-4168-8897-5B73105BBB87}.Debug|Win32.Build.0 = Debug|Win32 + {8BCFDA77-AEDC-4168-8897-5B73105BBB87}.Release|Win32.ActiveCfg = Release|Win32 + {8BCFDA77-AEDC-4168-8897-5B73105BBB87}.Release|Win32.Build.0 = Release|Win32 + {E3A7B181-22CC-4DA3-8410-6AD69879A9EC}.Debug|Win32.ActiveCfg = Debug|Win32 + {E3A7B181-22CC-4DA3-8410-6AD69879A9EC}.Debug|Win32.Build.0 = Debug|Win32 + {E3A7B181-22CC-4DA3-8410-6AD69879A9EC}.Release|Win32.ActiveCfg = Release|Win32 + {E3A7B181-22CC-4DA3-8410-6AD69879A9EC}.Release|Win32.Build.0 = Release|Win32 + {E2A4A85F-CA77-406D-8ABF-63EF94545ACC}.Debug|Win32.ActiveCfg = Debug|Win32 + {E2A4A85F-CA77-406D-8ABF-63EF94545ACC}.Debug|Win32.Build.0 = Debug|Win32 + {E2A4A85F-CA77-406D-8ABF-63EF94545ACC}.Release|Win32.ActiveCfg = Release|Win32 + {E2A4A85F-CA77-406D-8ABF-63EF94545ACC}.Release|Win32.Build.0 = Release|Win32 + {ADBE4EA8-F0C5-40C2-AE89-C56D0F2EC1DF}.Debug|Win32.ActiveCfg = Debug|Win32 + {ADBE4EA8-F0C5-40C2-AE89-C56D0F2EC1DF}.Debug|Win32.Build.0 = Debug|Win32 + {ADBE4EA8-F0C5-40C2-AE89-C56D0F2EC1DF}.Release|Win32.ActiveCfg = Release|Win32 + {ADBE4EA8-F0C5-40C2-AE89-C56D0F2EC1DF}.Release|Win32.Build.0 = Release|Win32 + {9E87CD9C-60CE-4533-85CF-85CA3A9BF26A}.Debug|Win32.ActiveCfg = Debug|Win32 + {9E87CD9C-60CE-4533-85CF-85CA3A9BF26A}.Debug|Win32.Build.0 = Debug|Win32 + {9E87CD9C-60CE-4533-85CF-85CA3A9BF26A}.Release|Win32.ActiveCfg = Release|Win32 + {9E87CD9C-60CE-4533-85CF-85CA3A9BF26A}.Release|Win32.Build.0 = Release|Win32 + {0E3F2C6D-1372-48D6-BCAB-E584917C4DE3}.Debug|Win32.ActiveCfg = Debug|Win32 + {0E3F2C6D-1372-48D6-BCAB-E584917C4DE3}.Debug|Win32.Build.0 = Debug|Win32 + {0E3F2C6D-1372-48D6-BCAB-E584917C4DE3}.Release|Win32.ActiveCfg = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/contrib/wpa/wpa_supplicant/vs2005/wpa_supplicant/wpa_supplicant.vcproj b/contrib/wpa/wpa_supplicant/vs2005/wpa_supplicant/wpa_supplicant.vcproj new file mode 100755 index 000000000000..51acab9270c6 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/vs2005/wpa_supplicant/wpa_supplicant.vcproj @@ -0,0 +1,461 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contrib/wpa/wpa_supplicant/vs2005/wpasvc/wpasvc.vcproj b/contrib/wpa/wpa_supplicant/vs2005/wpasvc/wpasvc.vcproj new file mode 100755 index 000000000000..6fd8af80303b --- /dev/null +++ b/contrib/wpa/wpa_supplicant/vs2005/wpasvc/wpasvc.vcproj @@ -0,0 +1,461 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contrib/wpa/wpa_supplicant/wmm_ac.h b/contrib/wpa/wpa_supplicant/wmm_ac.h index 5171b1683ef7..0d15ad01cc58 100644 --- a/contrib/wpa/wpa_supplicant/wmm_ac.h +++ b/contrib/wpa/wpa_supplicant/wmm_ac.h @@ -88,7 +88,7 @@ enum ts_dir_idx { */ struct wmm_ac_addts_request { /* - * dialog token - Used to link the recived ADDTS response with this + * dialog token - Used to link the received ADDTS response with this * saved ADDTS request when ADDTS response is being handled */ u8 dialog_token; diff --git a/contrib/wpa/wpa_supplicant/wnm_sta.c b/contrib/wpa/wpa_supplicant/wnm_sta.c index d88c863ac25e..8307f338af7f 100644 --- a/contrib/wpa/wpa_supplicant/wnm_sta.c +++ b/contrib/wpa/wpa_supplicant/wnm_sta.c @@ -24,6 +24,7 @@ #define MAX_TFS_IE_LEN 1024 #define WNM_MAX_NEIGHBOR_REPORT 10 +#define WNM_SCAN_RESULT_AGE 2 /* 2 seconds */ /* get the TFS IE from driver */ static int ieee80211_11_get_tfs_ie(struct wpa_supplicant *wpa_s, u8 *buf, @@ -37,12 +38,14 @@ static int ieee80211_11_get_tfs_ie(struct wpa_supplicant *wpa_s, u8 *buf, /* set the TFS IE to driver */ static int ieee80211_11_set_tfs_ie(struct wpa_supplicant *wpa_s, - const u8 *addr, u8 *buf, u16 *buf_len, + const u8 *addr, const u8 *buf, u16 buf_len, enum wnm_oper oper) { + u16 len = buf_len; + wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper); - return wpa_drv_wnm_oper(wpa_s, oper, addr, buf, buf_len); + return wpa_drv_wnm_oper(wpa_s, oper, addr, (u8 *) buf, &len); } @@ -149,8 +152,8 @@ int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s, static void wnm_sleep_mode_enter_success(struct wpa_supplicant *wpa_s, - u8 *tfsresp_ie_start, - u8 *tfsresp_ie_end) + const u8 *tfsresp_ie_start, + const u8 *tfsresp_ie_end) { wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_CONFIRM, wpa_s->bssid, NULL, NULL); @@ -166,7 +169,7 @@ static void wnm_sleep_mode_enter_success(struct wpa_supplicant *wpa_s, /* pass the TFS Resp IE(s) to driver for processing */ if (ieee80211_11_set_tfs_ie(wpa_s, wpa_s->bssid, tfsresp_ie_start, - &tfsresp_ie_len, + tfsresp_ie_len, WNM_SLEEP_TFS_RESP_IE_SET)) wpa_printf(MSG_DEBUG, "WNM: Fail to set TFS Resp IE"); } @@ -247,12 +250,12 @@ static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s, * Action [1] | Dialog Token [1] | Key Data Len [2] | Key Data | * WNM-Sleep Mode IE | TFS Response IE */ - u8 *pos = (u8 *) frm; /* point to payload after the action field */ + const u8 *pos = frm; /* point to payload after the action field */ u16 key_len_total; struct wnm_sleep_element *wnmsleep_ie = NULL; /* multiple TFS Resp IE (assuming consecutive) */ - u8 *tfsresp_ie_start = NULL; - u8 *tfsresp_ie_end = NULL; + const u8 *tfsresp_ie_start = NULL; + const u8 *tfsresp_ie_end = NULL; size_t left; if (!wpa_s->wnmsleep_used) { @@ -273,14 +276,14 @@ static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s, return; } pos += 3 + key_len_total; - while (pos - frm < len) { + while (pos - frm + 1 < len) { u8 ie_len = *(pos + 1); - if (pos + 2 + ie_len > frm + len) { + if (2 + ie_len > frm + len - pos) { wpa_printf(MSG_INFO, "WNM: Invalid IE len %u", ie_len); break; } wpa_hexdump(MSG_DEBUG, "WNM: Element", pos, 2 + ie_len); - if (*pos == WLAN_EID_WNMSLEEP) + if (*pos == WLAN_EID_WNMSLEEP && ie_len >= 4) wnmsleep_ie = (struct wnm_sleep_element *) pos; else if (*pos == WLAN_EID_TFS_RESP) { if (!tfsresp_ie_start) @@ -429,6 +432,7 @@ static int wnm_nei_get_chan(struct wpa_supplicant *wpa_s, u8 op_class, u8 chan) { struct wpa_bss *bss = wpa_s->current_bss; const char *country = NULL; + int freq; if (bss) { const u8 *elem = wpa_bss_get_ie(bss, WLAN_EID_COUNTRY); @@ -437,7 +441,21 @@ static int wnm_nei_get_chan(struct wpa_supplicant *wpa_s, u8 op_class, u8 chan) country = (const char *) (elem + 2); } - return ieee80211_chan_to_freq(country, op_class, chan); + freq = ieee80211_chan_to_freq(country, op_class, chan); + if (freq <= 0 && op_class == 0) { + /* + * Some APs do not advertise correct operating class + * information. Try to determine the most likely operating + * frequency based on the channel number. + */ + if (chan >= 1 && chan <= 13) + freq = 2407 + chan * 5; + else if (chan == 14) + freq = 2484; + else if (chan >= 36 && chan <= 169) + freq = 5000 + chan * 5; + } + return freq; } @@ -484,7 +502,7 @@ static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s, static struct wpa_bss * -compare_scan_neighbor_results(struct wpa_supplicant *wpa_s) +compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs) { u8 i; @@ -492,7 +510,7 @@ compare_scan_neighbor_results(struct wpa_supplicant *wpa_s) struct wpa_bss *target; if (!bss) - return 0; + return NULL; wpa_printf(MSG_DEBUG, "WNM: Current BSS " MACSTR " RSSI %d", MAC2STR(wpa_s->bssid), bss->level); @@ -517,6 +535,19 @@ compare_scan_neighbor_results(struct wpa_supplicant *wpa_s) continue; } + if (age_secs) { + struct os_reltime now; + + if (os_get_reltime(&now) == 0 && + os_reltime_expired(&now, &target->last_update, + age_secs)) { + wpa_printf(MSG_DEBUG, + "Candidate BSS is more than %ld seconds old", + age_secs); + continue; + } + } + if (bss->ssid_len != target->ssid_len || os_memcmp(bss->ssid, target->ssid, bss->ssid_len) != 0) { /* @@ -531,6 +562,25 @@ compare_scan_neighbor_results(struct wpa_supplicant *wpa_s) continue; } + if (wpa_s->current_ssid && + !wpa_scan_res_match(wpa_s, 0, target, wpa_s->current_ssid, + 1)) { + wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR + " (pref %d) does not match the current network profile", + MAC2STR(nei->bssid), + nei->preference_present ? nei->preference : + -1); + continue; + } + + if (wpa_is_bss_tmp_disallowed(wpa_s, target->bssid)) { + wpa_printf(MSG_DEBUG, + "MBO: Candidate BSS " MACSTR + " retry delay is not over yet", + MAC2STR(nei->bssid)); + continue; + } + if (target->level < bss->level && target->level < -80) { wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR " (pref %d) does not have sufficient signal level (%d)", @@ -552,12 +602,190 @@ compare_scan_neighbor_results(struct wpa_supplicant *wpa_s) } +static int wpa_bss_ies_eq(struct wpa_bss *a, struct wpa_bss *b, u8 eid) +{ + const u8 *ie_a, *ie_b; + + if (!a || !b) + return 0; + + ie_a = wpa_bss_get_ie(a, eid); + ie_b = wpa_bss_get_ie(b, eid); + + if (!ie_a || !ie_b || ie_a[1] != ie_b[1]) + return 0; + + return os_memcmp(ie_a, ie_b, ie_a[1]) == 0; +} + + +static u32 wnm_get_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) +{ + u32 info = 0; + + info |= NEI_REP_BSSID_INFO_AP_UNKNOWN_REACH; + + /* + * Leave the security and key scope bits unset to indicate that the + * security information is not available. + */ + + if (bss->caps & WLAN_CAPABILITY_SPECTRUM_MGMT) + info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT; + if (bss->caps & WLAN_CAPABILITY_QOS) + info |= NEI_REP_BSSID_INFO_QOS; + if (bss->caps & WLAN_CAPABILITY_APSD) + info |= NEI_REP_BSSID_INFO_APSD; + if (bss->caps & WLAN_CAPABILITY_RADIO_MEASUREMENT) + info |= NEI_REP_BSSID_INFO_RM; + if (bss->caps & WLAN_CAPABILITY_DELAYED_BLOCK_ACK) + info |= NEI_REP_BSSID_INFO_DELAYED_BA; + if (bss->caps & WLAN_CAPABILITY_IMM_BLOCK_ACK) + info |= NEI_REP_BSSID_INFO_IMM_BA; + if (wpa_bss_ies_eq(bss, wpa_s->current_bss, WLAN_EID_MOBILITY_DOMAIN)) + info |= NEI_REP_BSSID_INFO_MOBILITY_DOMAIN; + if (wpa_bss_ies_eq(bss, wpa_s->current_bss, WLAN_EID_HT_CAP)) + info |= NEI_REP_BSSID_INFO_HT; + + return info; +} + + +static int wnm_add_nei_rep(u8 *buf, size_t len, const u8 *bssid, u32 bss_info, + u8 op_class, u8 chan, u8 phy_type, u8 pref) +{ + u8 *pos = buf; + + if (len < 18) { + wpa_printf(MSG_DEBUG, + "WNM: Not enough room for Neighbor Report element"); + return -1; + } + + *pos++ = WLAN_EID_NEIGHBOR_REPORT; + /* length: 13 for basic neighbor report + 3 for preference subelement */ + *pos++ = 16; + os_memcpy(pos, bssid, ETH_ALEN); + pos += ETH_ALEN; + WPA_PUT_LE32(pos, bss_info); + pos += 4; + *pos++ = op_class; + *pos++ = chan; + *pos++ = phy_type; + *pos++ = WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE; + *pos++ = 1; + *pos++ = pref; + return pos - buf; +} + + +static int wnm_nei_rep_add_bss(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss, u8 *buf, size_t len, + u8 pref) +{ + const u8 *ie; + u8 op_class, chan; + int sec_chan = 0, vht = 0; + enum phy_type phy_type; + u32 info; + struct ieee80211_ht_operation *ht_oper = NULL; + struct ieee80211_vht_operation *vht_oper = NULL; + + ie = wpa_bss_get_ie(bss, WLAN_EID_HT_OPERATION); + if (ie && ie[1] >= 2) { + ht_oper = (struct ieee80211_ht_operation *) (ie + 2); + + if (ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE) + sec_chan = 1; + else if (ht_oper->ht_param & + HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW) + sec_chan = -1; + } + + ie = wpa_bss_get_ie(bss, WLAN_EID_VHT_OPERATION); + if (ie && ie[1] >= 1) { + vht_oper = (struct ieee80211_vht_operation *) (ie + 2); + + if (vht_oper->vht_op_info_chwidth == VHT_CHANWIDTH_80MHZ || + vht_oper->vht_op_info_chwidth == VHT_CHANWIDTH_160MHZ || + vht_oper->vht_op_info_chwidth == VHT_CHANWIDTH_80P80MHZ) + vht = vht_oper->vht_op_info_chwidth; + } + + if (ieee80211_freq_to_channel_ext(bss->freq, sec_chan, vht, &op_class, + &chan) == NUM_HOSTAPD_MODES) { + wpa_printf(MSG_DEBUG, + "WNM: Cannot determine operating class and channel"); + return -2; + } + + phy_type = ieee80211_get_phy_type(bss->freq, (ht_oper != NULL), + (vht_oper != NULL)); + if (phy_type == PHY_TYPE_UNSPECIFIED) { + wpa_printf(MSG_DEBUG, + "WNM: Cannot determine BSS phy type for Neighbor Report"); + return -2; + } + + info = wnm_get_bss_info(wpa_s, bss); + + return wnm_add_nei_rep(buf, len, bss->bssid, info, op_class, chan, + phy_type, pref); +} + + +static int wnm_add_cand_list(struct wpa_supplicant *wpa_s, u8 *buf, size_t len) +{ + u8 *pos = buf; + unsigned int i, pref = 255; + struct os_reltime now; + struct wpa_ssid *ssid = wpa_s->current_ssid; + + if (!ssid) + return 0; + + /* + * TODO: Define when scan results are no longer valid for the candidate + * list. + */ + os_get_reltime(&now); + if (os_reltime_expired(&now, &wpa_s->last_scan, 10)) + return 0; + + wpa_printf(MSG_DEBUG, + "WNM: Add candidate list to BSS Transition Management Response frame"); + for (i = 0; i < wpa_s->last_scan_res_used && pref; i++) { + struct wpa_bss *bss = wpa_s->last_scan_res[i]; + int res; + + if (wpa_scan_res_match(wpa_s, i, bss, ssid, 1)) { + res = wnm_nei_rep_add_bss(wpa_s, bss, pos, len, pref--); + if (res == -2) + continue; /* could not build entry for BSS */ + if (res < 0) + break; /* no more room for candidates */ + if (pref == 1) + break; + + pos += res; + len -= res; + } + } + + wpa_hexdump(MSG_DEBUG, + "WNM: BSS Transition Management Response candidate list", + buf, pos - buf); + + return pos - buf; +} + + static void wnm_send_bss_transition_mgmt_resp( struct wpa_supplicant *wpa_s, u8 dialog_token, enum bss_trans_mgmt_status_code status, u8 delay, const u8 *target_bssid) { - u8 buf[1000], *pos; + u8 buf[2000], *pos; struct ieee80211_mgmt *mgmt; size_t len; int res; @@ -597,6 +825,17 @@ static void wnm_send_bss_transition_mgmt_resp( pos += ETH_ALEN; } + if (status == WNM_BSS_TM_ACCEPT) + pos += wnm_add_cand_list(wpa_s, pos, buf + sizeof(buf) - pos); + +#ifdef CONFIG_MBO + if (status != WNM_BSS_TM_ACCEPT) { + pos += wpas_mbo_ie_bss_trans_reject( + wpa_s, pos, buf + sizeof(buf) - pos, + MBO_TRANSITION_REJECT_REASON_UNSPECIFIED); + } +#endif /* CONFIG_MBO */ + len = pos - (u8 *) &mgmt->u.action.category; res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, @@ -609,6 +848,41 @@ static void wnm_send_bss_transition_mgmt_resp( } +static void wnm_bss_tm_connect(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss, struct wpa_ssid *ssid, + int after_new_scan) +{ + wpa_dbg(wpa_s, MSG_DEBUG, + "WNM: Transition to BSS " MACSTR + " based on BSS Transition Management Request (old BSSID " + MACSTR " after_new_scan=%d)", + MAC2STR(bss->bssid), MAC2STR(wpa_s->bssid), after_new_scan); + + /* Send the BSS Management Response - Accept */ + if (wpa_s->wnm_reply) { + wpa_s->wnm_reply = 0; + wpa_printf(MSG_DEBUG, + "WNM: Sending successful BSS Transition Management Response"); + wnm_send_bss_transition_mgmt_resp(wpa_s, + wpa_s->wnm_dialog_token, + WNM_BSS_TM_ACCEPT, + 0, bss->bssid); + } + + if (bss == wpa_s->current_bss) { + wpa_printf(MSG_DEBUG, + "WNM: Already associated with the preferred candidate"); + wnm_deallocate_memory(wpa_s); + return; + } + + wpa_s->reassociate = 1; + wpa_printf(MSG_DEBUG, "WNM: Issuing connect"); + wpa_supplicant_connect(wpa_s, bss, ssid); + wnm_deallocate_memory(wpa_s); +} + + int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail) { struct wpa_bss *bss; @@ -618,6 +892,8 @@ int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail) if (!wpa_s->wnm_neighbor_report_elements) return 0; + wpa_dbg(wpa_s, MSG_DEBUG, + "WNM: Process scan results for BSS Transition Management"); if (os_reltime_before(&wpa_s->wnm_cand_valid_until, &wpa_s->scan_trigger_time)) { wpa_printf(MSG_DEBUG, "WNM: Previously stored BSS transition candidate list is not valid anymore - drop it"); @@ -633,7 +909,7 @@ int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail) } /* Compare the Neighbor Report and scan results */ - bss = compare_scan_neighbor_results(wpa_s); + bss = compare_scan_neighbor_results(wpa_s, 0); if (!bss) { wpa_printf(MSG_DEBUG, "WNM: No BSS transition candidate match found"); status = WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES; @@ -641,24 +917,7 @@ int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail) } /* Associate to the network */ - /* Send the BSS Management Response - Accept */ - if (wpa_s->wnm_reply) { - wpa_s->wnm_reply = 0; - wnm_send_bss_transition_mgmt_resp(wpa_s, - wpa_s->wnm_dialog_token, - WNM_BSS_TM_ACCEPT, - 0, bss->bssid); - } - - if (bss == wpa_s->current_bss) { - wpa_printf(MSG_DEBUG, - "WNM: Already associated with the preferred candidate"); - return 1; - } - - wpa_s->reassociate = 1; - wpa_supplicant_connect(wpa_s, bss, ssid); - wnm_deallocate_memory(wpa_s); + wnm_bss_tm_connect(wpa_s, bss, ssid, 1); return 1; send_bss_resp_fail: @@ -799,14 +1058,90 @@ static void wnm_set_scan_freqs(struct wpa_supplicant *wpa_s) } +static int wnm_fetch_scan_results(struct wpa_supplicant *wpa_s) +{ + struct wpa_scan_results *scan_res; + struct wpa_bss *bss; + struct wpa_ssid *ssid = wpa_s->current_ssid; + u8 i, found = 0; + size_t j; + + wpa_dbg(wpa_s, MSG_DEBUG, + "WNM: Fetch current scan results from the driver for checking transition candidates"); + scan_res = wpa_drv_get_scan_results2(wpa_s); + if (!scan_res) { + wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Failed to get scan results"); + return 0; + } + + if (scan_res->fetch_time.sec == 0) + os_get_reltime(&scan_res->fetch_time); + + filter_scan_res(wpa_s, scan_res); + + for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { + struct neighbor_report *nei; + + nei = &wpa_s->wnm_neighbor_report_elements[i]; + if (nei->preference_present && nei->preference == 0) + continue; + + for (j = 0; j < scan_res->num; j++) { + struct wpa_scan_res *res; + const u8 *ssid_ie; + + res = scan_res->res[j]; + if (os_memcmp(nei->bssid, res->bssid, ETH_ALEN) != 0 || + res->age > WNM_SCAN_RESULT_AGE * 1000) + continue; + bss = wpa_s->current_bss; + ssid_ie = wpa_scan_get_ie(res, WLAN_EID_SSID); + if (bss && ssid_ie && + (bss->ssid_len != ssid_ie[1] || + os_memcmp(bss->ssid, ssid_ie + 2, + bss->ssid_len) != 0)) + continue; + + /* Potential candidate found */ + found = 1; + scan_snr(res); + scan_est_throughput(wpa_s, res); + wpa_bss_update_scan_res(wpa_s, res, + &scan_res->fetch_time); + } + } + + wpa_scan_results_free(scan_res); + if (!found) { + wpa_dbg(wpa_s, MSG_DEBUG, + "WNM: No transition candidate matches existing scan results"); + return 0; + } + + bss = compare_scan_neighbor_results(wpa_s, WNM_SCAN_RESULT_AGE); + if (!bss) { + wpa_dbg(wpa_s, MSG_DEBUG, + "WNM: Comparison of scan results against transition candidates did not find matches"); + return 0; + } + + /* Associate to the network */ + wnm_bss_tm_connect(wpa_s, bss, ssid, 0); + return 1; +} + + static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, const u8 *pos, const u8 *end, int reply) { unsigned int beacon_int; u8 valid_int; +#ifdef CONFIG_MBO + const u8 *vendor; +#endif /* CONFIG_MBO */ - if (pos + 5 > end) + if (end - pos < 5) return; if (wpa_s->current_bss) @@ -826,10 +1161,23 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, wpa_s->wnm_dialog_token, wpa_s->wnm_mode, wpa_s->wnm_dissoc_timer, valid_int); +#if defined(CONFIG_MBO) && defined(CONFIG_TESTING_OPTIONS) + if (wpa_s->reject_btm_req_reason) { + wpa_printf(MSG_INFO, + "WNM: Testing - reject BSS Transition Management Request: reject_btm_req_reason=%d", + wpa_s->reject_btm_req_reason); + wnm_send_bss_transition_mgmt_resp(wpa_s, + wpa_s->wnm_dialog_token, + wpa_s->reject_btm_req_reason, + 0, NULL); + return; + } +#endif /* CONFIG_MBO && CONFIG_TESTING_OPTIONS */ + pos += 5; if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) { - if (pos + 12 > end) { + if (end - pos < 12) { wpa_printf(MSG_DEBUG, "WNM: Too short BSS TM Request"); return; } @@ -840,7 +1188,7 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) { char url[256]; - if (pos + 1 > end || pos + 1 + pos[0] > end) { + if (end - pos < 1 || 1 + pos[0] > end - pos) { wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition " "Management Request (URL)"); return; @@ -865,6 +1213,12 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, } } +#ifdef CONFIG_MBO + vendor = get_ie(pos, end - pos, WLAN_EID_VENDOR_SPECIFIC); + if (vendor) + wpas_mbo_ie_trans_req(wpa_s, vendor + 2, vendor[1]); +#endif /* CONFIG_MBO */ + if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) { unsigned int valid_ms; @@ -876,7 +1230,7 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, if (wpa_s->wnm_neighbor_report_elements == NULL) return; - while (pos + 2 <= end && + while (end - pos >= 2 && wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT) { u8 tag = *pos++; @@ -884,7 +1238,7 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, wpa_printf(MSG_DEBUG, "WNM: Neighbor report tag %u", tag); - if (pos + len > end) { + if (len > end - pos) { wpa_printf(MSG_DEBUG, "WNM: Truncated request"); return; } @@ -893,11 +1247,22 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, rep = &wpa_s->wnm_neighbor_report_elements[ wpa_s->wnm_num_neighbor_report]; wnm_parse_neighbor_report(wpa_s, pos, len, rep); + wpa_s->wnm_num_neighbor_report++; } pos += len; - wpa_s->wnm_num_neighbor_report++; } + + if (!wpa_s->wnm_num_neighbor_report) { + wpa_printf(MSG_DEBUG, + "WNM: Candidate list included bit is set, but no candidates found"); + wnm_send_bss_transition_mgmt_resp( + wpa_s, wpa_s->wnm_dialog_token, + WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES, + 0, NULL); + return; + } + wnm_sort_cand_list(wpa_s); wnm_dump_cand_list(wpa_s); valid_ms = valid_int * beacon_int * 128 / 125; @@ -911,6 +1276,20 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, wpa_s->wnm_cand_valid_until.usec %= 1000000; os_memcpy(wpa_s->wnm_cand_from_bss, wpa_s->bssid, ETH_ALEN); + /* + * Fetch the latest scan results from the kernel and check for + * candidates based on those results first. This can help in + * finding more up-to-date information should the driver has + * done some internal scanning operations after the last scan + * result update in wpa_supplicant. + */ + if (wnm_fetch_scan_results(wpa_s) > 0) + return; + + /* + * Try to use previously received scan results, if they are + * recent enough to use for a connection. + */ if (wpa_s->last_scan_res_used > 0) { struct os_reltime now; @@ -926,6 +1305,14 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, } wnm_set_scan_freqs(wpa_s); + if (wpa_s->wnm_num_neighbor_report == 1) { + os_memcpy(wpa_s->next_scan_bssid, + wpa_s->wnm_neighbor_report_elements[0].bssid, + ETH_ALEN); + wpa_printf(MSG_DEBUG, + "WNM: Scan only for a specific BSSID since there is only a single candidate " + MACSTR, MAC2STR(wpa_s->next_scan_bssid)); + } wpa_supplicant_req_scan(wpa_s, 0, 0); } else if (reply) { enum bss_trans_mgmt_status_code status; @@ -943,16 +1330,17 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s, - u8 query_reason) + u8 query_reason, int cand_list) { - u8 buf[1000], *pos; + u8 buf[2000], *pos; struct ieee80211_mgmt *mgmt; size_t len; int ret; wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Query to " - MACSTR " query_reason=%u", - MAC2STR(wpa_s->bssid), query_reason); + MACSTR " query_reason=%u%s", + MAC2STR(wpa_s->bssid), query_reason, + cand_list ? " candidate list" : ""); mgmt = (struct ieee80211_mgmt *) buf; os_memset(&buf, 0, sizeof(buf)); @@ -967,6 +1355,9 @@ int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s, mgmt->u.action.u.bss_tm_query.query_reason = query_reason; pos = mgmt->u.action.u.bss_tm_query.variable; + if (cand_list) + pos += wnm_add_cand_list(wpa_s, pos, buf + sizeof(buf) - pos); + len = pos - (u8 *) &mgmt->u.action.category; ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, @@ -987,7 +1378,7 @@ static void ieee802_11_rx_wnm_notif_req_wfa(struct wpa_supplicant *wpa_s, pos = data; end = data + len; - while (pos + 1 < end) { + while (end - pos > 1) { ie = *pos++; ie_len = *pos++; wpa_printf(MSG_DEBUG, "WNM: WFA subelement %u len %u", @@ -1025,7 +1416,7 @@ static void ieee802_11_rx_wnm_notif_req_wfa(struct wpa_supplicant *wpa_s, url = NULL; osu_method = 1; } else { - if (pos + url_len + 1 > ie_end) { + if (url_len + 1 > ie_end - pos) { wpa_printf(MSG_DEBUG, "WNM: Not enough room for Server URL (len=%u) and Server Method (left %d)", url_len, (int) (ie_end - pos)); @@ -1064,7 +1455,7 @@ static void ieee802_11_rx_wnm_notif_req_wfa(struct wpa_supplicant *wpa_s, "Imminent - Reason Code %u " "Re-Auth Delay %u URL Length %u", code, reauth_delay, url_len); - if (pos + url_len > ie_end) + if (url_len > ie_end - pos) break; url = os_malloc(url_len + 1); if (url == NULL) diff --git a/contrib/wpa/wpa_supplicant/wnm_sta.h b/contrib/wpa/wpa_supplicant/wnm_sta.h index 8de434807f19..81d815359634 100644 --- a/contrib/wpa/wpa_supplicant/wnm_sta.h +++ b/contrib/wpa/wpa_supplicant/wnm_sta.h @@ -56,7 +56,7 @@ void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s, const struct ieee80211_mgmt *mgmt, size_t len); int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s, - u8 query_reason); + u8 query_reason, int cand_list); void wnm_deallocate_memory(struct wpa_supplicant *wpa_s); diff --git a/contrib/wpa/wpa_supplicant/wpa_cli.c b/contrib/wpa/wpa_supplicant/wpa_cli.c index 7ddae3d3b6b8..a848b7737db5 100644 --- a/contrib/wpa/wpa_supplicant/wpa_cli.c +++ b/contrib/wpa/wpa_supplicant/wpa_cli.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - command line interface for wpa_supplicant daemon - * Copyright (c) 2004-2015, Jouni Malinen + * Copyright (c) 2004-2016, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -14,6 +14,7 @@ #include #endif /* CONFIG_CTRL_IFACE_UNIX */ +#include "common/cli.h" #include "common/wpa_ctrl.h" #include "utils/common.h" #include "utils/eloop.h" @@ -28,43 +29,13 @@ static const char *const wpa_cli_version = "wpa_cli v" VERSION_STR "\n" -"Copyright (c) 2004-2015, Jouni Malinen and contributors"; +"Copyright (c) 2004-2016, Jouni Malinen and contributors"; - -static const char *const wpa_cli_license = -"This software may be distributed under the terms of the BSD license.\n" -"See README for more details.\n"; - -static const char *const wpa_cli_full_license = -"This software may be distributed under the terms of the BSD license.\n" -"\n" -"Redistribution and use in source and binary forms, with or without\n" -"modification, are permitted provided that the following conditions are\n" -"met:\n" -"\n" -"1. Redistributions of source code must retain the above copyright\n" -" notice, this list of conditions and the following disclaimer.\n" -"\n" -"2. Redistributions in binary form must reproduce the above copyright\n" -" notice, this list of conditions and the following disclaimer in the\n" -" documentation and/or other materials provided with the distribution.\n" -"\n" -"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n" -" names of its contributors may be used to endorse or promote products\n" -" derived from this software without specific prior written permission.\n" -"\n" -"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n" -"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n" -"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n" -"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n" -"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n" -"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n" -"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n" -"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n" -"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n" -"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n" -"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" -"\n"; +#define VENDOR_ELEM_FRAME_ID \ + " 0: Probe Req (P2P), 1: Probe Resp (P2P) , 2: Probe Resp (GO), " \ + "3: Beacon (GO), 4: PD Req, 5: PD Resp, 6: GO Neg Req, " \ + "7: GO Neg Resp, 8: GO Neg Conf, 9: Inv Req, 10: Inv Resp, " \ + "11: Assoc Req (P2P), 12: Assoc Resp (P2P)" static struct wpa_ctrl *ctrl_conn; static struct wpa_ctrl *mon_conn; @@ -84,11 +55,6 @@ static int ping_interval = 5; static int interactive = 0; static char *ifname_prefix = NULL; -struct cli_txt_entry { - struct dl_list list; - char *txt; -}; - static DEFINE_DL_LIST(bsses); /* struct cli_txt_entry */ static DEFINE_DL_LIST(p2p_peers); /* struct cli_txt_entry */ static DEFINE_DL_LIST(p2p_groups); /* struct cli_txt_entry */ @@ -124,168 +90,6 @@ static void usage(void) } -static void cli_txt_list_free(struct cli_txt_entry *e) -{ - dl_list_del(&e->list); - os_free(e->txt); - os_free(e); -} - - -static void cli_txt_list_flush(struct dl_list *list) -{ - struct cli_txt_entry *e; - while ((e = dl_list_first(list, struct cli_txt_entry, list))) - cli_txt_list_free(e); -} - - -static struct cli_txt_entry * cli_txt_list_get(struct dl_list *txt_list, - const char *txt) -{ - struct cli_txt_entry *e; - dl_list_for_each(e, txt_list, struct cli_txt_entry, list) { - if (os_strcmp(e->txt, txt) == 0) - return e; - } - return NULL; -} - - -static void cli_txt_list_del(struct dl_list *txt_list, const char *txt) -{ - struct cli_txt_entry *e; - e = cli_txt_list_get(txt_list, txt); - if (e) - cli_txt_list_free(e); -} - - -static void cli_txt_list_del_addr(struct dl_list *txt_list, const char *txt) -{ - u8 addr[ETH_ALEN]; - char buf[18]; - if (hwaddr_aton(txt, addr) < 0) - return; - os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr)); - cli_txt_list_del(txt_list, buf); -} - - -#ifdef CONFIG_P2P -static void cli_txt_list_del_word(struct dl_list *txt_list, const char *txt, - int separator) -{ - const char *end; - char *buf; - end = os_strchr(txt, separator); - if (end == NULL) - end = txt + os_strlen(txt); - buf = dup_binstr(txt, end - txt); - if (buf == NULL) - return; - cli_txt_list_del(txt_list, buf); - os_free(buf); -} -#endif /* CONFIG_P2P */ - - -static int cli_txt_list_add(struct dl_list *txt_list, const char *txt) -{ - struct cli_txt_entry *e; - e = cli_txt_list_get(txt_list, txt); - if (e) - return 0; - e = os_zalloc(sizeof(*e)); - if (e == NULL) - return -1; - e->txt = os_strdup(txt); - if (e->txt == NULL) { - os_free(e); - return -1; - } - dl_list_add(txt_list, &e->list); - return 0; -} - - -#ifdef CONFIG_P2P -static int cli_txt_list_add_addr(struct dl_list *txt_list, const char *txt) -{ - u8 addr[ETH_ALEN]; - char buf[18]; - if (hwaddr_aton(txt, addr) < 0) - return -1; - os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr)); - return cli_txt_list_add(txt_list, buf); -} -#endif /* CONFIG_P2P */ - - -static int cli_txt_list_add_word(struct dl_list *txt_list, const char *txt, - int separator) -{ - const char *end; - char *buf; - int ret; - end = os_strchr(txt, separator); - if (end == NULL) - end = txt + os_strlen(txt); - buf = dup_binstr(txt, end - txt); - if (buf == NULL) - return -1; - ret = cli_txt_list_add(txt_list, buf); - os_free(buf); - return ret; -} - - -static char ** cli_txt_list_array(struct dl_list *txt_list) -{ - unsigned int i, count = dl_list_len(txt_list); - char **res; - struct cli_txt_entry *e; - - res = os_calloc(count + 1, sizeof(char *)); - if (res == NULL) - return NULL; - - i = 0; - dl_list_for_each(e, txt_list, struct cli_txt_entry, list) { - res[i] = os_strdup(e->txt); - if (res[i] == NULL) - break; - i++; - } - - return res; -} - - -static int get_cmd_arg_num(const char *str, int pos) -{ - int arg = 0, i; - - for (i = 0; i <= pos; i++) { - if (str[i] != ' ') { - arg++; - while (i <= pos && str[i] != ' ') - i++; - } - } - - if (arg > 0) - arg--; - return arg; -} - - -static int str_starts(const char *src, const char *match) -{ - return os_strncmp(src, match, os_strlen(match)) == 0; -} - - static int wpa_cli_show_event(const char *event) { const char *start; @@ -452,36 +256,6 @@ static int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd) } -static int write_cmd(char *buf, size_t buflen, const char *cmd, int argc, - char *argv[]) -{ - int i, res; - char *pos, *end; - - pos = buf; - end = buf + buflen; - - res = os_snprintf(pos, end - pos, "%s", cmd); - if (os_snprintf_error(end - pos, res)) - goto fail; - pos += res; - - for (i = 0; i < argc; i++) { - res = os_snprintf(pos, end - pos, " %s", argv[i]); - if (os_snprintf_error(end - pos, res)) - goto fail; - pos += res; - } - - buf[buflen - 1] = '\0'; - return 0; - -fail: - printf("Too long command\n"); - return -1; -} - - static int wpa_cli_cmd(struct wpa_ctrl *ctrl, const char *cmd, int min_args, int argc, char *argv[]) { @@ -581,7 +355,7 @@ static char ** wpa_cli_complete_help(const char *str, int pos) static int wpa_cli_cmd_license(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - printf("%s\n\n%s\n", wpa_cli_version, wpa_cli_full_license); + printf("%s\n\n%s\n", wpa_cli_version, cli_full_license); return 0; } @@ -677,7 +451,10 @@ static char ** wpa_cli_complete_set(const char *str, int pos) "tdls_external_control", "osu_dir", "wowlan_triggers", "p2p_search_delay", "mac_addr", "rand_addr_lifetime", "preassoc_mac_addr", "key_mgmt_offload", "passive_scan", - "reassoc_same_bss_optim", "wps_priority" + "reassoc_same_bss_optim", "wps_priority", +#ifdef CONFIG_TESTING_OPTIONS + "ignore_auth_resp", +#endif /* CONFIG_TESTING_OPTIONS */ }; int i, num_fields = ARRAY_SIZE(fields); @@ -705,6 +482,13 @@ static int wpa_cli_cmd_dump(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +static int wpa_cli_cmd_driver_flags(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "DRIVER_FLAGS"); +} + + static int wpa_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[]) { return wpa_cli_cmd(ctrl, "GET", 1, argc, argv); @@ -1548,7 +1332,7 @@ static const char *network_fields[] = { "ssid", "scan_ssid", "bssid", "bssid_blacklist", "bssid_whitelist", "psk", "proto", "key_mgmt", "bg_scan_period", "pairwise", "group", "auth_alg", "scan_freq", - "freq_list", + "freq_list", "max_oper_chwidth", #ifdef IEEE8021X_EAPOL "eap", "identity", "anonymous_identity", "password", "ca_cert", "ca_path", "client_cert", "private_key", "private_key_passwd", @@ -1606,7 +1390,7 @@ static const char *network_fields[] = { #ifdef CONFIG_HS20 "update_identifier", #endif /* CONFIG_HS20 */ - "mac_addr" + "mac_addr", "pbss", "wps_disabled" }; @@ -1764,6 +1548,13 @@ static int wpa_cli_cmd_scan_results(struct wpa_ctrl *ctrl, int argc, } +static int wpa_cli_cmd_abort_scan(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "ABORT_SCAN"); +} + + static int wpa_cli_cmd_bss(struct wpa_ctrl *ctrl, int argc, char *argv[]) { return wpa_cli_cmd(ctrl, "BSS", 1, argc, argv); @@ -1804,6 +1595,48 @@ static int wpa_cli_cmd_get_capability(struct wpa_ctrl *ctrl, int argc, } +static char ** wpa_cli_complete_get_capability(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + const char *fields[] = { + "eap", "pairwise", "group", "group_mgmt", "key_mgmt", + "proto", "auth_alg", "modes", "channels", "freq", +#ifdef CONFIG_TDLS + "tdls", +#endif /* CONFIG_TDLS */ +#ifdef CONFIG_ERP + "erp", +#endif /* CONFIG_ERP */ +#ifdef CONFIG_FIPS + "fips", +#endif /* CONFIG_FIPS */ +#ifdef CONFIG_ACS + "acs", +#endif /* CONFIG_ACS */ + }; + int i, num_fields = ARRAY_SIZE(fields); + char **res = NULL; + + if (arg == 1) { + res = os_calloc(num_fields + 1, sizeof(char *)); + if (res == NULL) + return NULL; + for (i = 0; i < num_fields; i++) { + res[i] = os_strdup(fields[i]); + if (res[i] == NULL) + return res; + } + } + if (arg == 2) { + res = os_calloc(1 + 1, sizeof(char *)); + if (res == NULL) + return NULL; + res[0] = os_strdup("strict"); + } + return res; +} + + static int wpa_cli_list_interfaces(struct wpa_ctrl *ctrl) { printf("Available interfaces:\n"); @@ -1866,14 +1699,15 @@ static int wpa_cli_cmd_interface_add(struct wpa_ctrl *ctrl, int argc, /* * INTERFACE_ADD TABTABTABTAB - * TAB[TAB] + * TAB[TAB[TAB]] */ res = os_snprintf(cmd, sizeof(cmd), - "INTERFACE_ADD %s\t%s\t%s\t%s\t%s\t%s\t%s", + "INTERFACE_ADD %s\t%s\t%s\t%s\t%s\t%s\t%s\t%s", argv[0], argc > 1 ? argv[1] : "", argc > 2 ? argv[2] : "", argc > 3 ? argv[3] : "", argc > 4 ? argv[4] : "", - argc > 5 ? argv[5] : "", argc > 6 ? argv[6] : ""); + argc > 5 ? argv[5] : "", argc > 6 ? argv[6] : "", + argc > 7 ? argv[7] : ""); if (os_snprintf_error(sizeof(cmd), res)) return -1; cmd[sizeof(cmd) - 1] = '\0'; @@ -1913,6 +1747,12 @@ static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd, printf("Not connected to hostapd - command dropped.\n"); return -1; } + if (ifname_prefix) { + os_snprintf(buf, sizeof(buf), "IFNAME=%s %s", + ifname_prefix, cmd); + buf[sizeof(buf) - 1] = '\0'; + cmd = buf; + } len = sizeof(buf) - 1; ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len, wpa_cli_msg_cb); @@ -2022,6 +1862,20 @@ static int wpa_cli_cmd_mesh_group_remove(struct wpa_ctrl *ctrl, int argc, return wpa_cli_cmd(ctrl, "MESH_GROUP_REMOVE", 1, argc, argv); } + +static int wpa_cli_cmd_mesh_peer_remove(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "MESH_PEER_REMOVE", 1, argc, argv); +} + + +static int wpa_cli_cmd_mesh_peer_add(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "MESH_PEER_ADD", 1, argc, argv); +} + #endif /* CONFIG_MESH */ @@ -2141,6 +1995,13 @@ static int wpa_cli_cmd_p2p_group_add(struct wpa_ctrl *ctrl, int argc, } +static int wpa_cli_cmd_p2p_group_member(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "P2P_GROUP_MEMBER", 1, argc, argv); +} + + static int wpa_cli_cmd_p2p_prov_disc(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -2477,6 +2338,27 @@ static int wpa_cli_cmd_p2p_remove_client(struct wpa_ctrl *ctrl, int argc, return wpa_cli_cmd(ctrl, "P2P_REMOVE_CLIENT", 1, argc, argv); } + +static int wpa_cli_cmd_vendor_elem_add(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "VENDOR_ELEM_ADD", 2, argc, argv); +} + + +static int wpa_cli_cmd_vendor_elem_get(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "VENDOR_ELEM_GET", 1, argc, argv); +} + + +static int wpa_cli_cmd_vendor_elem_remove(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "VENDOR_ELEM_REMOVE", 2, argc, argv); +} + #endif /* CONFIG_P2P */ #ifdef CONFIG_WIFI_DISPLAY @@ -2719,6 +2601,13 @@ static int wpa_cli_cmd_signal_poll(struct wpa_ctrl *ctrl, int argc, } +static int wpa_cli_cmd_signal_monitor(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "SIGNAL_MONITOR", 0, argc, argv); +} + + static int wpa_cli_cmd_pktcnt_poll(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -2823,6 +2712,20 @@ static int wpa_cli_cmd_get_pref_freq_list(struct wpa_ctrl *ctrl, int argc, } +static int wpa_cli_cmd_p2p_lo_start(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "P2P_LO_START", 4, argc, argv); +} + + +static int wpa_cli_cmd_p2p_lo_stop(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "P2P_LO_STOP", 0, argc, argv); +} + + enum wpa_cli_cmd_flags { cli_cmd_flag_none = 0x00, cli_cmd_flag_sensitive = 0x01 @@ -2880,6 +2783,9 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { { "get", wpa_cli_cmd_get, wpa_cli_complete_get, cli_cmd_flag_none, " = get information" }, + { "driver_flags", wpa_cli_cmd_driver_flags, NULL, + cli_cmd_flag_none, + "= list driver flags" }, { "logon", wpa_cli_cmd_logon, NULL, cli_cmd_flag_none, "= IEEE 802.1X EAPOL state machine logon" }, @@ -3001,11 +2907,14 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { { "scan_results", wpa_cli_cmd_scan_results, NULL, cli_cmd_flag_none, "= get latest scan results" }, + { "abort_scan", wpa_cli_cmd_abort_scan, NULL, + cli_cmd_flag_none, + "= request ongoing scan to be aborted" }, { "bss", wpa_cli_cmd_bss, wpa_cli_complete_bss, cli_cmd_flag_none, "< | > = get detailed scan result info" }, - { "get_capability", wpa_cli_cmd_get_capability, NULL, - cli_cmd_flag_none, + { "get_capability", wpa_cli_cmd_get_capability, + wpa_cli_complete_get_capability, cli_cmd_flag_none, " " "= get capabilities" }, { "reconfigure", wpa_cli_cmd_reconfigure, NULL, @@ -3017,8 +2926,10 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { { "interface_add", wpa_cli_cmd_interface_add, NULL, cli_cmd_flag_none, " \n" - " = adds new interface, all parameters but \n" - " are optional" }, + " = adds new interface, all " + "parameters but\n" + " are optional. Supported types are station ('sta') and " + "AP ('ap')" }, { "interface_remove", wpa_cli_cmd_interface_remove, NULL, cli_cmd_flag_none, " = removes the interface" }, @@ -3157,6 +3068,12 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { { "mesh_group_remove", wpa_cli_cmd_mesh_group_remove, NULL, cli_cmd_flag_none, " = Remove mesh group interface" }, + { "mesh_peer_remove", wpa_cli_cmd_mesh_peer_remove, NULL, + cli_cmd_flag_none, + " = Remove a mesh peer" }, + { "mesh_peer_add", wpa_cli_cmd_mesh_peer_add, NULL, + cli_cmd_flag_none, + " [duration=] = Add a mesh peer" }, #endif /* CONFIG_MESH */ #ifdef CONFIG_P2P { "p2p_find", wpa_cli_cmd_p2p_find, wpa_cli_complete_p2p_find, @@ -3180,6 +3097,9 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { " = remove P2P group interface (terminate group if GO)" }, { "p2p_group_add", wpa_cli_cmd_p2p_group_add, NULL, cli_cmd_flag_none, "[ht40] = add a new P2P group (local end as GO)" }, + { "p2p_group_member", wpa_cli_cmd_p2p_group_member, NULL, + cli_cmd_flag_none, + " = Get peer interface address on local GO using peer Device Address" }, { "p2p_prov_disc", wpa_cli_cmd_p2p_prov_disc, wpa_cli_complete_p2p_peer, cli_cmd_flag_none, " = request provisioning discovery" }, @@ -3248,6 +3168,18 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { { "p2p_remove_client", wpa_cli_cmd_p2p_remove_client, wpa_cli_complete_p2p_peer, cli_cmd_flag_none, " = remove a peer from all groups" }, + { "vendor_elem_add", wpa_cli_cmd_vendor_elem_add, NULL, + cli_cmd_flag_none, + " = add vendor specific IEs to frame(s)\n" + VENDOR_ELEM_FRAME_ID }, + { "vendor_elem_get", wpa_cli_cmd_vendor_elem_get, NULL, + cli_cmd_flag_none, + " = get vendor specific IE(s) to frame(s)\n" + VENDOR_ELEM_FRAME_ID }, + { "vendor_elem_remove", wpa_cli_cmd_vendor_elem_remove, NULL, + cli_cmd_flag_none, + " = remove vendor specific IE(s) in frame(s)\n" + VENDOR_ELEM_FRAME_ID }, #endif /* CONFIG_P2P */ #ifdef CONFIG_WIFI_DISPLAY { "wfd_subelem_set", wpa_cli_cmd_wfd_subelem_set, NULL, @@ -3336,6 +3268,9 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { { "signal_poll", wpa_cli_cmd_signal_poll, NULL, cli_cmd_flag_none, "= get signal parameters" }, + { "signal_monitor", wpa_cli_cmd_signal_monitor, NULL, + cli_cmd_flag_none, + "= set signal monitor parameters" }, { "pktcnt_poll", wpa_cli_cmd_pktcnt_poll, NULL, cli_cmd_flag_none, "= get TX/RX packet counters" }, @@ -3350,7 +3285,7 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { { "wnm_sleep", wpa_cli_cmd_wnm_sleep, NULL, cli_cmd_flag_none, " [interval=#] = enter/exit WNM-Sleep mode" }, { "wnm_bss_query", wpa_cli_cmd_wnm_bss_query, NULL, cli_cmd_flag_none, - " = Send BSS Transition Management Query" }, + " [list] = Send BSS Transition Management Query" }, #endif /* CONFIG_WNM */ { "raw", wpa_cli_cmd_raw, NULL, cli_cmd_flag_sensitive, " = Sent unprocessed command" }, @@ -3367,8 +3302,7 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { }, { "neighbor_rep_request", wpa_cli_cmd_neighbor_rep_request, NULL, cli_cmd_flag_none, - "[ssid=] = Trigger request to AP for neighboring AP report " - "(with optional given SSID, default: current SSID)" + "[ssid=] [lci] [civic] = Trigger request to AP for neighboring AP report (with optional given SSID in hex or enclosed in double quotes, default: current SSID; with optional LCI and location civic request)" }, { "erp_flush", wpa_cli_cmd_erp_flush, NULL, cli_cmd_flag_none, "= flush ERP keys" }, @@ -3380,6 +3314,12 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { { "get_pref_freq_list", wpa_cli_cmd_get_pref_freq_list, NULL, cli_cmd_flag_none, " = retrieve preferred freq list for the specified interface type" }, + { "p2p_lo_start", wpa_cli_cmd_p2p_lo_start, NULL, + cli_cmd_flag_none, + " = start P2P listen offload" }, + { "p2p_lo_stop", wpa_cli_cmd_p2p_lo_stop, NULL, + cli_cmd_flag_none, + "= stop P2P listen offload" }, { NULL, NULL, NULL, cli_cmd_flag_none, NULL } }; @@ -3578,12 +3518,6 @@ static int wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[]) } -static int str_match(const char *a, const char *b) -{ - return os_strncmp(a, b, os_strlen(b)) == 0; -} - - static int wpa_cli_exec(const char *program, const char *arg1, const char *arg2) { @@ -3591,6 +3525,10 @@ static int wpa_cli_exec(const char *program, const char *arg1, size_t len; int res; + /* If no interface is specified, set the global */ + if (!arg1) + arg1 = "global"; + len = os_strlen(arg1) + os_strlen(arg2) + 2; arg = os_malloc(len); if (arg == NULL) @@ -3635,7 +3573,7 @@ static void wpa_cli_action_process(const char *msg) pos = prev; } - if (str_match(pos, WPA_EVENT_CONNECTED)) { + if (str_starts(pos, WPA_EVENT_CONNECTED)) { int new_id = -1; os_unsetenv("WPA_ID"); os_unsetenv("WPA_ID_STR"); @@ -3671,44 +3609,48 @@ static void wpa_cli_action_process(const char *msg) wpa_cli_last_id = new_id; wpa_cli_exec(action_file, ifname, "CONNECTED"); } - } else if (str_match(pos, WPA_EVENT_DISCONNECTED)) { + } else if (str_starts(pos, WPA_EVENT_DISCONNECTED)) { if (wpa_cli_connected) { wpa_cli_connected = 0; wpa_cli_exec(action_file, ifname, "DISCONNECTED"); } - } else if (str_match(pos, MESH_GROUP_STARTED)) { + } else if (str_starts(pos, AP_EVENT_ENABLED)) { wpa_cli_exec(action_file, ctrl_ifname, pos); - } else if (str_match(pos, MESH_GROUP_REMOVED)) { + } else if (str_starts(pos, AP_EVENT_DISABLED)) { wpa_cli_exec(action_file, ctrl_ifname, pos); - } else if (str_match(pos, MESH_PEER_CONNECTED)) { + } else if (str_starts(pos, MESH_GROUP_STARTED)) { wpa_cli_exec(action_file, ctrl_ifname, pos); - } else if (str_match(pos, MESH_PEER_DISCONNECTED)) { + } else if (str_starts(pos, MESH_GROUP_REMOVED)) { wpa_cli_exec(action_file, ctrl_ifname, pos); - } else if (str_match(pos, P2P_EVENT_GROUP_STARTED)) { + } else if (str_starts(pos, MESH_PEER_CONNECTED)) { + wpa_cli_exec(action_file, ctrl_ifname, pos); + } else if (str_starts(pos, MESH_PEER_DISCONNECTED)) { + wpa_cli_exec(action_file, ctrl_ifname, pos); + } else if (str_starts(pos, P2P_EVENT_GROUP_STARTED)) { wpa_cli_exec(action_file, ifname, pos); - } else if (str_match(pos, P2P_EVENT_GROUP_REMOVED)) { + } else if (str_starts(pos, P2P_EVENT_GROUP_REMOVED)) { wpa_cli_exec(action_file, ifname, pos); - } else if (str_match(pos, P2P_EVENT_CROSS_CONNECT_ENABLE)) { + } else if (str_starts(pos, P2P_EVENT_CROSS_CONNECT_ENABLE)) { wpa_cli_exec(action_file, ifname, pos); - } else if (str_match(pos, P2P_EVENT_CROSS_CONNECT_DISABLE)) { + } else if (str_starts(pos, P2P_EVENT_CROSS_CONNECT_DISABLE)) { wpa_cli_exec(action_file, ifname, pos); - } else if (str_match(pos, P2P_EVENT_GO_NEG_FAILURE)) { + } else if (str_starts(pos, P2P_EVENT_GO_NEG_FAILURE)) { wpa_cli_exec(action_file, ifname, pos); - } else if (str_match(pos, WPS_EVENT_SUCCESS)) { + } else if (str_starts(pos, WPS_EVENT_SUCCESS)) { wpa_cli_exec(action_file, ifname, pos); - } else if (str_match(pos, WPS_EVENT_FAIL)) { + } else if (str_starts(pos, WPS_EVENT_FAIL)) { wpa_cli_exec(action_file, ifname, pos); - } else if (str_match(pos, AP_STA_CONNECTED)) { + } else if (str_starts(pos, AP_STA_CONNECTED)) { wpa_cli_exec(action_file, ifname, pos); - } else if (str_match(pos, AP_STA_DISCONNECTED)) { + } else if (str_starts(pos, AP_STA_DISCONNECTED)) { wpa_cli_exec(action_file, ifname, pos); - } else if (str_match(pos, ESS_DISASSOC_IMMINENT)) { + } else if (str_starts(pos, ESS_DISASSOC_IMMINENT)) { wpa_cli_exec(action_file, ifname, pos); - } else if (str_match(pos, HS20_SUBSCRIPTION_REMEDIATION)) { + } else if (str_starts(pos, HS20_SUBSCRIPTION_REMEDIATION)) { wpa_cli_exec(action_file, ifname, pos); - } else if (str_match(pos, HS20_DEAUTH_IMMINENT_NOTICE)) { + } else if (str_starts(pos, HS20_DEAUTH_IMMINENT_NOTICE)) { wpa_cli_exec(action_file, ifname, pos); - } else if (str_match(pos, WPA_EVENT_TERMINATING)) { + } else if (str_starts(pos, WPA_EVENT_TERMINATING)) { printf("wpa_supplicant is terminating - stop monitoring\n"); wpa_cli_quit = 1; } @@ -3818,7 +3760,7 @@ static int check_terminating(const char *msg) pos = msg; } - if (str_match(pos, WPA_EVENT_TERMINATING) && ctrl_conn) { + if (str_starts(pos, WPA_EVENT_TERMINATING) && ctrl_conn) { edit_clear_line(); printf("\rConnection to wpa_supplicant lost - trying to " "reconnect\n"); @@ -3869,37 +3811,6 @@ static void wpa_cli_recv_pending(struct wpa_ctrl *ctrl, int action_monitor) } } -#define max_args 10 - -static int tokenize_cmd(char *cmd, char *argv[]) -{ - char *pos; - int argc = 0; - - pos = cmd; - for (;;) { - while (*pos == ' ') - pos++; - if (*pos == '\0') - break; - argv[argc] = pos; - argc++; - if (argc == max_args) - break; - if (*pos == '"') { - char *pos2 = os_strrchr(pos, '"'); - if (pos2) - pos = pos2 + 1; - } - while (*pos != '\0' && *pos != ' ') - pos++; - if (*pos == ' ') - *pos++ = '\0'; - } - - return argc; -} - static void wpa_cli_ping(void *eloop_ctx, void *timeout_ctx) { @@ -4084,7 +3995,7 @@ static void try_connection(void *eloop_ctx, void *timeout_ctx) if (ctrl_ifname == NULL) ctrl_ifname = wpa_cli_get_default_ifname(); - if (!wpa_cli_open_connection(ctrl_ifname, 1) == 0) { + if (wpa_cli_open_connection(ctrl_ifname, 1)) { if (!warning_displayed) { printf("Could not connect to wpa_supplicant: " "%s - re-trying\n", @@ -4309,7 +4220,7 @@ int main(int argc, char *argv[]) interactive = (argc == optind) && (action_file == NULL); if (interactive) - printf("%s\n\n%s\n\n", wpa_cli_version, wpa_cli_license); + printf("%s\n\n%s\n\n", wpa_cli_version, cli_license); if (eloop_init()) return -1; @@ -4373,7 +4284,7 @@ int main(int argc, char *argv[]) } } - if (daemonize && os_daemonize(pid_file)) + if (daemonize && os_daemonize(pid_file) && eloop_sock_requeue()) return -1; if (action_file) diff --git a/contrib/wpa/wpa_supplicant/wpa_priv.c b/contrib/wpa/wpa_supplicant/wpa_priv.c index 850ec405b42c..511df4f18148 100644 --- a/contrib/wpa/wpa_supplicant/wpa_priv.c +++ b/contrib/wpa/wpa_supplicant/wpa_priv.c @@ -29,6 +29,8 @@ struct wpa_priv_interface { char *sock_name; int fd; + void *ctx; + const struct wpa_driver_ops *driver; void *drv_priv; void *drv_global_priv; @@ -40,6 +42,10 @@ struct wpa_priv_interface { struct sockaddr_un l2_addr; }; +struct wpa_priv_global { + struct wpa_priv_interface *interfaces; +}; + static void wpa_priv_cmd_register(struct wpa_priv_interface *iface, struct sockaddr_un *from) @@ -65,7 +71,8 @@ static void wpa_priv_cmd_register(struct wpa_priv_interface *iface, if (iface->driver->init2) { if (iface->driver->global_init) { - iface->drv_global_priv = iface->driver->global_init(); + iface->drv_global_priv = + iface->driver->global_init(iface->ctx); if (!iface->drv_global_priv) { wpa_printf(MSG_INFO, "Failed to initialize driver global context"); @@ -638,7 +645,7 @@ static void wpa_priv_interface_deinit(struct wpa_priv_interface *iface) static struct wpa_priv_interface * -wpa_priv_interface_init(const char *dir, const char *params) +wpa_priv_interface_init(void *ctx, const char *dir, const char *params) { struct wpa_priv_interface *iface; char *pos; @@ -654,6 +661,7 @@ wpa_priv_interface_init(const char *dir, const char *params) if (iface == NULL) return NULL; iface->fd = -1; + iface->ctx = ctx; len = pos - params; iface->driver_name = dup_binstr(params, len); @@ -1002,6 +1010,37 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, } +void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event, + union wpa_event_data *data) +{ + struct wpa_priv_global *global = ctx; + struct wpa_priv_interface *iface; + + if (event != EVENT_INTERFACE_STATUS) + return; + + for (iface = global->interfaces; iface; iface = iface->next) { + if (os_strcmp(iface->ifname, data->interface_status.ifname) == + 0) + break; + } + if (iface && iface->driver->get_ifindex) { + unsigned int ifindex; + + ifindex = iface->driver->get_ifindex(iface->drv_priv); + if (ifindex != data->interface_status.ifindex) { + wpa_printf(MSG_DEBUG, + "%s: interface status ifindex %d mismatch (%d)", + iface->ifname, ifindex, + data->interface_status.ifindex); + return; + } + } + if (iface) + wpa_supplicant_event(iface, event, data); +} + + void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) { @@ -1060,7 +1099,7 @@ static void wpa_priv_fd_workaround(void) static void usage(void) { printf("wpa_priv v" VERSION_STR "\n" - "Copyright (c) 2007-2009, Jouni Malinen and " + "Copyright (c) 2007-2016, Jouni Malinen and " "contributors\n" "\n" "usage:\n" @@ -1077,13 +1116,17 @@ int main(int argc, char *argv[]) char *pid_file = NULL; int daemonize = 0; char *ctrl_dir = "/var/run/wpa_priv"; - struct wpa_priv_interface *interfaces = NULL, *iface; + struct wpa_priv_global global; + struct wpa_priv_interface *iface; if (os_program_init()) return -1; wpa_priv_fd_workaround(); + os_memset(&global, 0, sizeof(global)); + global.interfaces = NULL; + for (;;) { c = getopt(argc, argv, "Bc:dP:"); if (c < 0) @@ -1121,14 +1164,14 @@ int main(int argc, char *argv[]) for (i = optind; i < argc; i++) { wpa_printf(MSG_DEBUG, "Adding driver:interface %s", argv[i]); - iface = wpa_priv_interface_init(ctrl_dir, argv[i]); + iface = wpa_priv_interface_init(&global, ctrl_dir, argv[i]); if (iface == NULL) goto out; - iface->next = interfaces; - interfaces = iface; + iface->next = global.interfaces; + global.interfaces = iface; } - if (daemonize && os_daemonize(pid_file)) + if (daemonize && os_daemonize(pid_file) && eloop_sock_requeue()) goto out; eloop_register_signal_terminate(wpa_priv_terminate, NULL); @@ -1137,7 +1180,7 @@ int main(int argc, char *argv[]) ret = 0; out: - iface = interfaces; + iface = global.interfaces; while (iface) { struct wpa_priv_interface *prev = iface; iface = iface->next; diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant.c b/contrib/wpa/wpa_supplicant/wpa_supplicant.c index ef55fdcf79c0..7361ee96d1df 100644 --- a/contrib/wpa/wpa_supplicant/wpa_supplicant.c +++ b/contrib/wpa/wpa_supplicant/wpa_supplicant.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - * Copyright (c) 2003-2015, Jouni Malinen + * Copyright (c) 2003-2016, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -11,6 +11,10 @@ */ #include "includes.h" +#ifdef CONFIG_MATCH_IFACE +#include +#include +#endif /* CONFIG_MATCH_IFACE */ #include "common.h" #include "crypto/random.h" @@ -58,7 +62,7 @@ const char *const wpa_supplicant_version = "wpa_supplicant v" VERSION_STR "\n" -"Copyright (c) 2003-2015, Jouni Malinen and contributors"; +"Copyright (c) 2003-2016, Jouni Malinen and contributors"; const char *const wpa_supplicant_license = "This software may be distributed under the terms of the BSD license.\n" @@ -188,7 +192,9 @@ static void wpa_supplicant_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; const u8 *bssid = wpa_s->bssid; - if (is_zero_ether_addr(bssid)) + if (!is_zero_ether_addr(wpa_s->pending_bssid) && + (wpa_s->wpa_state == WPA_AUTHENTICATING || + wpa_s->wpa_state == WPA_ASSOCIATING)) bssid = wpa_s->pending_bssid; wpa_msg(wpa_s, MSG_INFO, "Authentication with " MACSTR " timed out.", MAC2STR(bssid)); @@ -397,6 +403,18 @@ void free_hw_features(struct wpa_supplicant *wpa_s) } +static void free_bss_tmp_disallowed(struct wpa_supplicant *wpa_s) +{ + struct wpa_bss_tmp_disallowed *bss, *prev; + + dl_list_for_each_safe(bss, prev, &wpa_s->bss_tmp_disallowed, + struct wpa_bss_tmp_disallowed, list) { + dl_list_del(&bss->list); + os_free(bss); + } +} + + static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) { int i; @@ -536,6 +554,8 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) wpa_s->last_scan_res = NULL; #ifdef CONFIG_HS20 + if (wpa_s->drv_priv) + wpa_drv_configure_frame_filters(wpa_s, 0); hs20_deinit(wpa_s); #endif /* CONFIG_HS20 */ @@ -545,6 +565,21 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) } wmm_ac_notify_disassoc(wpa_s); + + wpa_s->sched_scan_plans_num = 0; + os_free(wpa_s->sched_scan_plans); + wpa_s->sched_scan_plans = NULL; + +#ifdef CONFIG_MBO + wpa_s->non_pref_chan_num = 0; + os_free(wpa_s->non_pref_chan); + wpa_s->non_pref_chan = NULL; +#endif /* CONFIG_MBO */ + + free_bss_tmp_disallowed(wpa_s); + + wpabuf_free(wpa_s->lci); + wpa_s->lci = NULL; } @@ -963,6 +998,11 @@ static void wpa_supplicant_reconfig(int sig, void *signal_ctx) wpa_supplicant_terminate_proc(global); } } + + if (wpa_debug_reopen_file() < 0) { + /* Ignore errors since we cannot really do much to fix this */ + wpa_printf(MSG_DEBUG, "Could not reopen debug log file"); + } } @@ -1150,6 +1190,10 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, return -1; } +#ifdef CONFIG_NO_WPA + wpa_s->group_cipher = WPA_CIPHER_NONE; + wpa_s->pairwise_cipher = WPA_CIPHER_NONE; +#else /* CONFIG_NO_WPA */ sel = ie.group_cipher & ssid->group_cipher; wpa_s->group_cipher = wpa_pick_group_cipher(sel); if (wpa_s->group_cipher < 0) { @@ -1169,6 +1213,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, } wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK %s", wpa_cipher_txt(wpa_s->pairwise_cipher)); +#endif /* CONFIG_NO_WPA */ sel = ie.key_mgmt & ssid->key_mgmt; #ifdef CONFIG_SAE @@ -1279,7 +1324,8 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, int psk_set = 0; if (ssid->psk_set) { - wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN, NULL); + wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN, NULL, + NULL); psk_set = 1; } #ifndef CONFIG_NO_PBKDF2 @@ -1290,7 +1336,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, 4096, psk, PMK_LEN); wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)", psk, PMK_LEN); - wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL); + wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL, NULL); psk_set = 1; os_memset(psk, 0, sizeof(psk)); } @@ -1328,7 +1374,8 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, wpa_hexdump_key(MSG_MSGDUMP, "PSK (from " "external passphrase)", psk, PMK_LEN); - wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL); + wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL, + NULL); psk_set = 1; os_memset(psk, 0, sizeof(psk)); } else @@ -1341,7 +1388,8 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, ext_password_free(pw); return -1; } - wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL); + wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL, + NULL); psk_set = 1; os_memset(psk, 0, sizeof(psk)); } else { @@ -1404,9 +1452,20 @@ static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx) if (wpa_s->conf->hs20) *pos |= 0x40; /* Bit 46 - WNM-Notification */ #endif /* CONFIG_HS20 */ +#ifdef CONFIG_MBO + *pos |= 0x40; /* Bit 46 - WNM-Notification */ +#endif /* CONFIG_MBO */ break; case 6: /* Bits 48-55 */ break; + case 7: /* Bits 56-63 */ + break; + case 8: /* Bits 64-71 */ + if (wpa_s->conf->ftm_responder) + *pos |= 0x40; /* Bit 70 - FTM responder */ + if (wpa_s->conf->ftm_initiator) + *pos |= 0x80; /* Bit 71 - FTM initiator */ + break; } } @@ -1416,6 +1475,9 @@ int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen) u8 *pos = buf; u8 len = 6, i; + if (len < 9 && + (wpa_s->conf->ftm_initiator || wpa_s->conf->ftm_responder)) + len = 9; if (len < wpa_s->extended_capa_len) len = wpa_s->extended_capa_len; if (buflen < (size_t) len + 2) { @@ -1586,6 +1648,15 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, struct wpa_connect_work *cwork; int rand_style; + wpa_s->own_disconnect_req = 0; + + /* + * If we are starting a new connection, any previously pending EAPOL + * RX cannot be valid anymore. + */ + wpabuf_free(wpa_s->pending_eapol_rx); + wpa_s->pending_eapol_rx = NULL; + if (ssid->mac_addr == -1) rand_style = wpa_s->conf->mac_addr; else @@ -1593,9 +1664,11 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, wmm_ac_clear_saved_tspecs(wpa_s); wpa_s->reassoc_same_bss = 0; + wpa_s->reassoc_same_ess = 0; if (wpa_s->last_ssid == ssid) { wpa_dbg(wpa_s, MSG_DEBUG, "Re-association to the same ESS"); + wpa_s->reassoc_same_ess = 1; if (wpa_s->current_bss && wpa_s->current_bss == bss) { wmm_ac_save_tspecs(wpa_s); wpa_s->reassoc_same_bss = 1; @@ -1661,10 +1734,9 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, return; } wpa_s->current_bss = bss; - wpa_msg_ctrl(wpa_s, MSG_INFO, MESH_GROUP_STARTED - "ssid=\"%s\" id=%d", - wpa_ssid_txt(ssid->ssid, ssid->ssid_len), - ssid->id); + wpa_msg(wpa_s, MSG_INFO, MESH_GROUP_STARTED "ssid=\"%s\" id=%d", + wpa_ssid_txt(ssid->ssid, ssid->ssid_len), + ssid->id); #else /* CONFIG_MESH */ wpa_msg(wpa_s, MSG_ERROR, "mesh mode support not included in the build"); @@ -1694,6 +1766,8 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, return; } + wpas_abort_ongoing_scan(wpa_s); + cwork = os_zalloc(sizeof(*cwork)); if (cwork == NULL) return; @@ -1715,6 +1789,36 @@ static int bss_is_ibss(struct wpa_bss *bss) } +static int drv_supports_vht(struct wpa_supplicant *wpa_s, + const struct wpa_ssid *ssid) +{ + enum hostapd_hw_mode hw_mode; + struct hostapd_hw_modes *mode = NULL; + u8 channel; + int i; + +#ifdef CONFIG_HT_OVERRIDES + if (ssid->disable_ht) + return 0; +#endif /* CONFIG_HT_OVERRIDES */ + + hw_mode = ieee80211_freq_to_chan(ssid->frequency, &channel); + if (hw_mode == NUM_HOSTAPD_MODES) + return 0; + for (i = 0; wpa_s->hw.modes && i < wpa_s->hw.num_modes; i++) { + if (wpa_s->hw.modes[i].mode == hw_mode) { + mode = &wpa_s->hw.modes[i]; + break; + } + } + + if (!mode) + return 0; + + return mode->vht_capab != 0; +} + + void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, const struct wpa_ssid *ssid, struct hostapd_freq_params *freq) @@ -1727,8 +1831,10 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, struct hostapd_channel_data *pri_chan = NULL, *sec_chan = NULL; u8 channel; int i, chan_idx, ht40 = -1, res, obss_scan = 1; - unsigned int j; + unsigned int j, k; struct hostapd_freq_params vht_freq; + int chwidth, seg0, seg1; + u32 vht_caps = 0; freq->freq = ssid->frequency; @@ -1780,6 +1886,13 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, if (!mode) return; +#ifdef CONFIG_HT_OVERRIDES + if (ssid->disable_ht) { + freq->ht_enabled = 0; + return; + } +#endif /* CONFIG_HT_OVERRIDES */ + freq->ht_enabled = ht_supported(mode); if (!freq->ht_enabled) return; @@ -1801,6 +1914,11 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, if (pri_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR)) return; +#ifdef CONFIG_HT_OVERRIDES + if (ssid->disable_ht40) + return; +#endif /* CONFIG_HT_OVERRIDES */ + /* Check/setup HT40+/HT40- */ for (j = 0; j < ARRAY_SIZE(ht40plus); j++) { if (ht40plus[j] == channel) { @@ -1825,22 +1943,16 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, freq->channel = pri_chan->chan; - switch (ht40) { - case -1: + if (ht40 == -1) { if (!(pri_chan->flag & HOSTAPD_CHAN_HT40MINUS)) return; - freq->sec_channel_offset = -1; - break; - case 1: + } else { if (!(pri_chan->flag & HOSTAPD_CHAN_HT40PLUS)) return; - freq->sec_channel_offset = 1; - break; - default: - break; } + freq->sec_channel_offset = ht40; - if (freq->sec_channel_offset && obss_scan) { + if (obss_scan) { struct wpa_scan_results *scan_res; scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0); @@ -1878,12 +1990,12 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, "IBSS/mesh: setup freq channel %d, sec_channel_offset %d", freq->channel, freq->sec_channel_offset); - /* Not sure if mesh is ready for VHT */ - if (ssid->mode != WPAS_MODE_IBSS) + if (!drv_supports_vht(wpa_s, ssid)) return; /* For IBSS check VHT_IBSS flag */ - if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_VHT_IBSS)) + if (ssid->mode == WPAS_MODE_IBSS && + !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_VHT_IBSS)) return; vht_freq = *freq; @@ -1914,12 +2026,55 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, return; } + chwidth = VHT_CHANWIDTH_80MHZ; + seg0 = vht80[j] + 6; + seg1 = 0; + + if (ssid->max_oper_chwidth == VHT_CHANWIDTH_80P80MHZ) { + /* setup center_freq2, bandwidth */ + for (k = 0; k < ARRAY_SIZE(vht80); k++) { + /* Only accept 80 MHz segments separated by a gap */ + if (j == k || abs(vht80[j] - vht80[k]) == 16) + continue; + for (i = vht80[k]; i < vht80[k] + 16; i += 4) { + struct hostapd_channel_data *chan; + + chan = hw_get_channel_chan(mode, i, NULL); + if (!chan) + continue; + + if (chan->flag & (HOSTAPD_CHAN_DISABLED | + HOSTAPD_CHAN_NO_IR | + HOSTAPD_CHAN_RADAR)) + continue; + + /* Found a suitable second segment for 80+80 */ + chwidth = VHT_CHANWIDTH_80P80MHZ; + vht_caps |= + VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ; + seg1 = vht80[k] + 6; + } + + if (chwidth == VHT_CHANWIDTH_80P80MHZ) + break; + } + } else if (ssid->max_oper_chwidth == VHT_CHANWIDTH_160MHZ) { + if (freq->freq == 5180) { + chwidth = VHT_CHANWIDTH_160MHZ; + vht_caps |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; + seg0 = 50; + } else if (freq->freq == 5520) { + chwidth = VHT_CHANWIDTH_160MHZ; + vht_caps |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; + seg0 = 114; + } + } + if (hostapd_set_freq_params(&vht_freq, mode->mode, freq->freq, freq->channel, freq->ht_enabled, vht_freq.vht_enabled, freq->sec_channel_offset, - VHT_CHANWIDTH_80MHZ, - vht80[j] + 6, 0, 0) != 0) + chwidth, seg0, seg1, vht_caps) != 0) return; *freq = vht_freq; @@ -1944,6 +2099,7 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) int wep_keys_set = 0; int assoc_failed = 0; struct wpa_ssid *old_ssid; + u8 prev_bssid[ETH_ALEN]; #ifdef CONFIG_HT_OVERRIDES struct ieee80211_ht_capabilities htcaps; struct ieee80211_ht_capabilities htcaps_mask; @@ -1952,6 +2108,9 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) struct ieee80211_vht_capabilities vhtcaps; struct ieee80211_vht_capabilities vhtcaps_mask; #endif /* CONFIG_VHT_OVERRIDES */ +#ifdef CONFIG_MBO + const u8 *mbo = NULL; +#endif /* CONFIG_MBO */ if (deinit) { if (work->started) { @@ -1974,6 +2133,7 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) return; } + os_memcpy(prev_bssid, wpa_s->bssid, ETH_ALEN); os_memset(¶ms, 0, sizeof(params)); wpa_s->reassociate = 0; wpa_s->eap_expected_failure = 0; @@ -2015,7 +2175,10 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) } else { wpa_msg(wpa_s, MSG_INFO, "Trying to associate with SSID '%s'", wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); - os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); + if (bss) + os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN); + else + os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); } if (!wpa_s->pno) wpa_supplicant_cancel_sched_scan(wpa_s); @@ -2136,25 +2299,21 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) os_memset(wpa_s->p2p_ip_addr_info, 0, sizeof(wpa_s->p2p_ip_addr_info)); #endif /* CONFIG_P2P */ -#ifdef CONFIG_HS20 - if (is_hs20_network(wpa_s, ssid, bss)) { - struct wpabuf *hs20; - hs20 = wpabuf_alloc(20); - if (hs20) { - int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid); - size_t len; +#ifdef CONFIG_MBO + if (bss) { + mbo = wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE); + if (mbo) { + int len; - wpas_hs20_add_indication(hs20, pps_mo_id); - len = sizeof(wpa_ie) - wpa_ie_len; - if (wpabuf_len(hs20) <= len) { - os_memcpy(wpa_ie + wpa_ie_len, - wpabuf_head(hs20), wpabuf_len(hs20)); - wpa_ie_len += wpabuf_len(hs20); - } - wpabuf_free(hs20); + len = wpas_mbo_supp_op_class_ie(wpa_s, bss->freq, + wpa_ie + wpa_ie_len, + sizeof(wpa_ie) - + wpa_ie_len); + if (len > 0) + wpa_ie_len += len; } } -#endif /* CONFIG_HS20 */ +#endif /* CONFIG_MBO */ /* * Workaround: Add Extended Capabilities element only if the AP @@ -2164,6 +2323,11 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) * element in all cases, it is justifiable to skip it to avoid * interoperability issues. */ + if (ssid->p2p_group) + wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_CLIENT); + else + wpa_drv_get_ext_capa(wpa_s, WPA_IF_STATION); + if (!bss || wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB)) { u8 ext_capab[18]; int ext_capab_len; @@ -2180,6 +2344,29 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) } } +#ifdef CONFIG_HS20 + if (is_hs20_network(wpa_s, ssid, bss)) { + struct wpabuf *hs20; + + hs20 = wpabuf_alloc(20); + if (hs20) { + int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid); + size_t len; + + wpas_hs20_add_indication(hs20, pps_mo_id); + len = sizeof(wpa_ie) - wpa_ie_len; + if (wpabuf_len(hs20) <= len) { + os_memcpy(wpa_ie + wpa_ie_len, + wpabuf_head(hs20), wpabuf_len(hs20)); + wpa_ie_len += wpabuf_len(hs20); + } + wpabuf_free(hs20); + + hs20_configure_frame_filters(wpa_s); + } + } +#endif /* CONFIG_HS20 */ + if (wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]) { struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]; size_t len; @@ -2204,6 +2391,17 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) } #endif /* CONFIG_FST */ +#ifdef CONFIG_MBO + if (mbo) { + int len; + + len = wpas_mbo_ie(wpa_s, wpa_ie + wpa_ie_len, + sizeof(wpa_ie) - wpa_ie_len); + if (len >= 0) + wpa_ie_len += len; + } +#endif /* CONFIG_MBO */ + wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL); use_crypt = 1; cipher_pairwise = wpa_s->pairwise_cipher; @@ -2256,9 +2454,11 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) } params.bssid_hint = bss->bssid; params.freq_hint = bss->freq; + params.pbss = bss_is_pbss(bss); } else { params.ssid = ssid->ssid; params.ssid_len = ssid->ssid_len; + params.pbss = (ssid->pbss != 2) ? ssid->pbss : 0; } if (ssid->mode == WPAS_MODE_IBSS && ssid->bssid_set && @@ -2342,8 +2542,8 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) params.p2p = ssid->p2p_group; - if (wpa_s->parent->set_sta_uapsd) - params.uapsd = wpa_s->parent->sta_uapsd; + if (wpa_s->p2pdev->set_sta_uapsd) + params.uapsd = wpa_s->p2pdev->sta_uapsd; else params.uapsd = -1; @@ -2384,6 +2584,10 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) } #endif /* CONFIG_P2P */ + if (wpa_s->reassoc_same_ess && !is_zero_ether_addr(prev_bssid) && + wpa_s->current_ssid) + params.prev_bssid = prev_bssid; + ret = wpa_drv_associate(wpa_s, ¶ms); if (ret < 0) { wpa_msg(wpa_s, MSG_INFO, "Association request to the driver " @@ -2451,8 +2655,14 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) } old_ssid = wpa_s->current_ssid; wpa_s->current_ssid = ssid; - if (!wpas_driver_bss_selection(wpa_s) || ssid->bssid_set) + + if (!wpas_driver_bss_selection(wpa_s) || ssid->bssid_set) { wpa_s->current_bss = bss; +#ifdef CONFIG_HS20 + hs20_configure_frame_filters(wpa_s); +#endif /* CONFIG_HS20 */ + } + wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid); wpa_supplicant_initiate_eapol(wpa_s); if (old_ssid != wpa_s->current_ssid) @@ -2497,12 +2707,12 @@ void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s, MAC2STR(wpa_s->bssid), MAC2STR(wpa_s->pending_bssid), reason_code, wpa_supplicant_state_txt(wpa_s->wpa_state)); - if (!is_zero_ether_addr(wpa_s->bssid)) - addr = wpa_s->bssid; - else if (!is_zero_ether_addr(wpa_s->pending_bssid) && - (wpa_s->wpa_state == WPA_AUTHENTICATING || - wpa_s->wpa_state == WPA_ASSOCIATING)) + if (!is_zero_ether_addr(wpa_s->pending_bssid) && + (wpa_s->wpa_state == WPA_AUTHENTICATING || + wpa_s->wpa_state == WPA_ASSOCIATING)) addr = wpa_s->pending_bssid; + else if (!is_zero_ether_addr(wpa_s->bssid)) + addr = wpa_s->bssid; else if (wpa_s->wpa_state == WPA_ASSOCIATING) { /* * When using driver-based BSS selection, we may not know the @@ -2520,8 +2730,8 @@ void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s, #ifdef CONFIG_MESH if (wpa_s->ifmsh) { - wpa_msg_ctrl(wpa_s, MSG_INFO, MESH_GROUP_REMOVED "%s", - wpa_s->ifname); + wpa_msg(wpa_s, MSG_INFO, MESH_GROUP_REMOVED "%s", + wpa_s->ifname); wpa_supplicant_leave_mesh(wpa_s); } #endif /* CONFIG_MESH */ @@ -2558,6 +2768,95 @@ static void wpa_supplicant_enable_one_network(struct wpa_supplicant *wpa_s, } +/** + * wpa_supplicant_add_network - Add a new network + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: The new network configuration or %NULL if operation failed + * + * This function performs the following operations: + * 1. Adds a new network. + * 2. Send network addition notification. + * 3. Marks the network disabled. + * 4. Set network default parameters. + */ +struct wpa_ssid * wpa_supplicant_add_network(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid; + + ssid = wpa_config_add_network(wpa_s->conf); + if (!ssid) + return NULL; + wpas_notify_network_added(wpa_s, ssid); + ssid->disabled = 1; + wpa_config_set_network_defaults(ssid); + + return ssid; +} + + +/** + * wpa_supplicant_remove_network - Remove a configured network based on id + * @wpa_s: wpa_supplicant structure for a network interface + * @id: Unique network id to search for + * Returns: 0 on success, or -1 if the network was not found, -2 if the network + * could not be removed + * + * This function performs the following operations: + * 1. Removes the network. + * 2. Send network removal notification. + * 3. Update internal state machines. + * 4. Stop any running sched scans. + */ +int wpa_supplicant_remove_network(struct wpa_supplicant *wpa_s, int id) +{ + struct wpa_ssid *ssid; + int was_disabled; + + ssid = wpa_config_get_network(wpa_s->conf, id); + if (!ssid) + return -1; + wpas_notify_network_removed(wpa_s, ssid); + + if (wpa_s->last_ssid == ssid) + wpa_s->last_ssid = NULL; + + if (ssid == wpa_s->current_ssid || !wpa_s->current_ssid) { +#ifdef CONFIG_SME + wpa_s->sme.prev_bssid_set = 0; +#endif /* CONFIG_SME */ + /* + * Invalidate the EAP session cache if the current or + * previously used network is removed. + */ + eapol_sm_invalidate_cached_session(wpa_s->eapol); + } + + if (ssid == wpa_s->current_ssid) { + wpa_sm_set_config(wpa_s->wpa, NULL); + eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); + + if (wpa_s->wpa_state >= WPA_AUTHENTICATING) + wpa_s->own_disconnect_req = 1; + wpa_supplicant_deauthenticate(wpa_s, + WLAN_REASON_DEAUTH_LEAVING); + } + + was_disabled = ssid->disabled; + + if (wpa_config_remove_network(wpa_s->conf, id) < 0) + return -2; + + if (!was_disabled && wpa_s->sched_scanning) { + wpa_printf(MSG_DEBUG, + "Stop ongoing sched_scan to remove network from filters"); + wpa_supplicant_cancel_sched_scan(wpa_s); + wpa_supplicant_req_scan(wpa_s, 0, 0); + } + + return 0; +} + + /** * wpa_supplicant_enable_network - Mark a configured network as enabled * @wpa_s: wpa_supplicant structure for a network interface @@ -2688,7 +2987,8 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s, wpas_notify_network_enabled_changed(wpa_s, other_ssid); } - if (ssid && ssid == wpa_s->current_ssid && wpa_s->current_ssid) { + if (ssid && ssid == wpa_s->current_ssid && wpa_s->current_ssid && + wpa_s->wpa_state >= WPA_AUTHENTICATING) { /* We are already associated with the selected network */ wpa_printf(MSG_DEBUG, "Already associated with the " "selected network - do nothing"); @@ -2717,6 +3017,7 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s, if (wpa_s->connect_without_scan || wpa_supplicant_fast_associate(wpa_s) != 1) { wpa_s->scan_req = NORMAL_SCAN_REQ; + wpas_scan_reset_sched_scan(wpa_s); wpa_supplicant_req_scan(wpa_s, 0, disconnected ? 100000 : 0); } @@ -2994,7 +3295,7 @@ static int select_driver(struct wpa_supplicant *wpa_s, int i) struct wpa_global *global = wpa_s->global; if (wpa_drivers[i]->global_init && global->drv_priv[i] == NULL) { - global->drv_priv[i] = wpa_drivers[i]->global_init(); + global->drv_priv[i] = wpa_drivers[i]->global_init(global); if (global->drv_priv[i] == NULL) { wpa_printf(MSG_ERROR, "Failed to initialize driver " "'%s'", wpa_drivers[i]->name); @@ -3077,6 +3378,13 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR, MAC2STR(src_addr)); wpa_hexdump(MSG_MSGDUMP, "RX EAPOL", buf, len); +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_s->ignore_auth_resp) { + wpa_printf(MSG_INFO, "RX EAPOL - ignore_auth_resp active!"); + return; + } +#endif /* CONFIG_TESTING_OPTIONS */ + #ifdef CONFIG_PEERKEY if (wpa_s->wpa_state > WPA_ASSOCIATED && wpa_s->current_ssid && wpa_s->current_ssid->peerkey && @@ -3361,8 +3669,11 @@ wpa_supplicant_alloc(struct wpa_supplicant *parent) wpa_s->scan_interval = 5; wpa_s->new_connection = 1; wpa_s->parent = parent ? parent : wpa_s; + wpa_s->p2pdev = wpa_s->parent; wpa_s->sched_scanning = 0; + dl_list_init(&wpa_s->bss_tmp_disallowed); + return wpa_s; } @@ -3614,8 +3925,8 @@ void wpa_supplicant_apply_vht_overrides( if (!vhtcaps || !vhtcaps_mask) return; - vhtcaps->vht_capabilities_info = ssid->vht_capa; - vhtcaps_mask->vht_capabilities_info = ssid->vht_capa_mask; + vhtcaps->vht_capabilities_info = host_to_le32(ssid->vht_capa); + vhtcaps_mask->vht_capabilities_info = host_to_le32(ssid->vht_capa_mask); #ifdef CONFIG_HT_OVERRIDES /* if max ampdu is <= 3, we have to make the HT cap the same */ @@ -3637,15 +3948,17 @@ void wpa_supplicant_apply_vht_overrides( #define OVERRIDE_MCS(i) \ if (ssid->vht_tx_mcs_nss_ ##i >= 0) { \ vhtcaps_mask->vht_supported_mcs_set.tx_map |= \ - 3 << 2 * (i - 1); \ + host_to_le16(3 << 2 * (i - 1)); \ vhtcaps->vht_supported_mcs_set.tx_map |= \ - ssid->vht_tx_mcs_nss_ ##i << 2 * (i - 1); \ + host_to_le16(ssid->vht_tx_mcs_nss_ ##i << \ + 2 * (i - 1)); \ } \ if (ssid->vht_rx_mcs_nss_ ##i >= 0) { \ vhtcaps_mask->vht_supported_mcs_set.rx_map |= \ - 3 << 2 * (i - 1); \ + host_to_le16(3 << 2 * (i - 1)); \ vhtcaps->vht_supported_mcs_set.rx_map |= \ - ssid->vht_rx_mcs_nss_ ##i << 2 * (i - 1); \ + host_to_le16(ssid->vht_rx_mcs_nss_ ##i << \ + 2 * (i - 1)); \ } OVERRIDE_MCS(1); @@ -3817,8 +4130,9 @@ static void wpas_fst_update_mb_ie_cb(void *ctx, const u8 *addr, } -const u8 * wpas_fst_get_peer_first(void *ctx, struct fst_get_peer_ctx **get_ctx, - Boolean mb_only) +static const u8 * wpas_fst_get_peer_first(void *ctx, + struct fst_get_peer_ctx **get_ctx, + Boolean mb_only) { struct wpa_supplicant *wpa_s = ctx; @@ -3830,8 +4144,9 @@ const u8 * wpas_fst_get_peer_first(void *ctx, struct fst_get_peer_ctx **get_ctx, } -const u8 * wpas_fst_get_peer_next(void *ctx, struct fst_get_peer_ctx **get_ctx, - Boolean mb_only) +static const u8 * wpas_fst_get_peer_next(void *ctx, + struct fst_get_peer_ctx **get_ctx, + Boolean mb_only) { return NULL; } @@ -3870,6 +4185,55 @@ static int wpas_set_wowlan_triggers(struct wpa_supplicant *wpa_s, } +enum wpa_radio_work_band wpas_freq_to_band(int freq) +{ + if (freq < 3000) + return BAND_2_4_GHZ; + if (freq > 50000) + return BAND_60_GHZ; + return BAND_5_GHZ; +} + + +unsigned int wpas_get_bands(struct wpa_supplicant *wpa_s, const int *freqs) +{ + int i; + unsigned int band = 0; + + if (freqs) { + /* freqs are specified for the radio work */ + for (i = 0; freqs[i]; i++) + band |= wpas_freq_to_band(freqs[i]); + } else { + /* + * freqs are not specified, implies all + * the supported freqs by HW + */ + for (i = 0; i < wpa_s->hw.num_modes; i++) { + if (wpa_s->hw.modes[i].num_channels != 0) { + if (wpa_s->hw.modes[i].mode == + HOSTAPD_MODE_IEEE80211B || + wpa_s->hw.modes[i].mode == + HOSTAPD_MODE_IEEE80211G) + band |= BAND_2_4_GHZ; + else if (wpa_s->hw.modes[i].mode == + HOSTAPD_MODE_IEEE80211A) + band |= BAND_5_GHZ; + else if (wpa_s->hw.modes[i].mode == + HOSTAPD_MODE_IEEE80211AD) + band |= BAND_60_GHZ; + else if (wpa_s->hw.modes[i].mode == + HOSTAPD_MODE_IEEE80211ANY) + band = BAND_2_4_GHZ | BAND_5_GHZ | + BAND_60_GHZ; + } + } + } + + return band; +} + + static struct wpa_radio * radio_add_interface(struct wpa_supplicant *wpa_s, const char *rn) { @@ -3922,11 +4286,103 @@ static void radio_work_free(struct wpa_radio_work *work) } #endif /* CONFIG_P2P */ + if (work->started) { + work->wpa_s->radio->num_active_works--; + wpa_dbg(work->wpa_s, MSG_DEBUG, + "radio_work_free('%s'@%p: num_active_works --> %u", + work->type, work, + work->wpa_s->radio->num_active_works); + } + dl_list_del(&work->list); os_free(work); } +static struct wpa_radio_work * radio_work_get_next_work(struct wpa_radio *radio) +{ + struct wpa_radio_work *active_work = NULL; + struct wpa_radio_work *tmp; + + /* Get the active work to know the type and band. */ + dl_list_for_each(tmp, &radio->work, struct wpa_radio_work, list) { + if (tmp->started) { + active_work = tmp; + break; + } + } + + if (!active_work) { + /* No active work, start one */ + radio->num_active_works = 0; + dl_list_for_each(tmp, &radio->work, struct wpa_radio_work, + list) { + if (os_strcmp(tmp->type, "scan") == 0 && + radio->external_scan_running && + (((struct wpa_driver_scan_params *) + tmp->ctx)->only_new_results || + tmp->wpa_s->clear_driver_scan_cache)) + continue; + return tmp; + } + return NULL; + } + + if (os_strcmp(active_work->type, "sme-connect") == 0 || + os_strcmp(active_work->type, "connect") == 0) { + /* + * If the active work is either connect or sme-connect, + * do not parallelize them with other radio works. + */ + wpa_dbg(active_work->wpa_s, MSG_DEBUG, + "Do not parallelize radio work with %s", + active_work->type); + return NULL; + } + + dl_list_for_each(tmp, &radio->work, struct wpa_radio_work, list) { + if (tmp->started) + continue; + + /* + * If connect or sme-connect are enqueued, parallelize only + * those operations ahead of them in the queue. + */ + if (os_strcmp(tmp->type, "connect") == 0 || + os_strcmp(tmp->type, "sme-connect") == 0) + break; + + /* + * Check that the radio works are distinct and + * on different bands. + */ + if (os_strcmp(active_work->type, tmp->type) != 0 && + (active_work->bands != tmp->bands)) { + /* + * If a scan has to be scheduled through nl80211 scan + * interface and if an external scan is already running, + * do not schedule the scan since it is likely to get + * rejected by kernel. + */ + if (os_strcmp(tmp->type, "scan") == 0 && + radio->external_scan_running && + (((struct wpa_driver_scan_params *) + tmp->ctx)->only_new_results || + tmp->wpa_s->clear_driver_scan_cache)) + continue; + + wpa_dbg(active_work->wpa_s, MSG_DEBUG, + "active_work:%s new_work:%s", + active_work->type, tmp->type); + return tmp; + } + } + + /* Did not find a radio work to schedule in parallel. */ + return NULL; +} + + static void radio_start_next_work(void *eloop_ctx, void *timeout_ctx) { struct wpa_radio *radio = eloop_ctx; @@ -3935,26 +4391,48 @@ static void radio_start_next_work(void *eloop_ctx, void *timeout_ctx) struct wpa_supplicant *wpa_s; work = dl_list_first(&radio->work, struct wpa_radio_work, list); - if (work == NULL) - return; - - if (work->started) - return; /* already started and still in progress */ - - wpa_s = dl_list_first(&radio->ifaces, struct wpa_supplicant, - radio_list); - if (wpa_s && wpa_s->radio->external_scan_running) { - wpa_printf(MSG_DEBUG, "Delay radio work start until externally triggered scan completes"); + if (work == NULL) { + radio->num_active_works = 0; return; } + wpa_s = dl_list_first(&radio->ifaces, struct wpa_supplicant, + radio_list); + + if (!(wpa_s && + wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS)) { + if (work->started) + return; /* already started and still in progress */ + + if (wpa_s && wpa_s->radio->external_scan_running) { + wpa_printf(MSG_DEBUG, "Delay radio work start until externally triggered scan completes"); + return; + } + } else { + work = NULL; + if (radio->num_active_works < MAX_ACTIVE_WORKS) { + /* get the work to schedule next */ + work = radio_work_get_next_work(radio); + } + if (!work) + return; + } + + wpa_s = work->wpa_s; os_get_reltime(&now); os_reltime_sub(&now, &work->time, &diff); - wpa_dbg(work->wpa_s, MSG_DEBUG, "Starting radio work '%s'@%p after %ld.%06ld second wait", + wpa_dbg(wpa_s, MSG_DEBUG, + "Starting radio work '%s'@%p after %ld.%06ld second wait", work->type, work, diff.sec, diff.usec); work->started = 1; work->time = now; + radio->num_active_works++; + work->cb(work, 0); + + if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS) && + radio->num_active_works < MAX_ACTIVE_WORKS) + radio_work_check_next(wpa_s); } @@ -4062,6 +4540,7 @@ int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq, void (*cb)(struct wpa_radio_work *work, int deinit), void *ctx) { + struct wpa_radio *radio = wpa_s->radio; struct wpa_radio_work *work; int was_empty; @@ -4076,6 +4555,16 @@ int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq, work->cb = cb; work->ctx = ctx; + if (freq) + work->bands = wpas_freq_to_band(freq); + else if (os_strcmp(type, "scan") == 0 || + os_strcmp(type, "p2p-scan") == 0) + work->bands = wpas_get_bands(wpa_s, + ((struct wpa_driver_scan_params *) + ctx)->freqs); + else + work->bands = wpas_get_bands(wpa_s, NULL); + was_empty = dl_list_empty(&wpa_s->radio->work); if (next) dl_list_add(&wpa_s->radio->work, &work->list); @@ -4084,6 +4573,12 @@ int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq, if (was_empty) { wpa_dbg(wpa_s, MSG_DEBUG, "First radio work item in the queue - schedule start immediately"); radio_work_check_next(wpa_s); + } else if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS) + && radio->num_active_works < MAX_ACTIVE_WORKS) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Try to schedule a radio work (num_active_works=%u)", + radio->num_active_works); + radio_work_check_next(wpa_s); } return 0; @@ -4339,6 +4834,11 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, wpa_s->probe_resp_offloads = capa.probe_resp_offloads; wpa_s->max_scan_ssids = capa.max_scan_ssids; wpa_s->max_sched_scan_ssids = capa.max_sched_scan_ssids; + wpa_s->max_sched_scan_plans = capa.max_sched_scan_plans; + wpa_s->max_sched_scan_plan_interval = + capa.max_sched_scan_plan_interval; + wpa_s->max_sched_scan_plan_iterations = + capa.max_sched_scan_plan_iterations; wpa_s->sched_scan_supported = capa.sched_scan_supported; wpa_s->max_match_sets = capa.max_match_sets; wpa_s->max_remain_on_chan = capa.max_remain_on_chan; @@ -4478,6 +4978,17 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, wpas_rrm_reset(wpa_s); + wpas_sched_scan_plans_set(wpa_s, wpa_s->conf->sched_scan_plans); + +#ifdef CONFIG_HS20 + hs20_init(wpa_s); +#endif /* CONFIG_HS20 */ +#ifdef CONFIG_MBO + wpas_mbo_update_non_pref_chan(wpa_s, wpa_s->conf->non_pref_chan); +#endif /* CONFIG_MBO */ + + wpa_supplicant_set_default_scan_ies(wpa_s); + return 0; } @@ -4493,6 +5004,8 @@ static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s, iface = global->ifaces; while (iface) { + if (iface->p2pdev == wpa_s) + iface->p2pdev = iface->parent; if (iface == wpa_s || iface->parent != wpa_s) { iface = iface->next; continue; @@ -4563,6 +5076,74 @@ static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s, } +#ifdef CONFIG_MATCH_IFACE + +/** + * wpa_supplicant_match_iface - Match an interface description to a name + * @global: Pointer to global data from wpa_supplicant_init() + * @ifname: Name of the interface to match + * Returns: Pointer to the created interface description or %NULL on failure + */ +struct wpa_interface * wpa_supplicant_match_iface(struct wpa_global *global, + const char *ifname) +{ + int i; + struct wpa_interface *iface, *miface; + + for (i = 0; i < global->params.match_iface_count; i++) { + miface = &global->params.match_ifaces[i]; + if (!miface->ifname || + fnmatch(miface->ifname, ifname, 0) == 0) { + iface = os_zalloc(sizeof(*iface)); + if (!iface) + return NULL; + *iface = *miface; + iface->ifname = ifname; + return iface; + } + } + + return NULL; +} + + +/** + * wpa_supplicant_match_existing - Match existing interfaces + * @global: Pointer to global data from wpa_supplicant_init() + * Returns: 0 on success, -1 on failure + */ +static int wpa_supplicant_match_existing(struct wpa_global *global) +{ + struct if_nameindex *ifi, *ifp; + struct wpa_supplicant *wpa_s; + struct wpa_interface *iface; + + ifp = if_nameindex(); + if (!ifp) { + wpa_printf(MSG_ERROR, "if_nameindex: %s", strerror(errno)); + return -1; + } + + for (ifi = ifp; ifi->if_name; ifi++) { + wpa_s = wpa_supplicant_get_iface(global, ifi->if_name); + if (wpa_s) + continue; + iface = wpa_supplicant_match_iface(global, ifi->if_name); + if (iface) { + wpa_s = wpa_supplicant_add_iface(global, iface, NULL); + os_free(iface); + if (wpa_s) + wpa_s->matched = 1; + } + } + + if_freenameindex(ifp); + return 0; +} + +#endif /* CONFIG_MATCH_IFACE */ + + /** * wpa_supplicant_add_iface - Add a new network interface * @global: Pointer to global data from wpa_supplicant_init() @@ -4864,6 +5445,18 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params) if (params->override_ctrl_interface) global->params.override_ctrl_interface = os_strdup(params->override_ctrl_interface); +#ifdef CONFIG_MATCH_IFACE + global->params.match_iface_count = params->match_iface_count; + if (params->match_iface_count) { + global->params.match_ifaces = + os_calloc(params->match_iface_count, + sizeof(struct wpa_interface)); + os_memcpy(global->params.match_ifaces, + params->match_ifaces, + params->match_iface_count * + sizeof(struct wpa_interface)); + } +#endif /* CONFIG_MATCH_IFACE */ #ifdef CONFIG_P2P if (params->conf_p2p_dev) global->params.conf_p2p_dev = @@ -4939,12 +5532,18 @@ int wpa_supplicant_run(struct wpa_global *global) struct wpa_supplicant *wpa_s; if (global->params.daemonize && - wpa_supplicant_daemon(global->params.pid_file)) + (wpa_supplicant_daemon(global->params.pid_file) || + eloop_sock_requeue())) return -1; +#ifdef CONFIG_MATCH_IFACE + if (wpa_supplicant_match_existing(global)) + return -1; +#endif + if (global->params.wait_for_monitor) { for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) - if (wpa_s->ctrl_iface) + if (wpa_s->ctrl_iface && !wpa_s->p2p_mgmt) wpa_supplicant_ctrl_iface_wait( wpa_s->ctrl_iface); } @@ -5010,6 +5609,9 @@ void wpa_supplicant_deinit(struct wpa_global *global) os_free(global->params.ctrl_interface_group); os_free(global->params.override_driver); os_free(global->params.override_ctrl_interface); +#ifdef CONFIG_MATCH_IFACE + os_free(global->params.match_ifaces); +#endif /* CONFIG_MATCH_IFACE */ #ifdef CONFIG_P2P os_free(global->params.conf_p2p_dev); #endif /* CONFIG_P2P */ @@ -5042,6 +5644,9 @@ void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s) if (wpa_s->conf->changed_parameters & CFG_CHANGED_EXT_PW_BACKEND) wpas_init_ext_pw(wpa_s); + if (wpa_s->conf->changed_parameters & CFG_CHANGED_SCHED_SCAN_PLANS) + wpas_sched_scan_plans_set(wpa_s, wpa_s->conf->sched_scan_plans); + #ifdef CONFIG_WPS wpas_wps_update_config(wpa_s); #endif /* CONFIG_WPS */ @@ -5281,6 +5886,16 @@ int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s, if (wpa_s->wpa_state == WPA_SCANNING && !wpa_s->scanning) wpa_supplicant_req_scan(wpa_s, 0, 0); break; + case WPA_CTRL_REQ_EXT_CERT_CHECK: + if (eap->pending_ext_cert_check != PENDING_CHECK) + return -1; + if (os_strcmp(value, "good") == 0) + eap->pending_ext_cert_check = EXT_CERT_CHECK_GOOD; + else if (os_strcmp(value, "bad") == 0) + eap->pending_ext_cert_check = EXT_CERT_CHECK_BAD; + else + return -1; + break; default: wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", field); return -1; @@ -5350,6 +5965,19 @@ int wpas_get_ssid_pmf(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) return NO_MGMT_FRAME_PROTECTION; } + if (ssid && + (ssid->key_mgmt & + ~(WPA_KEY_MGMT_NONE | WPA_KEY_MGMT_WPS | + WPA_KEY_MGMT_IEEE8021X_NO_WPA)) == 0) { + /* + * Do not use the default PMF value for non-RSN networks + * since PMF is available only with RSN and pmf=2 + * configuration would otherwise prevent connections to + * all open networks. + */ + return NO_MGMT_FRAME_PROTECTION; + } + return wpa_s->conf->pmf; } @@ -5508,6 +6136,27 @@ void wpas_request_connection(struct wpa_supplicant *wpa_s) } +/** + * wpas_request_disconnection - Request disconnection + * @wpa_s: Pointer to the network interface + * + * This function is used to request disconnection from the currently connected + * network. This will stop any ongoing scans and initiate deauthentication. + */ +void wpas_request_disconnection(struct wpa_supplicant *wpa_s) +{ +#ifdef CONFIG_SME + wpa_s->sme.prev_bssid_set = 0; +#endif /* CONFIG_SME */ + wpa_s->reassociate = 0; + wpa_s->disconnected = 1; + wpa_supplicant_cancel_sched_scan(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); +} + + void dump_freq_data(struct wpa_supplicant *wpa_s, const char *title, struct wpa_used_freq_data *freqs_data, unsigned int len) @@ -5690,11 +6339,19 @@ void wpas_rrm_process_neighbor_rep(struct wpa_supplicant *wpa_s, #define ECANCELED -1 #endif +/* Measurement Request element + Location Subject + Maximum Age subelement */ +#define MEASURE_REQUEST_LCI_LEN (3 + 1 + 4) +/* Measurement Request element + Location Civic Request */ +#define MEASURE_REQUEST_CIVIC_LEN (3 + 5) + + /** * wpas_rrm_send_neighbor_rep_request - Request a neighbor report from our AP * @wpa_s: Pointer to wpa_supplicant * @ssid: if not null, this is sent in the request. Otherwise, no SSID IE * is sent in the request. + * @lci: if set, neighbor request will include LCI request + * @civic: if set, neighbor request will include civic location request * @cb: Callback function to be called once the requested report arrives, or * timed out after RRM_NEIGHBOR_REPORT_TIMEOUT seconds. * In the former case, 'neighbor_rep' is a newly allocated wpabuf, and it's @@ -5708,7 +6365,8 @@ void wpas_rrm_process_neighbor_rep(struct wpa_supplicant *wpa_s, * Request must contain a callback function. */ int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s, - const struct wpa_ssid *ssid, + const struct wpa_ssid_value *ssid, + int lci, int civic, void (*cb)(void *ctx, struct wpabuf *neighbor_rep), void *cb_ctx) @@ -5749,7 +6407,9 @@ int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s, } /* 3 = action category + action code + dialog token */ - buf = wpabuf_alloc(3 + (ssid ? 2 + ssid->ssid_len : 0)); + buf = wpabuf_alloc(3 + (ssid ? 2 + ssid->ssid_len : 0) + + (lci ? 2 + MEASURE_REQUEST_LCI_LEN : 0) + + (civic ? 2 + MEASURE_REQUEST_CIVIC_LEN : 0)); if (buf == NULL) { wpa_printf(MSG_DEBUG, "RRM: Failed to allocate Neighbor Report Request"); @@ -5769,6 +6429,72 @@ int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s, wpabuf_put_data(buf, ssid->ssid, ssid->ssid_len); } + if (lci) { + /* IEEE P802.11-REVmc/D5.0 9.4.2.21 */ + wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST); + wpabuf_put_u8(buf, MEASURE_REQUEST_LCI_LEN); + + /* + * Measurement token; nonzero number that is unique among the + * Measurement Request elements in a particular frame. + */ + wpabuf_put_u8(buf, 1); /* Measurement Token */ + + /* + * Parallel, Enable, Request, and Report bits are 0, Duration is + * reserved. + */ + wpabuf_put_u8(buf, 0); /* Measurement Request Mode */ + wpabuf_put_u8(buf, MEASURE_TYPE_LCI); /* Measurement Type */ + + /* IEEE P802.11-REVmc/D5.0 9.4.2.21.10 - LCI request */ + /* Location Subject */ + wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE); + + /* Optional Subelements */ + /* + * IEEE P802.11-REVmc/D5.0 Figure 9-170 + * The Maximum Age subelement is required, otherwise the AP can + * send only data that was determined after receiving the + * request. Setting it here to unlimited age. + */ + wpabuf_put_u8(buf, LCI_REQ_SUBELEM_MAX_AGE); + wpabuf_put_u8(buf, 2); + wpabuf_put_le16(buf, 0xffff); + } + + if (civic) { + /* IEEE P802.11-REVmc/D5.0 9.4.2.21 */ + wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST); + wpabuf_put_u8(buf, MEASURE_REQUEST_CIVIC_LEN); + + /* + * Measurement token; nonzero number that is unique among the + * Measurement Request elements in a particular frame. + */ + wpabuf_put_u8(buf, 2); /* Measurement Token */ + + /* + * Parallel, Enable, Request, and Report bits are 0, Duration is + * reserved. + */ + wpabuf_put_u8(buf, 0); /* Measurement Request Mode */ + /* Measurement Type */ + wpabuf_put_u8(buf, MEASURE_TYPE_LOCATION_CIVIC); + + /* IEEE P802.11-REVmc/D5.0 9.4.2.21.14: + * Location Civic request */ + /* Location Subject */ + wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE); + wpabuf_put_u8(buf, 0); /* Civic Location Type: IETF RFC 4776 */ + /* Location Service Interval Units: Seconds */ + wpabuf_put_u8(buf, 0); + /* Location Service Interval: 0 - Only one report is requested + */ + wpabuf_put_le16(buf, 0); + /* No optional subelements */ + } + wpa_s->rrm.next_neighbor_rep_token++; if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, @@ -5791,6 +6517,147 @@ int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s, } +static struct wpabuf * wpas_rrm_build_lci_report(struct wpa_supplicant *wpa_s, + const u8 *request, size_t len, + struct wpabuf *report) +{ + u8 token, type, subject; + u16 max_age = 0; + struct os_reltime t, diff; + unsigned long diff_l; + u8 *ptoken; + const u8 *subelem; + + if (!wpa_s->lci || len < 3 + 4) + return report; + + token = *request++; + /* Measurement request mode isn't used */ + request++; + type = *request++; + subject = *request++; + + wpa_printf(MSG_DEBUG, + "Measurement request token %u type %u location subject %u", + token, type, subject); + + if (type != MEASURE_TYPE_LCI || subject != LOCATION_SUBJECT_REMOTE) { + wpa_printf(MSG_INFO, + "Not building LCI report - bad type or location subject"); + return report; + } + + /* Subelements are formatted exactly like elements */ + subelem = get_ie(request, len, LCI_REQ_SUBELEM_MAX_AGE); + if (subelem && subelem[1] == 2) + max_age = WPA_GET_LE16(subelem + 2); + + if (os_get_reltime(&t)) + return report; + + os_reltime_sub(&t, &wpa_s->lci_time, &diff); + /* LCI age is calculated in 10th of a second units. */ + diff_l = diff.sec * 10 + diff.usec / 100000; + + if (max_age != 0xffff && max_age < diff_l) + return report; + + if (wpabuf_resize(&report, 2 + wpabuf_len(wpa_s->lci))) + return report; + + wpabuf_put_u8(report, WLAN_EID_MEASURE_REPORT); + wpabuf_put_u8(report, wpabuf_len(wpa_s->lci)); + /* We'll override user's measurement token */ + ptoken = wpabuf_put(report, 0); + wpabuf_put_buf(report, wpa_s->lci); + *ptoken = token; + + return report; +} + + +void wpas_rrm_handle_radio_measurement_request(struct wpa_supplicant *wpa_s, + const u8 *src, + const u8 *frame, size_t len) +{ + struct wpabuf *buf, *report; + u8 token; + const u8 *ie, *end; + + if (wpa_s->wpa_state != WPA_COMPLETED) { + wpa_printf(MSG_INFO, + "RRM: Ignoring radio measurement request: Not associated"); + return; + } + + if (!wpa_s->rrm.rrm_used) { + wpa_printf(MSG_INFO, + "RRM: Ignoring radio measurement request: Not RRM network"); + return; + } + + if (len < 3) { + wpa_printf(MSG_INFO, + "RRM: Ignoring too short radio measurement request"); + return; + } + + end = frame + len; + + token = *frame++; + + /* Ignore number of repetitions because it's not used in LCI request */ + frame += 2; + + report = NULL; + while ((ie = get_ie(frame, end - frame, WLAN_EID_MEASURE_REQUEST)) && + ie[1] >= 3) { + u8 msmt_type; + + msmt_type = ie[4]; + wpa_printf(MSG_DEBUG, "RRM request %d", msmt_type); + + switch (msmt_type) { + case MEASURE_TYPE_LCI: + report = wpas_rrm_build_lci_report(wpa_s, ie + 2, ie[1], + report); + break; + default: + wpa_printf(MSG_INFO, + "RRM: Unsupported radio measurement request %d", + msmt_type); + break; + } + + frame = ie + ie[1] + 2; + } + + if (!report) + return; + + buf = wpabuf_alloc(3 + wpabuf_len(report)); + if (!buf) { + wpabuf_free(report); + return; + } + + wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); + wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REPORT); + wpabuf_put_u8(buf, token); + + wpabuf_put_buf(buf, report); + wpabuf_free(report); + + if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, src, + wpa_s->own_addr, wpa_s->bssid, + wpabuf_head(buf), wpabuf_len(buf), 0)) { + wpa_printf(MSG_ERROR, + "RRM: Radio measurement report failed: Sending Action frame failed"); + } + wpabuf_free(buf); +} + + void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s, const u8 *src, const u8 *frame, size_t len, @@ -5868,3 +6735,175 @@ void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s, } wpabuf_free(buf); } + + +struct wpa_supplicant * +wpas_vendor_elem(struct wpa_supplicant *wpa_s, enum wpa_vendor_elem_frame frame) +{ + switch (frame) { +#ifdef CONFIG_P2P + case VENDOR_ELEM_PROBE_REQ_P2P: + case VENDOR_ELEM_PROBE_RESP_P2P: + case VENDOR_ELEM_PROBE_RESP_P2P_GO: + case VENDOR_ELEM_BEACON_P2P_GO: + case VENDOR_ELEM_P2P_PD_REQ: + case VENDOR_ELEM_P2P_PD_RESP: + case VENDOR_ELEM_P2P_GO_NEG_REQ: + case VENDOR_ELEM_P2P_GO_NEG_RESP: + case VENDOR_ELEM_P2P_GO_NEG_CONF: + case VENDOR_ELEM_P2P_INV_REQ: + case VENDOR_ELEM_P2P_INV_RESP: + case VENDOR_ELEM_P2P_ASSOC_REQ: + case VENDOR_ELEM_P2P_ASSOC_RESP: + return wpa_s->p2pdev; +#endif /* CONFIG_P2P */ + default: + return wpa_s; + } +} + + +void wpas_vendor_elem_update(struct wpa_supplicant *wpa_s) +{ + unsigned int i; + char buf[30]; + + wpa_printf(MSG_DEBUG, "Update vendor elements"); + + for (i = 0; i < NUM_VENDOR_ELEM_FRAMES; i++) { + if (wpa_s->vendor_elem[i]) { + int res; + + res = os_snprintf(buf, sizeof(buf), "frame[%u]", i); + if (!os_snprintf_error(sizeof(buf), res)) { + wpa_hexdump_buf(MSG_DEBUG, buf, + wpa_s->vendor_elem[i]); + } + } + } + +#ifdef CONFIG_P2P + if (wpa_s->parent == wpa_s && + wpa_s->global->p2p && + !wpa_s->global->p2p_disabled) + p2p_set_vendor_elems(wpa_s->global->p2p, wpa_s->vendor_elem); +#endif /* CONFIG_P2P */ +} + + +int wpas_vendor_elem_remove(struct wpa_supplicant *wpa_s, int frame, + const u8 *elem, size_t len) +{ + u8 *ie, *end; + + ie = wpabuf_mhead_u8(wpa_s->vendor_elem[frame]); + end = ie + wpabuf_len(wpa_s->vendor_elem[frame]); + + for (; ie + 1 < end; ie += 2 + ie[1]) { + if (ie + len > end) + break; + if (os_memcmp(ie, elem, len) != 0) + continue; + + if (wpabuf_len(wpa_s->vendor_elem[frame]) == len) { + wpabuf_free(wpa_s->vendor_elem[frame]); + wpa_s->vendor_elem[frame] = NULL; + } else { + os_memmove(ie, ie + len, end - (ie + len)); + wpa_s->vendor_elem[frame]->used -= len; + } + wpas_vendor_elem_update(wpa_s); + return 0; + } + + return -1; +} + + +struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes, + u16 num_modes, enum hostapd_hw_mode mode) +{ + u16 i; + + for (i = 0; i < num_modes; i++) { + if (modes[i].mode == mode) + return &modes[i]; + } + + return NULL; +} + + +static struct +wpa_bss_tmp_disallowed * wpas_get_disallowed_bss(struct wpa_supplicant *wpa_s, + const u8 *bssid) +{ + struct wpa_bss_tmp_disallowed *bss; + + dl_list_for_each(bss, &wpa_s->bss_tmp_disallowed, + struct wpa_bss_tmp_disallowed, list) { + if (os_memcmp(bssid, bss->bssid, ETH_ALEN) == 0) + return bss; + } + + return NULL; +} + + +void wpa_bss_tmp_disallow(struct wpa_supplicant *wpa_s, const u8 *bssid, + unsigned int sec) +{ + struct wpa_bss_tmp_disallowed *bss; + struct os_reltime until; + + os_get_reltime(&until); + until.sec += sec; + + bss = wpas_get_disallowed_bss(wpa_s, bssid); + if (bss) { + bss->disallowed_until = until; + return; + } + + bss = os_malloc(sizeof(*bss)); + if (!bss) { + wpa_printf(MSG_DEBUG, + "Failed to allocate memory for temp disallow BSS"); + return; + } + + bss->disallowed_until = until; + os_memcpy(bss->bssid, bssid, ETH_ALEN); + dl_list_add(&wpa_s->bss_tmp_disallowed, &bss->list); +} + + +int wpa_is_bss_tmp_disallowed(struct wpa_supplicant *wpa_s, const u8 *bssid) +{ + struct wpa_bss_tmp_disallowed *bss = NULL, *tmp, *prev; + struct os_reltime now, age; + + os_get_reltime(&now); + + dl_list_for_each_safe(tmp, prev, &wpa_s->bss_tmp_disallowed, + struct wpa_bss_tmp_disallowed, list) { + if (!os_reltime_before(&now, &tmp->disallowed_until)) { + /* This BSS is not disallowed anymore */ + dl_list_del(&tmp->list); + os_free(tmp); + continue; + } + if (os_memcmp(bssid, tmp->bssid, ETH_ALEN) == 0) { + bss = tmp; + break; + } + } + if (!bss) + return 0; + + os_reltime_sub(&bss->disallowed_until, &now, &age); + wpa_printf(MSG_DEBUG, + "BSS " MACSTR " disabled for %ld.%0ld seconds", + MAC2STR(bss->bssid), age.sec, age.usec); + return 1; +} diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant.conf b/contrib/wpa/wpa_supplicant/wpa_supplicant.conf index 68c04768752b..b2e49d8ae325 100644 --- a/contrib/wpa/wpa_supplicant/wpa_supplicant.conf +++ b/contrib/wpa/wpa_supplicant/wpa_supplicant.conf @@ -106,6 +106,25 @@ eapol_version=1 # networks are found, a new IBSS or AP mode network is created. ap_scan=1 +# Whether to force passive scan for network connection +# +# By default, scans will send out Probe Request frames on channels that allow +# active scanning. This advertise the local station to the world. Normally this +# is fine, but users may wish to do passive scanning where the radio should only +# listen quietly for Beacon frames and not send any Probe Request frames. Actual +# functionality may be driver dependent. +# +# This parameter can be used to force only passive scanning to be used +# for network connection cases. It should be noted that this will slow +# down scan operations and reduce likelihood of finding the AP. In +# addition, some use cases will override this due to functional +# requirements, e.g., for finding an AP that uses hidden SSID +# (scan_ssid=1) or P2P device discovery. +# +# 0: Do normal scans (allow active scans) (default) +# 1: Do passive scans. +#passive_scan=0 + # MPM residency # By default, wpa_supplicant implements the mesh peering manager (MPM) for an # open mesh. However, if the driver can implement the MPM, you may set this to @@ -137,10 +156,13 @@ ap_scan=1 fast_reauth=1 # OpenSSL Engine support -# These options can be used to load OpenSSL engines. +# These options can be used to load OpenSSL engines in special or legacy +# modes. # The two engines that are supported currently are shown below: # They are both from the opensc project (http://www.opensc.org/) -# By default no engines are loaded. +# By default the PKCS#11 engine is loaded if the client_cert or +# private_key option appear to be a PKCS#11 URI, and these options +# should not need to be used explicitly. # make the opensc engine available #opensc_engine_path=/usr/lib/opensc/engine_opensc.so # make the pkcs11 engine available @@ -166,7 +188,7 @@ fast_reauth=1 #load_dynamic_eap=/usr/lib/wpa_supplicant/eap_md5.so # Driver interface parameters -# This field can be used to configure arbitrary driver interace parameters. The +# This field can be used to configure arbitrary driver interface parameters. The # format is specific to the selected driver interface. This field is not used # in most cases. #driver_param="field=value" @@ -283,7 +305,9 @@ fast_reauth=1 # up to the limit of 300 seconds (3, 9, 27 ... 300) # For periodic module, parameters would be #autoscan=periodic:30 -# So a delay of 30 seconds will be applied between each scan +# So a delay of 30 seconds will be applied between each scan. +# Note: If sched_scan_plans are configured and supported by the driver, +# autoscan is ignored. # filter_ssids - SSID-based scan result filtering # 0 = do not filter scan results (default) @@ -327,10 +351,12 @@ fast_reauth=1 # Protected Management Frames default # This parameter can be used to set the default behavior for the ieee80211w -# parameter. By default, PMF is disabled unless enabled with the global pmf=1/2 -# parameter or with the per-network ieee80211w=1/2 parameter. With pmf=1/2, PMF -# is enabled/required by default, but can be disabled with the per-network -# ieee80211w parameter. +# parameter for RSN networks. By default, PMF is disabled unless enabled with +# the global pmf=1/2 parameter or with the per-network ieee80211w=1/2 parameter. +# With pmf=1/2, PMF is enabled/required by default, but can be disabled with the +# per-network ieee80211w parameter. This global default value does not apply +# for non-RSN networks (key_mgmt=NONE) since PMF is available only when using +# RSN. #pmf=0 # Enabled SAE finite cyclic groups in preference order @@ -405,6 +431,28 @@ fast_reauth=1 # matching network block #auto_interworking=0 +# GAS Address3 field behavior +# 0 = P2P specification (Address3 = AP BSSID); default +# 1 = IEEE 802.11 standard compliant (Address3 = Wildcard BSSID when +# sent to not-associated AP; if associated, AP BSSID) +#gas_address3=0 + +# Publish fine timing measurement (FTM) responder functionality in +# the Extended Capabilities element bit 70. +# Controls whether FTM responder functionality will be published by AP/STA. +# Note that actual FTM responder operation is managed outside wpa_supplicant. +# 0 = Do not publish; default +# 1 = Publish +#ftm_responder=0 + +# Publish fine timing measurement (FTM) initiator functionality in +# the Extended Capabilities element bit 71. +# Controls whether FTM initiator functionality will be published by AP/STA. +# Note that actual FTM initiator operation is managed outside wpa_supplicant. +# 0 = Do not publish; default +# 1 = Publish +#ftm_initiator=0 + # credential block # # Each credential used for automatic network selection is configured as a set @@ -439,6 +487,10 @@ fast_reauth=1 # (EAP-TLS). Full path to the file should be used since working # directory may change when wpa_supplicant is run in the background. # +# Certificates from PKCS#11 tokens can be referenced by a PKCS#11 URI. +# +# For example: private_key="pkcs11:manufacturer=piv_II;id=%01" +# # Alternatively, a named configuration blob can be used by setting # this to blob://blob_name. # @@ -449,6 +501,9 @@ fast_reauth=1 # used since working directory may change when wpa_supplicant is run # in the background. # +# Keys in PKCS#11 tokens can be referenced by a PKCS#11 URI. +# For example: private_key="pkcs11:manufacturer=piv_II;id=%01" +# # Windows certificate store can be used by leaving client_cert out and # configuring private_key in one of the following formats: # @@ -553,6 +608,8 @@ fast_reauth=1 # 0 = do not use OCSP stapling (TLS certificate status extension) # 1 = try to use OCSP stapling, but not require response # 2 = require valid OCSP stapling response +# 3 = require valid OCSP stapling response for all not-trusted +# certificates in the server certificate chain # # sim_num: Identifier for which SIM to use in multi-SIM devices # @@ -585,6 +642,41 @@ fast_reauth=1 # Hotspot 2.0 # hs20=1 +# Scheduled scan plans +# +# A space delimited list of scan plans. Each scan plan specifies the scan +# interval and number of iterations, delimited by a colon. The last scan plan +# will run infinitely and thus must specify only the interval and not the number +# of iterations. +# +# The driver advertises the maximum number of scan plans supported. If more scan +# plans than supported are configured, only the first ones are set (up to the +# maximum supported). The last scan plan that specifies only the interval is +# always set as the last plan. +# +# If the scan interval or the number of iterations for a scan plan exceeds the +# maximum supported, it will be set to the maximum supported value. +# +# Format: +# sched_scan_plans= ... +# +# Example: +# sched_scan_plans=10:100 20:200 30 + +# Multi Band Operation (MBO) non-preferred channels +# A space delimited list of non-preferred channels where each channel is a colon +# delimited list of values. +# Format: +# non_pref_chan=::: +# Example: +# non_pref_chan="81:5:10:2 81:1:0:2 81:9:0:2" + +# MBO Cellular Data Capabilities +# 1 = Cellular data connection available +# 2 = Cellular data connection not available +# 3 = Not cellular capable (default) +#mbo_cell_capa=3 + # network block # # Each network (usually AP's sharing the same SSID) is configured as a separate @@ -646,6 +738,17 @@ fast_reauth=1 # an IBSS network with the configured SSID is already present, the frequency of # the network will be used instead of this configured value. # +# pbss: Whether to use PBSS. Relevant to IEEE 802.11ad networks only. +# 0 = do not use PBSS +# 1 = use PBSS +# 2 = don't care (not allowed in AP mode) +# Used together with mode configuration. When mode is AP, it means to start a +# PCP instead of a regular AP. When mode is infrastructure it means connect +# to a PCP instead of AP. In this mode you can also specify 2 (don't care) +# which means connect to either PCP or AP. +# P2P_GO and P2P_GROUP_FORMATION modes must use PBSS in IEEE 802.11ad network. +# For more details, see IEEE Std 802.11ad-2012. +# # scan_freq: List of frequencies to scan # Space-separated list of frequencies in MHz to scan when searching for this # BSS. If the subset of channels used by the network is known, this option can @@ -694,8 +797,19 @@ fast_reauth=1 # IEEE8021X = IEEE 802.1X using EAP authentication and (optionally) dynamically # generated WEP keys # NONE = WPA is not used; plaintext or static WEP could be used +# WPA-NONE = WPA-None for IBSS (deprecated; use proto=RSN key_mgmt=WPA-PSK +# instead) +# FT-PSK = Fast BSS Transition (IEEE 802.11r) with pre-shared key +# FT-EAP = Fast BSS Transition (IEEE 802.11r) with EAP authentication # WPA-PSK-SHA256 = Like WPA-PSK but using stronger SHA256-based algorithms # WPA-EAP-SHA256 = Like WPA-EAP but using stronger SHA256-based algorithms +# SAE = Simultaneous authentication of equals; pre-shared key/password -based +# authentication with stronger security than WPA-PSK especially when using +# not that strong password +# FT-SAE = SAE with FT +# WPA-EAP-SUITE-B = Suite B 128-bit level +# WPA-EAP-SUITE-B-192 = Suite B 192-bit level +# OSEN = Hotspot 2.0 Rel 2 online signup connection # If not set, this defaults to: WPA-PSK WPA-EAP # # ieee80211w: whether management frame protection is enabled @@ -786,9 +900,13 @@ fast_reauth=1 # wpa_ptk_rekey: Maximum lifetime for PTK in seconds. This can be used to # enforce rekeying of PTK to mitigate some attacks against TKIP deficiencies. # +# group_rekey: Group rekeying time in seconds. This value, if non-zero, is used +# as the dot11RSNAConfigGroupRekeyTime parameter when operating in +# Authenticator role in IBSS. +# # Following fields are only used with internal EAP implementation. # eap: space-separated list of accepted EAP methods -# MD5 = EAP-MD5 (unsecure and does not generate keying material -> +# MD5 = EAP-MD5 (insecure and does not generate keying material -> # cannot be used with WPA; to be used as a Phase 2 method # with EAP-PEAP or EAP-TTLS) # MSCHAPV2 = EAP-MSCHAPv2 (cannot be used separately with WPA; to be used @@ -879,23 +997,23 @@ fast_reauth=1 # automatically converted into DH params. # subject_match: Substring to be matched against the subject of the # authentication server certificate. If this string is set, the server -# sertificate is only accepted if it contains this string in the subject. +# certificate is only accepted if it contains this string in the subject. # The subject string is in following format: # /C=US/ST=CA/L=San Francisco/CN=Test AS/emailAddress=as@example.com -# Note: Since this is a substring match, this cannot be used securily to +# Note: Since this is a substring match, this cannot be used securely to # do a suffix match against a possible domain name in the CN entry. For # such a use case, domain_suffix_match or domain_match should be used # instead. # altsubject_match: Semicolon separated string of entries to be matched against # the alternative subject name of the authentication server certificate. -# If this string is set, the server sertificate is only accepted if it +# If this string is set, the server certificate is only accepted if it # contains one of the entries in an alternative subject name extension. # altSubjectName string is in following format: TYPE:VALUE # Example: EMAIL:server@example.com # Example: DNS:server.example.com;DNS:server2.example.com # Following types are supported: EMAIL, DNS, URI # domain_suffix_match: Constraint for server domain name. If set, this FQDN is -# used as a suffix match requirement for the AAAserver certificate in +# used as a suffix match requirement for the AAA 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. @@ -983,6 +1101,12 @@ fast_reauth=1 # that have issues interoperating with updated TLS version) # tls_disable_tlsv1_2=1 - disable use of TLSv1.2 (a workaround for AAA servers # that have issues interoperating with updated TLS version) +# tls_ext_cert_check=0 - No external server certificate validation (default) +# tls_ext_cert_check=1 - External server certificate validation enabled; this +# requires an external program doing validation of server certificate +# chain when receiving CTRL-RSP-EXT_CERT_CHECK event from the control +# interface and report the result of the validation with +# CTRL-RSP_EXT_CERT_CHECK. # # Following certificate/private key fields are used in inner Phase2 # authentication when using EAP-TTLS or EAP-PEAP. @@ -1014,6 +1138,8 @@ fast_reauth=1 # 0 = do not use OCSP stapling (TLS certificate status extension) # 1 = try to use OCSP stapling, but not require response # 2 = require valid OCSP stapling response +# 3 = require valid OCSP stapling response for all not-trusted +# certificates in the server certificate chain # # openssl_ciphers: OpenSSL specific cipher configuration # This can be used to override the global openssl_ciphers configuration @@ -1047,6 +1173,9 @@ fast_reauth=1 # number of authentication servers. Strict EAP conformance mode can be # configured by disabling workarounds with eap_workaround=0. +# update_identifier: PPS MO ID +# (Hotspot 2.0 PerProviderSubscription/UpdateIdentifier) + # Station inactivity limit # # If a station does not send anything in ap_max_inactivity seconds, an @@ -1070,6 +1199,11 @@ fast_reauth=1 # Beacon interval (default: 100 TU) #beacon_int=100 +# WPS in AP mode +# 0 = WPS enabled and configured (default) +# 1 = WPS disabled +#wps_disabled=0 + # MAC address policy # 0 = use permanent MAC address # 1 = use random MAC address for each ESS connection @@ -1132,13 +1266,13 @@ fast_reauth=1 ##### Fast Session Transfer (FST) support ##################################### # # The options in this section are only available when the build configuration -# option CONFIG_FST is set while compiling hostapd. They allow this interface -# to be a part of FST setup. +# option CONFIG_FST is set while compiling wpa_supplicant. They allow this +# interface to be a part of FST setup. # # FST is the transfer of a session from a channel to another channel, in the # same or different frequency bands. # -# For detals, see IEEE Std 802.11ad-2012. +# For details, see IEEE Std 802.11ad-2012. # Identifier of an FST Group the interface belongs to. #fst_group_id=bond0 @@ -1471,22 +1605,10 @@ network={ group=CCMP TKIP identity="user@example.com" ca_cert="/etc/cert/ca.pem" - client_cert="/etc/cert/user.pem" - engine=1 - - # The engine configured here must be available. Look at - # OpenSSL engine support in the global section. - # The key available through the engine must be the private key - # matching the client certificate configured above. - - # use the opensc engine - #engine_id="opensc" - #key_id="45" - - # use the pkcs11 engine - engine_id="pkcs11" - key_id="id_45" + # Certificate and/or key identified by PKCS#11 URI (RFC7512) + client_cert="pkcs11:manufacturer=piv_II;id=%01" + private_key="pkcs11:manufacturer=piv_II;id=%01" # Optional PIN configuration; this can be left out and PIN will be # asked through the control interface diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h b/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h index 318768bc2776..ef9273d09a32 100644 --- a/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h +++ b/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h @@ -44,6 +44,7 @@ struct wpa_driver_associate_params; struct ctrl_iface_priv; struct ctrl_iface_global_priv; struct wpas_dbus_priv; +struct wpas_binder_priv; /** * struct wpa_interface - Parameters for wpa_supplicant_add_iface() @@ -228,6 +229,17 @@ struct wpa_params { char *conf_p2p_dev; #endif /* CONFIG_P2P */ +#ifdef CONFIG_MATCH_IFACE + /** + * match_ifaces - Interface descriptions to match + */ + struct wpa_interface *match_ifaces; + + /** + * match_iface_count - Number of defined matching interfaces + */ + int match_iface_count; +#endif /* CONFIG_MATCH_IFACE */ }; struct p2p_srv_bonjour { @@ -253,6 +265,7 @@ struct wpa_global { struct wpa_params params; struct ctrl_iface_global_priv *ctrl_iface; struct wpas_dbus_priv *dbus; + struct wpas_binder_priv *binder; void **drv_priv; size_t drv_count; struct os_time suspend_time; @@ -278,6 +291,7 @@ struct wpa_global { unsigned int p2p_24ghz_social_channels:1; unsigned int pending_p2ps_group:1; unsigned int pending_group_iface_for_p2ps:1; + unsigned int pending_p2ps_group_freq; #ifdef CONFIG_WIFI_DISPLAY int wifi_display; @@ -300,10 +314,14 @@ struct wpa_radio { char name[16]; /* from driver_ops get_radio_name() or empty if not * available */ unsigned int external_scan_running:1; + unsigned int num_active_works; struct dl_list ifaces; /* struct wpa_supplicant::radio_list entries */ struct dl_list work; /* struct wpa_radio_work::list entries */ }; +#define MAX_ACTIVE_WORKS 2 + + /** * struct wpa_radio_work - Radio work item */ @@ -316,6 +334,7 @@ struct wpa_radio_work { void *ctx; unsigned int started:1; struct os_reltime time; + unsigned int bands; }; int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq, @@ -347,6 +366,9 @@ struct wpa_external_work { unsigned int timeout; }; +enum wpa_radio_work_band wpas_freq_to_band(int freq); +unsigned int wpas_get_bands(struct wpa_supplicant *wpa_s, const int *freqs); + /** * offchannel_send_action_result - Result of offchannel send Action frame */ @@ -371,11 +393,6 @@ struct wps_ap_info { u8 uuid[WPS_UUID_LEN]; }; -struct wpa_ssid_value { - u8 ssid[SSID_MAX_LEN]; - size_t ssid_len; -}; - #define WPA_FREQ_USED_BY_INFRA_STATION BIT(0) #define WPA_FREQ_USED_BY_P2P_CLIENT BIT(1) @@ -414,6 +431,21 @@ enum wpa_supplicant_test_failure { WPAS_TEST_FAILURE_SCAN_TRIGGER, }; +struct icon_entry { + struct dl_list list; + u8 bssid[ETH_ALEN]; + u8 dialog_token; + char *file_name; + u8 *image; + size_t image_len; +}; + +struct wpa_bss_tmp_disallowed { + struct dl_list list; + u8 bssid[ETH_ALEN]; + struct os_reltime disallowed_until; +}; + /** * struct wpa_supplicant - Internal data for wpa_supplicant interface * @@ -427,12 +459,16 @@ struct wpa_supplicant { struct wpa_radio *radio; /* shared radio context */ struct dl_list radio_list; /* list head: struct wpa_radio::ifaces */ struct wpa_supplicant *parent; + struct wpa_supplicant *p2pdev; struct wpa_supplicant *next; struct l2_packet_data *l2; struct l2_packet_data *l2_br; unsigned char own_addr[ETH_ALEN]; unsigned char perm_addr[ETH_ALEN]; char ifname[100]; +#ifdef CONFIG_MATCH_IFACE + int matched; +#endif /* CONFIG_MATCH_IFACE */ #ifdef CONFIG_CTRL_IFACE_DBUS char *dbus_path; #endif /* CONFIG_CTRL_IFACE_DBUS */ @@ -443,6 +479,9 @@ struct wpa_supplicant { char *preq_notify_peer; #endif /* CONFIG_AP */ #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ +#ifdef CONFIG_CTRL_IFACE_BINDER + const void *binder_object_key; +#endif /* CONFIG_CTRL_IFACE_BINDER */ char bridge_ifname[16]; char *confname; @@ -455,7 +494,8 @@ struct wpa_supplicant { u8 pending_bssid[ETH_ALEN]; /* If wpa_state == WPA_ASSOCIATING, this * field contains the target BSSID. */ int reassociate; /* reassociation requested */ - int reassoc_same_bss; /* reassociating to the same bss */ + unsigned int reassoc_same_bss:1; /* reassociating to the same BSS */ + unsigned int reassoc_same_ess:1; /* reassociating to the same ESS */ int disconnected; /* all connections disabled; i.e., do no reassociate * before this has been cleared */ struct wpa_ssid *current_ssid; @@ -500,9 +540,10 @@ struct wpa_supplicant { struct wpa_ssid *prev_sched_ssid; /* last SSID used in sched scan */ int sched_scan_timeout; - int sched_scan_interval; int first_sched_scan; int sched_scan_timed_out; + struct sched_scan_plan *sched_scan_plans; + size_t sched_scan_plans_num; void (*scan_res_handler)(struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res); @@ -533,6 +574,7 @@ struct wpa_supplicant { struct wpa_radio_work *scan_work; int scanning; int sched_scanning; + unsigned int sched_scan_stop_req:1; int new_connection; int eapol_received; /* number of EAPOL packets received after the @@ -613,6 +655,7 @@ struct wpa_supplicant { #define MAX_SCAN_ID 16 int scan_id[MAX_SCAN_ID]; unsigned int scan_id_count; + u8 next_scan_bssid[ETH_ALEN]; struct wpa_ssid_value *ssids_from_scan_req; unsigned int num_ssids_from_scan_req; @@ -634,6 +677,9 @@ struct wpa_supplicant { int max_scan_ssids; int max_sched_scan_ssids; + unsigned int max_sched_scan_plans; + unsigned int max_sched_scan_plan_interval; + unsigned int max_sched_scan_plan_iterations; int sched_scan_supported; unsigned int max_match_sets; unsigned int max_remain_on_chan; @@ -723,7 +769,7 @@ struct wpa_supplicant { int mesh_if_idx; unsigned int mesh_if_created:1; unsigned int mesh_ht_enabled:1; - int mesh_auth_block_duration; /* sec */ + unsigned int mesh_vht_enabled:1; #endif /* CONFIG_MESH */ unsigned int off_channel_freq; @@ -845,6 +891,10 @@ struct wpa_supplicant { int *p2p_group_common_freqs; unsigned int p2p_group_common_freqs_num; u8 p2ps_join_addr[ETH_ALEN]; + + unsigned int p2p_go_max_oper_chwidth; + unsigned int p2p_go_vht_center_freq2; + int p2p_lo_started; #endif /* CONFIG_P2P */ struct wpa_ssid *bgscan_ssid; @@ -886,6 +936,7 @@ struct wpa_supplicant { unsigned int fetch_osu_icon_in_progress:1; struct wpa_bss *interworking_gas_bss; unsigned int osu_icon_id; + struct dl_list icon_head; /* struct icon_entry */ struct osu_provider *osu_prov; size_t osu_prov_count; struct os_reltime osu_icon_fetch_start; @@ -915,6 +966,9 @@ struct wpa_supplicant { /* WLAN_REASON_* reason codes. Negative if locally generated. */ int disconnect_reason; + /* WLAN_STATUS_* status codes from (Re)Association Response frame. */ + u16 assoc_status_code; + struct ext_password_data *ext_pw; struct wpabuf *last_gas_resp, *prev_gas_resp; @@ -970,6 +1024,10 @@ struct wpa_supplicant { struct l2_packet_data *l2_test; unsigned int extra_roc_dur; enum wpa_supplicant_test_failure test_failure; + unsigned int reject_btm_req_reason; + unsigned int p2p_go_csa_on_inv:1; + unsigned int ignore_auth_resp:1; + unsigned int ignore_assoc_disallow:1; #endif /* CONFIG_TESTING_OPTIONS */ struct wmm_ac_assoc_data *wmm_ac_assoc_info; @@ -986,6 +1044,31 @@ struct wpa_supplicant { const struct wpabuf *fst_ies; struct wpabuf *received_mb_ies; #endif /* CONFIG_FST */ + +#ifdef CONFIG_MBO + /* Multiband operation non-preferred channel */ + struct wpa_mbo_non_pref_channel { + enum mbo_non_pref_chan_reason reason; + u8 oper_class; + u8 chan; + u8 preference; + } *non_pref_chan; + size_t non_pref_chan_num; + u8 mbo_wnm_token; +#endif /* CONFIG_MBO */ + + /* + * This should be under CONFIG_MBO, but it is left out to allow using + * the bss_temp_disallowed list for other purposes as well. + */ + struct dl_list bss_tmp_disallowed; + + /* + * Content of a measurement report element with type 8 (LCI), + * own location. + */ + struct wpabuf *lci; + struct os_reltime lci_time; }; @@ -1027,6 +1110,8 @@ void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s); void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s, int reason_code); +struct wpa_ssid * wpa_supplicant_add_network(struct wpa_supplicant *wpa_s); +int wpa_supplicant_remove_network(struct wpa_supplicant *wpa_s, int id); void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); void wpa_supplicant_disable_network(struct wpa_supplicant *wpa_s, @@ -1051,6 +1136,8 @@ void free_hw_features(struct wpa_supplicant *wpa_s); void wpa_show_license(void); +struct wpa_interface * wpa_supplicant_match_iface(struct wpa_global *global, + const char *ifname); struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global, struct wpa_interface *iface, struct wpa_supplicant *parent); @@ -1080,6 +1167,7 @@ int disallowed_bssid(struct wpa_supplicant *wpa_s, const u8 *bssid); int disallowed_ssid(struct wpa_supplicant *wpa_s, const u8 *ssid, size_t ssid_len); void wpas_request_connection(struct wpa_supplicant *wpa_s); +void wpas_request_disconnection(struct wpa_supplicant *wpa_s); int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen); int wpas_update_random_addr(struct wpa_supplicant *wpa_s, int style); int wpas_update_random_addr_disassoc(struct wpa_supplicant *wpa_s); @@ -1089,15 +1177,37 @@ void wpas_rrm_reset(struct wpa_supplicant *wpa_s); void wpas_rrm_process_neighbor_rep(struct wpa_supplicant *wpa_s, const u8 *report, size_t report_len); int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s, - const struct wpa_ssid *ssid, + const struct wpa_ssid_value *ssid, + int lci, int civic, void (*cb)(void *ctx, struct wpabuf *neighbor_rep), void *cb_ctx); +void wpas_rrm_handle_radio_measurement_request(struct wpa_supplicant *wpa_s, + const u8 *src, + const u8 *frame, size_t len); void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s, const u8 *src, const u8 *frame, size_t len, int rssi); + +/* MBO functions */ +int wpas_mbo_ie(struct wpa_supplicant *wpa_s, u8 *buf, size_t len); +const u8 * wpas_mbo_get_bss_attr(struct wpa_bss *bss, enum mbo_attr_id attr); +int wpas_mbo_update_non_pref_chan(struct wpa_supplicant *wpa_s, + const char *non_pref_chan); +void wpas_mbo_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ie); +int wpas_mbo_supp_op_class_ie(struct wpa_supplicant *wpa_s, int freq, u8 *pos, + size_t len); +void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *ie, + size_t len); +size_t wpas_mbo_ie_bss_trans_reject(struct wpa_supplicant *wpa_s, u8 *pos, + size_t len, + enum mbo_transition_reject_reason reason); +void wpas_mbo_update_cell_capa(struct wpa_supplicant *wpa_s, u8 mbo_cell_capa); +struct wpabuf * mbo_build_anqp_buf(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss); + /** * wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response * @wpa_s: Pointer to wpa_supplicant data @@ -1159,6 +1269,12 @@ int get_shared_radio_freqs(struct wpa_supplicant *wpa_s, void wpas_network_reenabled(void *eloop_ctx, void *timeout_ctx); +void wpas_vendor_elem_update(struct wpa_supplicant *wpa_s); +struct wpa_supplicant * wpas_vendor_elem(struct wpa_supplicant *wpa_s, + enum wpa_vendor_elem_frame frame); +int wpas_vendor_elem_remove(struct wpa_supplicant *wpa_s, int frame, + const u8 *elem, size_t len); + #ifdef CONFIG_FST struct fst_wpa_obj; @@ -1168,4 +1284,18 @@ void fst_wpa_supplicant_fill_iface_obj(struct wpa_supplicant *wpa_s, #endif /* CONFIG_FST */ +int wpas_sched_scan_plans_set(struct wpa_supplicant *wpa_s, const char *cmd); + +struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes, + u16 num_modes, enum hostapd_hw_mode mode); + +void wpa_bss_tmp_disallow(struct wpa_supplicant *wpa_s, const u8 *bssid, + unsigned int sec); +int wpa_is_bss_tmp_disallowed(struct wpa_supplicant *wpa_s, const u8 *bssid); + +struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, + int i, struct wpa_bss *bss, + struct wpa_ssid *group, + int only_first_ssid); + #endif /* WPA_SUPPLICANT_I_H */ diff --git a/contrib/wpa/wpa_supplicant/wpas_glue.c b/contrib/wpa/wpa_supplicant/wpas_glue.c index 29c22ba2c967..f84c8b90ac2f 100644 --- a/contrib/wpa/wpa_supplicant/wpas_glue.c +++ b/contrib/wpa/wpa_supplicant/wpas_glue.c @@ -739,6 +739,8 @@ enum wpa_ctrl_req_type wpa_supplicant_ctrl_req_from_string(const char *field) return WPA_CTRL_REQ_SIM; else if (os_strcmp(field, "PSK_PASSPHRASE") == 0) return WPA_CTRL_REQ_PSK_PASSPHRASE; + else if (os_strcmp(field, "EXT_CERT_CHECK") == 0) + return WPA_CTRL_REQ_EXT_CERT_CHECK; return WPA_CTRL_REQ_UNKNOWN; } @@ -782,6 +784,10 @@ const char * wpa_supplicant_ctrl_req_to_string(enum wpa_ctrl_req_type field, *txt = "PSK or passphrase"; ret = "PSK_PASSPHRASE"; break; + case WPA_CTRL_REQ_EXT_CERT_CHECK: + *txt = "External server certificate validation"; + ret = "EXT_CERT_CHECK"; + break; default: break; } @@ -837,6 +843,8 @@ static void wpa_supplicant_eap_param_needed(void *ctx, if (ssid == NULL) return; + if (field == WPA_CTRL_REQ_EXT_CERT_CHECK) + ssid->eap.pending_ext_cert_check = PENDING_CHECK; wpas_notify_network_request(wpa_s, ssid, field, default_txt); field_name = wpa_supplicant_ctrl_req_to_string(field, default_txt, @@ -1013,7 +1021,6 @@ static void wpa_supplicant_set_rekey_offload(void *ctx, wpa_drv_set_rekey_info(wpa_s, kek, kek_len, kck, kck_len, replay_ctr); } -#endif /* CONFIG_NO_WPA */ static int wpa_supplicant_key_mgmt_set_pmk(void *ctx, const u8 *pmk, @@ -1028,6 +1035,7 @@ static int wpa_supplicant_key_mgmt_set_pmk(void *ctx, const u8 *pmk, else return 0; } +#endif /* CONFIG_NO_WPA */ int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s) @@ -1124,6 +1132,7 @@ void wpa_supplicant_rsn_supp_set_config(struct wpa_supplicant *wpa_s, } } #endif /* CONFIG_P2P */ + conf.wpa_rsc_relaxation = wpa_s->conf->wpa_rsc_relaxation; } wpa_sm_set_config(wpa_s->wpa, ssid ? &conf : NULL); } diff --git a/contrib/wpa/wpa_supplicant/wpas_kay.c b/contrib/wpa/wpa_supplicant/wpas_kay.c index 354decf98c8b..d6ec8c5090e9 100644 --- a/contrib/wpa/wpa_supplicant/wpas_kay.c +++ b/contrib/wpa/wpa_supplicant/wpas_kay.c @@ -50,10 +50,9 @@ static int wpas_set_replay_protect(void *wpa_s, Boolean enabled, u32 window) } -static int wpas_set_current_cipher_suite(void *wpa_s, const u8 *cs, - size_t cs_len) +static int wpas_set_current_cipher_suite(void *wpa_s, u64 cs) { - return wpa_drv_set_current_cipher_suite(wpa_s, cs, cs_len); + return wpa_drv_set_current_cipher_suite(wpa_s, cs); } @@ -109,7 +108,8 @@ static int wpas_create_receive_sc(void *wpa_s, u32 channel, enum validate_frames vf, enum confidentiality_offset co) { - return wpa_drv_create_receive_sc(wpa_s, channel, sci->addr, sci->port, + return wpa_drv_create_receive_sc(wpa_s, channel, sci->addr, + be_to_host16(sci->port), conf_offset_val(co), vf); } @@ -150,7 +150,8 @@ wpas_create_transmit_sc(void *wpa_s, u32 channel, const struct ieee802_1x_mka_sci *sci, enum confidentiality_offset co) { - return wpa_drv_create_transmit_sc(wpa_s, channel, sci->addr, sci->port, + return wpa_drv_create_transmit_sc(wpa_s, channel, sci->addr, + be_to_host16(sci->port), conf_offset_val(co)); } diff --git a/contrib/wpa/wpa_supplicant/wpas_module_tests.c b/contrib/wpa/wpa_supplicant/wpas_module_tests.c index 6af1678a4dfb..4e37591be36c 100644 --- a/contrib/wpa/wpa_supplicant/wpas_module_tests.c +++ b/contrib/wpa/wpa_supplicant/wpas_module_tests.c @@ -9,6 +9,7 @@ #include "utils/includes.h" #include "utils/common.h" +#include "utils/module_tests.h" #include "wpa_supplicant_i.h" #include "blacklist.h" @@ -79,30 +80,18 @@ int wpas_module_tests(void) ret = -1; #ifdef CONFIG_WPS - { - int wps_module_tests(void); - if (wps_module_tests() < 0) - ret = -1; - } + if (wps_module_tests() < 0) + ret = -1; #endif /* CONFIG_WPS */ - { - int utils_module_tests(void); - if (utils_module_tests() < 0) - ret = -1; - } + if (utils_module_tests() < 0) + ret = -1; - { - int common_module_tests(void); - if (common_module_tests() < 0) - ret = -1; - } + if (common_module_tests() < 0) + ret = -1; - { - int crypto_module_tests(void); - if (crypto_module_tests() < 0) - ret = -1; - } + if (crypto_module_tests() < 0) + ret = -1; return ret; } diff --git a/contrib/wpa/wpa_supplicant/wps_supplicant.c b/contrib/wpa/wpa_supplicant/wps_supplicant.c index 60f761c81b80..74a420c671d0 100644 --- a/contrib/wpa/wpa_supplicant/wps_supplicant.c +++ b/contrib/wpa/wpa_supplicant/wps_supplicant.c @@ -583,8 +583,8 @@ static void wpa_supplicant_wps_event_m2d(struct wpa_supplicant *wpa_s, m2d->dev_password_id, m2d->config_error); wpas_notify_wps_event_m2d(wpa_s, m2d); #ifdef CONFIG_P2P - if (wpa_s->parent && wpa_s->parent != wpa_s) { - wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_M2D + if (wpa_s->p2pdev && wpa_s->p2pdev != wpa_s) { + wpa_msg(wpa_s->p2pdev, MSG_INFO, WPS_EVENT_M2D "dev_password_id=%d config_error=%d", m2d->dev_password_id, m2d->config_error); } @@ -617,8 +617,8 @@ static void wpa_supplicant_wps_event_fail(struct wpa_supplicant *wpa_s, WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)", fail->msg, fail->config_error, fail->error_indication, wps_ei_str(fail->error_indication)); - if (wpa_s->parent && wpa_s->parent != wpa_s) - wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_FAIL + if (wpa_s->p2pdev && wpa_s->p2pdev != wpa_s) + wpa_msg(wpa_s->p2pdev, MSG_INFO, WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)", fail->msg, fail->config_error, fail->error_indication, @@ -627,8 +627,8 @@ static void wpa_supplicant_wps_event_fail(struct wpa_supplicant *wpa_s, wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_FAIL "msg=%d config_error=%d", fail->msg, fail->config_error); - if (wpa_s->parent && wpa_s->parent != wpa_s) - wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_FAIL + if (wpa_s->p2pdev && wpa_s->p2pdev != wpa_s) + wpa_msg(wpa_s->p2pdev, MSG_INFO, WPS_EVENT_FAIL "msg=%d config_error=%d", fail->msg, fail->config_error); } @@ -683,6 +683,13 @@ static void wpas_wps_reenable_networks_cb(void *eloop_ctx, void *timeout_ctx) } +int wpas_wps_reenable_networks_pending(struct wpa_supplicant *wpa_s) +{ + return eloop_is_timeout_registered(wpas_wps_reenable_networks_cb, + wpa_s, NULL); +} + + static void wpa_supplicant_wps_event_success(struct wpa_supplicant *wpa_s) { wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_SUCCESS); @@ -1135,6 +1142,13 @@ int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid, return -1; ssid->temporary = 1; ssid->p2p_group = p2p_group; + /* + * When starting a regular WPS process (not P2P group formation) + * the registrar/final station can be either AP or PCP + * so use a "don't care" value for the pbss flag. + */ + if (!p2p_group) + ssid->pbss = 2; #ifdef CONFIG_P2P if (p2p_group && wpa_s->go_params && wpa_s->go_params->ssid_len) { ssid->ssid = os_zalloc(wpa_s->go_params->ssid_len + 1); @@ -1142,6 +1156,10 @@ int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid, ssid->ssid_len = wpa_s->go_params->ssid_len; os_memcpy(ssid->ssid, wpa_s->go_params->ssid, ssid->ssid_len); + if (wpa_s->go_params->freq > 56160) { + /* P2P in 60 GHz uses PBSS */ + ssid->pbss = 1; + } wpa_hexdump_ascii(MSG_DEBUG, "WPS: Use specific AP " "SSID", ssid->ssid, ssid->ssid_len); } @@ -1186,6 +1204,13 @@ static int wpas_wps_start_dev_pw(struct wpa_supplicant *wpa_s, } ssid->temporary = 1; ssid->p2p_group = p2p_group; + /* + * When starting a regular WPS process (not P2P group formation) + * the registrar/final station can be either AP or PCP + * so use a "don't care" value for the pbss flag. + */ + if (!p2p_group) + ssid->pbss = 2; if (ssid_val) { ssid->ssid = os_malloc(ssid_len); if (ssid->ssid) { @@ -1209,6 +1234,10 @@ static int wpas_wps_start_dev_pw(struct wpa_supplicant *wpa_s, ssid->ssid_len = wpa_s->go_params->ssid_len; os_memcpy(ssid->ssid, wpa_s->go_params->ssid, ssid->ssid_len); + if (wpa_s->go_params->freq > 56160) { + /* P2P in 60 GHz uses PBSS */ + ssid->pbss = 1; + } wpa_hexdump_ascii(MSG_DEBUG, "WPS: Use specific AP " "SSID", ssid->ssid, ssid->ssid_len); } @@ -1221,7 +1250,10 @@ static int wpas_wps_start_dev_pw(struct wpa_supplicant *wpa_s, os_snprintf(val, sizeof(val), "\"dev_pw_id=%u%s\"", dev_pw_id, hash); } else { - rpin = wps_generate_pin(); + if (wps_generate_pin(&rpin) < 0) { + wpa_printf(MSG_DEBUG, "WPS: Could not generate PIN"); + return -1; + } os_snprintf(val, sizeof(val), "\"pin=%08d dev_pw_id=%u%s\"", rpin, dev_pw_id, hash); } diff --git a/contrib/wpa/wpa_supplicant/wps_supplicant.h b/contrib/wpa/wpa_supplicant/wps_supplicant.h index 3c25ca86dc65..c8fe47e37279 100644 --- a/contrib/wpa/wpa_supplicant/wps_supplicant.h +++ b/contrib/wpa/wpa_supplicant/wps_supplicant.h @@ -85,6 +85,7 @@ int wpas_er_wps_nfc_report_handover(struct wpa_supplicant *wpa_s, void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res); void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid); +int wpas_wps_reenable_networks_pending(struct wpa_supplicant *wpa_s); #else /* CONFIG_WPS */ @@ -147,6 +148,12 @@ static inline void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, { } +static inline int +wpas_wps_reenable_networks_pending(struct wpa_supplicant *wpa_s) +{ + return 0; +} + #endif /* CONFIG_WPS */ #endif /* WPS_SUPPLICANT_H */ diff --git a/usr.sbin/wpa/Makefile.inc b/usr.sbin/wpa/Makefile.inc index 2029fd68d33e..ebde81533cef 100644 --- a/usr.sbin/wpa/Makefile.inc +++ b/usr.sbin/wpa/Makefile.inc @@ -34,5 +34,6 @@ CFLAGS+=-I${WPA_DISTDIR}/src/wps CFLAGS+= -DCONFIG_CTRL_IFACE CFLAGS+= -DCONFIG_CTRL_IFACE_UNIX +CFLAGS+= -DNEED_AP_MLME .include diff --git a/usr.sbin/wpa/hostapd/Makefile b/usr.sbin/wpa/hostapd/Makefile index b2feb94d9b34..63200fe72d8b 100644 --- a/usr.sbin/wpa/hostapd/Makefile +++ b/usr.sbin/wpa/hostapd/Makefile @@ -7,19 +7,25 @@ ${WPA_DISTDIR}/src/drivers PROG= hostapd -SRCS= accounting.c aes-omac1.c ap_config.c ap_drv_ops.c ap_mlme.c authsrv.c \ +SRCS= accounting.c aes-omac1.c ap_config.c ap_drv_ops.c ap_list.c \ + ap_mlme.c authsrv.c \ base64.c beacon.c bss_load.c chap.c common.c config_file.c \ ctrl_iface.c \ - ctrl_iface_ap.c driver_common.c l2_packet_freebsd.c driver_bsd.c \ + ctrl_iface_ap.c ctrl_iface_common.c dfs.c \ + driver_common.c l2_packet_freebsd.c driver_bsd.c \ drivers.c drv_callbacks.c eap_common.c eap_peap_common.c \ eap_register.c eap_server.c eap_server_methods.c eap_user_db.c \ eapol_auth_dump.c eapol_auth_sm.c eloop.c gas.c gas_serv.c hostapd.c \ hs20.c http_client.c http_server.c httpread.c \ - hw_features_common.c ieee802_11_auth.c \ - ieee802_11_common.c ieee802_11_shared.c ieee802_1x.c ip_addr.c \ - main.c ms_funcs.c os_unix.c peerkey_auth.c pmksa_cache_auth.c \ - preauth_auth.c radius.c radius_client.c radius_das.c sta_info.c \ - tkip_countermeasures.c upnp_xml.c utils.c uuid.c vlan_init.c \ + hw_features.c hw_features_common.c \ + ieee802_11.c ieee802_11_auth.c ieee802_11_common.c \ + ieee802_11_shared.c ieee802_1x.c \ + ip_addr.c \ + main.c ms_funcs.c neighbor_db.c \ + os_unix.c peerkey_auth.c pmksa_cache_auth.c \ + preauth_auth.c radius.c radius_client.c radius_das.c rrm.c sta_info.c \ + tkip_countermeasures.c upnp_xml.c utils.c uuid.c \ + vlan.c vlan_ifconfig.c vlan_init.c wmm.c \ wpa_auth.c wpa_auth_glue.c wpa_auth_ie.c wpa_common.c wpa_debug.c \ wpabuf.c wps.c wps_attr_build.c wps_attr_parse.c wps_attr_process.c \ wps_common.c wps_dev_attr.c wps_enrollee.c wps_hostapd.c \ diff --git a/usr.sbin/wpa/hostapd_cli/Makefile b/usr.sbin/wpa/hostapd_cli/Makefile index a9c4393df52b..ad652333c753 100644 --- a/usr.sbin/wpa/hostapd_cli/Makefile +++ b/usr.sbin/wpa/hostapd_cli/Makefile @@ -5,7 +5,8 @@ .PATH.c:${HOSTAPD_DISTDIR} PROG= hostapd_cli -SRCS= common.c edit.c eloop.c hostapd_cli.c os_unix.c wpa_ctrl.c wpa_debug.c +SRCS= cli.c common.c edit.c eloop.c hostapd_cli.c os_unix.c \ + wpa_ctrl.c wpa_debug.c CFLAGS+= -DCONFIG_CTRL_IFACE CFLAGS+= -DCONFIG_CTRL_IFACE_UNIX diff --git a/usr.sbin/wpa/wpa_cli/Makefile b/usr.sbin/wpa/wpa_cli/Makefile index 585499dd5ab1..e90d69f21acb 100644 --- a/usr.sbin/wpa/wpa_cli/Makefile +++ b/usr.sbin/wpa/wpa_cli/Makefile @@ -5,7 +5,8 @@ .PATH.c:${WPA_SUPPLICANT_DISTDIR} PROG= wpa_cli -SRCS= common.c edit.c eloop.c os_unix.c wpa_cli.c wpa_ctrl.c wpa_debug.c +SRCS= cli.c common.c edit.c eloop.c os_unix.c wpa_cli.c \ + wpa_ctrl.c wpa_debug.c MAN= wpa_cli.8 diff --git a/usr.sbin/wpa/wpa_supplicant/Makefile b/usr.sbin/wpa/wpa_supplicant/Makefile index a8ccb1652f79..bdb8fa9488bf 100644 --- a/usr.sbin/wpa/wpa_supplicant/Makefile +++ b/usr.sbin/wpa/wpa_supplicant/Makefile @@ -8,20 +8,36 @@ ${WPA_DISTDIR}/src/drivers PROG= wpa_supplicant -SRCS= ap_drv_ops.c base64.c blacklist.c bss.c common.c config.c \ - config_file.c ctrl_iface.c ctrl_iface_unix.c driver_bsd.c \ +SRCS= accounting.c ap_drv_ops.c ap_config.c ap_list.c \ + ap_mlme.c \ + authsrv.c \ + base64.c beacon.c blacklist.c bss.c bss_load.c common.c config.c \ + config_file.c ctrl_iface.c ctrl_iface_common.c \ + ctrl_iface_unix.c dfs.c driver_bsd.c \ driver_common.c driver_ndis.c driver_wired.c drivers.c \ - eap_register.c eloop.c events.c gas.c gas_query.c hs20.c \ + eap_register.c eapol_auth_sm.c eap_server_methods.c eap_server.c \ + eap_user_db.c \ + eloop.c events.c gas.c gas_query.c gas_serv.c hostapd.c hs20.c \ hs20_supplicant.c http_client.c http_server.c httpread.c \ - hw_features_common.c \ - ieee802_11_common.c ieee802_11_shared.c \ - interworking.c l2_packet_freebsd.c main.c \ - notify.c offchannel.c os_unix.c peerkey.c pmksa_cache.c \ - preauth.c scan.c upnp_xml.c uuid.c wmm_ac.c \ - wpa.c wpa_common.c wpa_debug.c \ - wpa_ft.c wpa_ie.c wpa_supplicant.c wpabuf.c wpas_glue.c wps.c \ + hw_features.c hw_features_common.c \ + ieee802_11.c ieee802_11_auth.c ieee802_11_common.c \ + ieee802_11_shared.c ieee802_1x.c \ + interworking.c ip_addr.c l2_packet_freebsd.c main.c \ + neighbor_db.c \ + notify.c offchannel.c os_unix.c peerkey.c peerkey_auth.c \ + pmksa_cache.c \ + pmksa_cache_auth.c \ + preauth.c scan.c radius.c radius_client.c radius_das.c rrm.c \ + sta_info.c \ + tkip_countermeasures.c \ + upnp_xml.c utils.c uuid.c vlan.c vlan_ifconfig.c \ + vlan_init.c wmm.c wmm_ac.c \ + wpa.c wpa_auth.c wpa_auth_ft.c wpa_common.c wpa_debug.c \ + wpa_auth_glue.c wpa_auth_ie.c wpa_ft.c \ + wpa_ie.c wpa_supplicant.c wpabuf.c wpas_glue.c wps.c \ wps_attr_build.c wps_attr_parse.c wps_attr_process.c \ - wps_common.c wps_dev_attr.c wps_enrollee.c wps_registrar.c \ + wps_common.c wps_dev_attr.c wps_enrollee.c wps_hostapd.c \ + wps_registrar.c \ wps_supplicant.c wps_upnp.c wps_upnp_ap.c wps_upnp_event.c \ wps_upnp_ssdp.c wps_upnp_web.c Packet32.c