diff options
277 files changed, 19640 insertions, 6175 deletions
diff --git a/contrib/wpa/hostapd/ChangeLog b/contrib/wpa/hostapd/ChangeLog index e6f8c6a..af54e1e 100644 --- a/contrib/wpa/hostapd/ChangeLog +++ b/contrib/wpa/hostapd/ChangeLog @@ -1,5 +1,41 @@ ChangeLog for hostapd +2015-09-27 - v2.5 + * fixed WPS UPnP vulnerability with HTTP chunked transfer encoding + [http://w1.fi/security/2015-2/] (CVE-2015-4141) + * fixed WMM Action frame parser + [http://w1.fi/security/2015-3/] (CVE-2015-4142) + * fixed EAP-pwd server missing payload length validation + [http://w1.fi/security/2015-4/] + (CVE-2015-4143, CVE-2015-4144, CVE-2015-4145) + * fixed validation of WPS and P2P NFC NDEF record payload length + [http://w1.fi/security/2015-5/] + * nl80211: + - fixed vendor command handling to check OUI properly + * fixed hlr_auc_gw build with OpenSSL + * hlr_auc_gw: allow Milenage RES length to be reduced + * disable HT for a station that does not support WMM/QoS + * added support for hashed password (NtHash) in EAP-pwd server + * fixed and extended dynamic VLAN cases + * added EAP-EKE server support for deriving Session-Id + * set Acct-Session-Id to a random value to make it more likely to be + unique even if the device does not have a proper clock + * added more 2.4 GHz channels for 20/40 MHz HT co-ex scan + * modified SAE routines to be more robust and PWE generation to be + stronger against timing attacks + * added support for Brainpool Elliptic Curves with SAE + * increases maximum value accepted for cwmin/cwmax + * added support for CCMP-256 and GCMP-256 as group ciphers with FT + * added Fast Session Transfer (FST) module + * removed optional fields from RSNE when using FT with PMF + (workaround for interoperability issues with iOS 8.4) + * added EAP server support for TLS session resumption + * fixed key derivation for Suite B 192-bit AKM (this breaks + compatibility with the earlier version) + * added mechanism to track unconnected stations and do minimal band + steering + * number of small fixes + 2015-03-15 - v2.4 * allow OpenSSL cipher configuration to be set for internal EAP server (openssl_ciphers parameter) diff --git a/contrib/wpa/hostapd/config_file.c b/contrib/wpa/hostapd/config_file.c index 53143f7..82ac61d 100644 --- a/contrib/wpa/hostapd/config_file.c +++ b/contrib/wpa/hostapd/config_file.c @@ -222,9 +222,15 @@ static int hostapd_config_read_eap_user(const char *fname, return 0; if (os_strncmp(fname, "sqlite:", 7) == 0) { +#ifdef CONFIG_SQLITE os_free(conf->eap_user_sqlite); conf->eap_user_sqlite = os_strdup(fname + 7); return 0; +#else /* CONFIG_SQLITE */ + wpa_printf(MSG_ERROR, + "EAP user file in SQLite DB, but CONFIG_SQLITE was not enabled in the build."); + return -1; +#endif /* CONFIG_SQLITE */ } f = fopen(fname, "r"); @@ -775,6 +781,24 @@ static int hostapd_config_read_wep(struct hostapd_wep_keys *wep, int keyidx, } +static int hostapd_parse_chanlist(struct hostapd_config *conf, char *val) +{ + char *pos; + + /* for backwards compatibility, translate ' ' in conf str to ',' */ + pos = val; + while (pos) { + pos = os_strchr(pos, ' '); + if (pos) + *pos++ = ','; + } + if (freq_range_list_parse(&conf->acs_ch_list, val)) + return -1; + + return 0; +} + + static int hostapd_parse_intlist(int **int_list, char *val) { int *list; @@ -875,7 +899,9 @@ static int hostapd_config_read_int10(const char *value) static int valid_cw(int cw) { return (cw == 1 || cw == 3 || cw == 7 || cw == 15 || cw == 31 || - cw == 63 || cw == 127 || cw == 255 || cw == 511 || cw == 1023); + cw == 63 || cw == 127 || cw == 255 || cw == 511 || cw == 1023 || + cw == 2047 || cw == 4095 || cw == 8191 || cw == 16383 || + cw == 32767); } @@ -886,11 +912,11 @@ enum { IEEE80211_TX_QUEUE_DATA3 = 3 /* used for EDCA AC_BK data */ }; -static int hostapd_config_tx_queue(struct hostapd_config *conf, char *name, - char *val) +static int hostapd_config_tx_queue(struct hostapd_config *conf, + const char *name, const char *val) { int num; - char *pos; + const char *pos; struct hostapd_tx_queue_params *queue; /* skip 'tx_queue_' prefix */ @@ -1134,13 +1160,23 @@ static int hostapd_config_vht_capab(struct hostapd_config *conf, if (os_strstr(capab, "[BF-ANTENNA-2]") && (conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE)) conf->vht_capab |= (1 << VHT_CAP_BEAMFORMEE_STS_OFFSET); + if (os_strstr(capab, "[BF-ANTENNA-3]") && + (conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE)) + conf->vht_capab |= (2 << VHT_CAP_BEAMFORMEE_STS_OFFSET); + if (os_strstr(capab, "[BF-ANTENNA-4]") && + (conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE)) + conf->vht_capab |= (3 << VHT_CAP_BEAMFORMEE_STS_OFFSET); if (os_strstr(capab, "[SOUNDING-DIMENSION-2]") && (conf->vht_capab & VHT_CAP_SU_BEAMFORMER_CAPABLE)) conf->vht_capab |= (1 << VHT_CAP_SOUNDING_DIMENSION_OFFSET); + if (os_strstr(capab, "[SOUNDING-DIMENSION-3]") && + (conf->vht_capab & VHT_CAP_SU_BEAMFORMER_CAPABLE)) + conf->vht_capab |= (2 << VHT_CAP_SOUNDING_DIMENSION_OFFSET); + if (os_strstr(capab, "[SOUNDING-DIMENSION-4]") && + (conf->vht_capab & VHT_CAP_SU_BEAMFORMER_CAPABLE)) + conf->vht_capab |= (3 << VHT_CAP_SOUNDING_DIMENSION_OFFSET); if (os_strstr(capab, "[MU-BEAMFORMER]")) conf->vht_capab |= VHT_CAP_MU_BEAMFORMER_CAPABLE; - if (os_strstr(capab, "[MU-BEAMFORMEE]")) - conf->vht_capab |= VHT_CAP_MU_BEAMFORMEE_CAPABLE; if (os_strstr(capab, "[VHT-TXOP-PS]")) conf->vht_capab |= VHT_CAP_VHT_TXOP_PS; if (os_strstr(capab, "[HTC-VHT]")) @@ -1699,7 +1735,7 @@ static int hs20_parse_osu_ssid(struct hostapd_bss_config *bss, char *str; str = wpa_config_parse_string(pos, &slen); - if (str == NULL || slen < 1 || slen > HOSTAPD_MAX_SSID_LEN) { + if (str == NULL || slen < 1 || slen > SSID_MAX_LEN) { wpa_printf(MSG_ERROR, "Line %d: Invalid SSID '%s'", line, pos); os_free(str); return -1; @@ -1900,7 +1936,7 @@ fail: static int hostapd_config_fill(struct hostapd_config *conf, struct hostapd_bss_config *bss, - char *buf, char *pos, int line) + const char *buf, char *pos, int line) { if (os_strcmp(buf, "interface") == 0) { os_strlcpy(conf->bss[0]->iface, pos, @@ -1946,7 +1982,7 @@ static int hostapd_config_fill(struct hostapd_config *conf, line); } else if (os_strcmp(buf, "ssid") == 0) { bss->ssid.ssid_len = os_strlen(pos); - if (bss->ssid.ssid_len > HOSTAPD_MAX_SSID_LEN || + if (bss->ssid.ssid_len > SSID_MAX_LEN || bss->ssid.ssid_len < 1) { wpa_printf(MSG_ERROR, "Line %d: invalid SSID '%s'", line, pos); @@ -1957,7 +1993,7 @@ static int hostapd_config_fill(struct hostapd_config *conf, } else if (os_strcmp(buf, "ssid2") == 0) { size_t slen; char *str = wpa_config_parse_string(pos, &slen); - if (str == NULL || slen < 1 || slen > HOSTAPD_MAX_SSID_LEN) { + if (str == NULL || slen < 1 || slen > SSID_MAX_LEN) { wpa_printf(MSG_ERROR, "Line %d: invalid SSID '%s'", line, pos); os_free(str); @@ -2043,6 +2079,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, bss->private_key_passwd = os_strdup(pos); } else if (os_strcmp(buf, "check_crl") == 0) { bss->check_crl = atoi(pos); + } else if (os_strcmp(buf, "tls_session_lifetime") == 0) { + bss->tls_session_lifetime = atoi(pos); } else if (os_strcmp(buf, "ocsp_stapling_response") == 0) { os_free(bss->ocsp_stapling_response); bss->ocsp_stapling_response = os_strdup(pos); @@ -2515,13 +2553,17 @@ static int hostapd_config_fill(struct hostapd_config *conf, conf->hw_mode = HOSTAPD_MODE_IEEE80211G; else if (os_strcmp(pos, "ad") == 0) conf->hw_mode = HOSTAPD_MODE_IEEE80211AD; + else if (os_strcmp(pos, "any") == 0) + conf->hw_mode = HOSTAPD_MODE_IEEE80211ANY; else { wpa_printf(MSG_ERROR, "Line %d: unknown hw_mode '%s'", line, pos); return 1; } } else if (os_strcmp(buf, "wps_rf_bands") == 0) { - if (os_strcmp(pos, "a") == 0) + if (os_strcmp(pos, "ad") == 0) + bss->wps_rf_bands = WPS_RF_60GHZ; + else if (os_strcmp(pos, "a") == 0) bss->wps_rf_bands = WPS_RF_50GHZ; else if (os_strcmp(pos, "g") == 0 || os_strcmp(pos, "b") == 0) @@ -2542,12 +2584,15 @@ static int hostapd_config_fill(struct hostapd_config *conf, line); return 1; #else /* CONFIG_ACS */ + conf->acs = 1; conf->channel = 0; #endif /* CONFIG_ACS */ - } else + } else { conf->channel = atoi(pos); + conf->acs = conf->channel == 0; + } } else if (os_strcmp(buf, "chanlist") == 0) { - if (hostapd_parse_intlist(&conf->chanlist, pos)) { + if (hostapd_parse_chanlist(conf, pos)) { wpa_printf(MSG_ERROR, "Line %d: invalid channel list", line); return 1; @@ -2810,7 +2855,7 @@ static int hostapd_config_fill(struct hostapd_config *conf, os_free(bss->wps_pin_requests); bss->wps_pin_requests = os_strdup(pos); } else if (os_strcmp(buf, "device_name") == 0) { - if (os_strlen(pos) > 32) { + if (os_strlen(pos) > WPS_DEV_NAME_MAX_LEN) { wpa_printf(MSG_ERROR, "Line %d: Too long " "device_name", line); return 1; @@ -3111,6 +3156,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, 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) { bss->osen = atoi(pos); } else if (os_strcmp(buf, "anqp_domain_id") == 0) { @@ -3223,6 +3270,24 @@ static int hostapd_config_fill(struct hostapd_config *conf, bss->bss_load_test_set = 1; } else if (os_strcmp(buf, "radio_measurements") == 0) { bss->radio_measurements = atoi(pos); + } else if (os_strcmp(buf, "own_ie_override") == 0) { + struct wpabuf *tmp; + size_t len = os_strlen(pos) / 2; + + tmp = wpabuf_alloc(len); + if (!tmp) + return 1; + + if (hexstr2bin(pos, wpabuf_put(tmp, len), len)) { + wpabuf_free(tmp); + wpa_printf(MSG_ERROR, + "Line %d: Invalid own_ie_override '%s'", + line, pos); + return 1; + } + + wpabuf_free(bss->own_ie_override); + bss->own_ie_override = tmp; #endif /* CONFIG_TESTING_OPTIONS */ } else if (os_strcmp(buf, "vendor_elements") == 0) { struct wpabuf *elems; @@ -3276,6 +3341,74 @@ static int hostapd_config_fill(struct hostapd_config *conf, } else if (os_strcmp(buf, "wowlan_triggers") == 0) { os_free(bss->wowlan_triggers); bss->wowlan_triggers = os_strdup(pos); +#ifdef CONFIG_FST + } else if (os_strcmp(buf, "fst_group_id") == 0) { + size_t len = os_strlen(pos); + + if (!len || len >= sizeof(conf->fst_cfg.group_id)) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid fst_group_id value '%s'", + line, pos); + return 1; + } + + if (conf->fst_cfg.group_id[0]) { + wpa_printf(MSG_ERROR, + "Line %d: Duplicate fst_group value '%s'", + line, pos); + return 1; + } + + os_strlcpy(conf->fst_cfg.group_id, pos, + sizeof(conf->fst_cfg.group_id)); + } else if (os_strcmp(buf, "fst_priority") == 0) { + char *endp; + long int val; + + if (!*pos) { + wpa_printf(MSG_ERROR, + "Line %d: fst_priority value not supplied (expected 1..%u)", + line, FST_MAX_PRIO_VALUE); + return -1; + } + + val = strtol(pos, &endp, 0); + if (*endp || val < 1 || val > FST_MAX_PRIO_VALUE) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid fst_priority %ld (%s) (expected 1..%u)", + line, val, pos, FST_MAX_PRIO_VALUE); + return 1; + } + conf->fst_cfg.priority = (u8) val; + } else if (os_strcmp(buf, "fst_llt") == 0) { + char *endp; + long int val; + + if (!*pos) { + wpa_printf(MSG_ERROR, + "Line %d: fst_llt value not supplied (expected 1..%u)", + line, FST_MAX_LLT_MS); + return -1; + } + val = strtol(pos, &endp, 0); + if (*endp || val < 1 || 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); + return 1; + } + conf->fst_cfg.llt = (u32) val; +#endif /* CONFIG_FST */ + } else if (os_strcmp(buf, "track_sta_max_num") == 0) { + conf->track_sta_max_num = atoi(pos); + } else if (os_strcmp(buf, "track_sta_max_age") == 0) { + conf->track_sta_max_age = atoi(pos); + } else if (os_strcmp(buf, "no_probe_resp_if_seen_on") == 0) { + os_free(bss->no_probe_resp_if_seen_on); + bss->no_probe_resp_if_seen_on = os_strdup(pos); + } 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 { wpa_printf(MSG_ERROR, "Line %d: unknown configuration item '%s'", @@ -3378,7 +3511,8 @@ struct hostapd_config * hostapd_config_read(const char *fname) int hostapd_set_iface(struct hostapd_config *conf, - struct hostapd_bss_config *bss, char *field, char *value) + struct hostapd_bss_config *bss, const char *field, + char *value) { int errors; size_t i; diff --git a/contrib/wpa/hostapd/config_file.h b/contrib/wpa/hostapd/config_file.h index fba57b8..c98bdb6 100644 --- a/contrib/wpa/hostapd/config_file.h +++ b/contrib/wpa/hostapd/config_file.h @@ -11,7 +11,7 @@ struct hostapd_config * hostapd_config_read(const char *fname); int hostapd_set_iface(struct hostapd_config *conf, - struct hostapd_bss_config *bss, char *field, + struct hostapd_bss_config *bss, const char *field, char *value); #endif /* CONFIG_FILE_H */ diff --git a/contrib/wpa/hostapd/ctrl_iface.c b/contrib/wpa/hostapd/ctrl_iface.c index 86f1aa6..cb6fb17 100644 --- a/contrib/wpa/hostapd/ctrl_iface.c +++ b/contrib/wpa/hostapd/ctrl_iface.c @@ -25,6 +25,7 @@ #include "common/ieee802_11_defs.h" #include "crypto/tls.h" #include "drivers/driver.h" +#include "eapol_auth/eapol_auth_sm.h" #include "radius/radius_client.h" #include "radius/radius_server.h" #include "l2_packet/l2_packet.h" @@ -43,10 +44,13 @@ #include "ap/beacon.h" #include "wps/wps_defs.h" #include "wps/wps.h" +#include "fst/fst_ctrl_iface.h" #include "config_file.h" #include "ctrl_iface.h" +#define HOSTAPD_CLI_DUP_VALUE_MAX_LEN 256 + struct wpa_ctrl_dst { struct wpa_ctrl_dst *next; struct sockaddr_un addr; @@ -57,6 +61,7 @@ struct wpa_ctrl_dst { static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, + enum wpa_msg_type type, const char *buf, size_t len); @@ -1055,6 +1060,97 @@ static int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd, #endif /* CONFIG_WNM */ +static int hostapd_ctrl_iface_get_key_mgmt(struct hostapd_data *hapd, + char *buf, size_t buflen) +{ + int ret = 0; + char *pos, *end; + + pos = buf; + end = buf + buflen; + + WPA_ASSERT(hapd->conf->wpa_key_mgmt); + + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) { + ret = os_snprintf(pos, end - pos, "WPA-PSK "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { + ret = os_snprintf(pos, end - pos, "WPA-EAP "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#ifdef CONFIG_IEEE80211R + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) { + ret = os_snprintf(pos, end - pos, "FT-PSK "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) { + ret = os_snprintf(pos, end - pos, "FT-EAP "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#ifdef CONFIG_SAE + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE) { + ret = os_snprintf(pos, end - pos, "FT-SAE "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_SAE */ +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) { + ret = os_snprintf(pos, end - pos, "WPA-PSK-SHA256 "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) { + ret = os_snprintf(pos, end - pos, "WPA-EAP-SHA256 "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_SAE + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE) { + ret = os_snprintf(pos, end - pos, "SAE "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_SAE */ + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) { + ret = os_snprintf(pos, end - pos, "WPA-EAP-SUITE-B "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + if (hapd->conf->wpa_key_mgmt & + WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) { + ret = os_snprintf(pos, end - pos, + "WPA-EAP-SUITE-B-192 "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + + if (pos > buf && *(pos - 1) == ' ') { + *(pos - 1) = '\0'; + pos--; + } + + return pos - buf; +} + + static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd, char *buf, size_t buflen) { @@ -1104,82 +1200,20 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd, } #endif /* CONFIG_WPS */ + if (hapd->conf->wpa) { + ret = os_snprintf(pos, end - pos, "wpa=%d\n", hapd->conf->wpa); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + if (hapd->conf->wpa && hapd->conf->wpa_key_mgmt) { ret = os_snprintf(pos, end - pos, "key_mgmt="); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; - if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) { - ret = os_snprintf(pos, end - pos, "WPA-PSK "); - if (os_snprintf_error(end - pos, ret)) - return pos - buf; - pos += ret; - } - if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { - ret = os_snprintf(pos, end - pos, "WPA-EAP "); - if (os_snprintf_error(end - pos, ret)) - return pos - buf; - pos += ret; - } -#ifdef CONFIG_IEEE80211R - if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) { - ret = os_snprintf(pos, end - pos, "FT-PSK "); - if (os_snprintf_error(end - pos, ret)) - return pos - buf; - pos += ret; - } - if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) { - ret = os_snprintf(pos, end - pos, "FT-EAP "); - if (os_snprintf_error(end - pos, ret)) - return pos - buf; - pos += ret; - } -#ifdef CONFIG_SAE - if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE) { - ret = os_snprintf(pos, end - pos, "FT-SAE "); - if (os_snprintf_error(end - pos, ret)) - return pos - buf; - pos += ret; - } -#endif /* CONFIG_SAE */ -#endif /* CONFIG_IEEE80211R */ -#ifdef CONFIG_IEEE80211W - if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) { - ret = os_snprintf(pos, end - pos, "WPA-PSK-SHA256 "); - if (os_snprintf_error(end - pos, ret)) - return pos - buf; - pos += ret; - } - if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) { - ret = os_snprintf(pos, end - pos, "WPA-EAP-SHA256 "); - if (os_snprintf_error(end - pos, ret)) - return pos - buf; - pos += ret; - } -#endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_SAE - if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE) { - ret = os_snprintf(pos, end - pos, "SAE "); - if (os_snprintf_error(end - pos, ret)) - return pos - buf; - pos += ret; - } -#endif /* CONFIG_SAE */ - if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) { - ret = os_snprintf(pos, end - pos, "WPA-EAP-SUITE-B "); - if (os_snprintf_error(end - pos, ret)) - return pos - buf; - pos += ret; - } - if (hapd->conf->wpa_key_mgmt & - WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) { - ret = os_snprintf(pos, end - pos, - "WPA-EAP-SUITE-B-192 "); - if (os_snprintf_error(end - pos, ret)) - return pos - buf; - pos += ret; - } + pos += hostapd_ctrl_iface_get_key_mgmt(hapd, pos, end - pos); ret = os_snprintf(pos, end - pos, "\n"); if (os_snprintf_error(end - pos, ret)) @@ -1528,7 +1562,7 @@ void hostapd_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf, { struct hostapd_data *hapd = ctx; const struct ether_header *eth; - const struct iphdr *ip; + struct iphdr ip; const u8 *pos; unsigned int i; @@ -1536,14 +1570,14 @@ void hostapd_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf, return; eth = (const struct ether_header *) buf; - ip = (const struct iphdr *) (eth + 1); - pos = (const u8 *) (ip + 1); + os_memcpy(&ip, eth + 1, sizeof(ip)); + pos = &buf[sizeof(*eth) + sizeof(ip)]; - if (ip->ihl != 5 || ip->version != 4 || - ntohs(ip->tot_len) != HWSIM_IP_LEN) + if (ip.ihl != 5 || ip.version != 4 || + ntohs(ip.tot_len) != HWSIM_IP_LEN) return; - for (i = 0; i < HWSIM_IP_LEN - sizeof(*ip); i++) { + for (i = 0; i < HWSIM_IP_LEN - sizeof(ip); i++) { if (*pos != (u8) i) return; pos++; @@ -1599,7 +1633,7 @@ static int hostapd_ctrl_iface_data_test_tx(struct hostapd_data *hapd, char *cmd) int used; long int val; u8 tos; - u8 buf[HWSIM_PACKETLEN]; + u8 buf[2 + HWSIM_PACKETLEN]; struct ether_header *eth; struct iphdr *ip; u8 *dpos; @@ -1627,7 +1661,7 @@ static int hostapd_ctrl_iface_data_test_tx(struct hostapd_data *hapd, char *cmd) return -1; tos = val; - eth = (struct ether_header *) buf; + eth = (struct ether_header *) &buf[2]; os_memcpy(eth->ether_dhost, dst, ETH_ALEN); os_memcpy(eth->ether_shost, src, ETH_ALEN); eth->ether_type = htons(ETHERTYPE_IP); @@ -1639,14 +1673,14 @@ static int hostapd_ctrl_iface_data_test_tx(struct hostapd_data *hapd, char *cmd) ip->tos = tos; ip->tot_len = htons(HWSIM_IP_LEN); ip->protocol = 1; - ip->saddr = htonl(192 << 24 | 168 << 16 | 1 << 8 | 1); - ip->daddr = htonl(192 << 24 | 168 << 16 | 1 << 8 | 2); + ip->saddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 1); + ip->daddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 2); ip->check = ipv4_hdr_checksum(ip, sizeof(*ip)); dpos = (u8 *) (ip + 1); for (i = 0; i < HWSIM_IP_LEN - sizeof(*ip); i++) *dpos++ = i; - if (l2_packet_send(hapd->l2_test, dst, ETHERTYPE_IP, buf, + if (l2_packet_send(hapd->l2_test, dst, ETHERTYPE_IP, &buf[2], HWSIM_PACKETLEN) < 0) return -1; @@ -1746,6 +1780,45 @@ static int hostapd_ctrl_get_alloc_fail(struct hostapd_data *hapd, #endif /* WPA_TRACE_BFD */ } + +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); + pos = os_strchr(cmd, ':'); + if (pos) { + pos++; + os_strlcpy(wpa_trace_test_fail_func, pos, + sizeof(wpa_trace_test_fail_func)); + } else { + wpa_trace_test_fail_after = 0; + } + + return 0; +#else /* WPA_TRACE_BFD */ + return -1; +#endif /* WPA_TRACE_BFD */ +} + + +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 */ + return -1; +#endif /* WPA_TRACE_BFD */ +} + #endif /* CONFIG_TESTING_OPTIONS */ @@ -1847,41 +1920,134 @@ static int hostapd_ctrl_iface_vendor(struct hostapd_data *hapd, char *cmd, } -static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, - void *sock_ctx) +static int hostapd_ctrl_iface_eapol_reauth(struct hostapd_data *hapd, + const char *cmd) { - struct hostapd_data *hapd = eloop_ctx; - char buf[4096]; - int res; - struct sockaddr_un from; - socklen_t fromlen = sizeof(from); - char *reply; - const int reply_size = 4096; - int reply_len; - int level = MSG_DEBUG; + u8 addr[ETH_ALEN]; + struct sta_info *sta; - res = recvfrom(sock, buf, sizeof(buf) - 1, 0, - (struct sockaddr *) &from, &fromlen); - if (res < 0) { - wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s", - strerror(errno)); - return; + if (hwaddr_aton(cmd, addr)) + return -1; + + sta = ap_get_sta(hapd, addr); + if (!sta || !sta->eapol_sm) + return -1; + + eapol_auth_reauthenticate(sta->eapol_sm); + return 0; +} + + +static int hostapd_ctrl_iface_eapol_set(struct hostapd_data *hapd, char *cmd) +{ + u8 addr[ETH_ALEN]; + struct sta_info *sta; + char *pos = cmd, *param; + + if (hwaddr_aton(pos, addr) || pos[17] != ' ') + return -1; + pos += 18; + param = pos; + pos = os_strchr(pos, ' '); + if (!pos) + return -1; + *pos++ = '\0'; + + sta = ap_get_sta(hapd, addr); + if (!sta || !sta->eapol_sm) + return -1; + + return eapol_auth_set_conf(sta->eapol_sm, param, pos); +} + + +static int hostapd_ctrl_iface_log_level(struct hostapd_data *hapd, char *cmd, + char *buf, size_t buflen) +{ + char *pos, *end, *stamp; + int ret; + + /* cmd: "LOG_LEVEL [<level>]" */ + if (*cmd == '\0') { + pos = buf; + end = buf + buflen; + ret = os_snprintf(pos, end - pos, "Current level: %s\n" + "Timestamp: %d\n", + debug_level_str(wpa_debug_level), + wpa_debug_timestamp); + if (os_snprintf_error(end - pos, ret)) + ret = 0; + + return ret; } - 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) { - if (sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, - fromlen) < 0) { - wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s", - strerror(errno)); + while (*cmd == ' ') + cmd++; + + stamp = os_strchr(cmd, ' '); + if (stamp) { + *stamp++ = '\0'; + while (*stamp == ' ') { + stamp++; } - return; } + if (os_strlen(cmd)) { + int level = str_to_debug_level(cmd); + if (level < 0) + return -1; + wpa_debug_level = level; + } + + if (stamp && os_strlen(stamp)) + wpa_debug_timestamp = atoi(stamp); + + os_memcpy(buf, "OK\n", 3); + return 3; +} + + +#ifdef NEED_AP_MLME +static int hostapd_ctrl_iface_track_sta_list(struct hostapd_data *hapd, + char *buf, size_t buflen) +{ + struct hostapd_iface *iface = hapd->iface; + char *pos, *end; + struct hostapd_sta_info *info; + struct os_reltime now; + + sta_track_expire(iface, 0); + + pos = buf; + end = buf + buflen; + + os_get_reltime(&now); + dl_list_for_each_reverse(info, &iface->sta_seen, + struct hostapd_sta_info, list) { + struct os_reltime age; + int ret; + + os_reltime_sub(&now, &info->last_seen, &age); + ret = os_snprintf(pos, end - pos, MACSTR " %u\n", + MAC2STR(info->addr), (unsigned int) age.sec); + if (os_snprintf_error(end - pos, ret)) + break; + pos += ret; + } + + return pos - buf; +} +#endif /* NEED_AP_MLME */ + + +static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, + char *buf, char *reply, + int reply_size, + struct sockaddr_un *from, + socklen_t fromlen) +{ + int reply_len, res; + os_memcpy(reply, "OK\n", 3); reply_len = 3; @@ -1938,13 +2104,13 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, reply_len = hostapd_ctrl_iface_sta_next(hapd, buf + 9, reply, reply_size); } else if (os_strcmp(buf, "ATTACH") == 0) { - if (hostapd_ctrl_iface_attach(hapd, &from, fromlen)) + if (hostapd_ctrl_iface_attach(hapd, from, fromlen)) reply_len = -1; } else if (os_strcmp(buf, "DETACH") == 0) { - if (hostapd_ctrl_iface_detach(hapd, &from, fromlen)) + if (hostapd_ctrl_iface_detach(hapd, from, fromlen)) reply_len = -1; } else if (os_strncmp(buf, "LEVEL ", 6) == 0) { - if (hostapd_ctrl_iface_level(hapd, &from, fromlen, + if (hostapd_ctrl_iface_level(hapd, from, fromlen, buf + 6)) reply_len = -1; } else if (os_strncmp(buf, "NEW_STA ", 8) == 0) { @@ -2079,6 +2245,11 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, } else if (os_strcmp(buf, "GET_ALLOC_FAIL") == 0) { reply_len = hostapd_ctrl_get_alloc_fail(hapd, reply, reply_size); + } else if (os_strncmp(buf, "TEST_FAIL ", 10) == 0) { + if (hostapd_ctrl_test_fail(hapd, buf + 10) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "GET_FAIL") == 0) { + reply_len = hostapd_ctrl_get_fail(hapd, reply, reply_size); #endif /* CONFIG_TESTING_OPTIONS */ } else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) { if (hostapd_ctrl_iface_chan_switch(hapd->iface, buf + 12)) @@ -2091,6 +2262,20 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, #ifdef RADIUS_SERVER radius_server_erp_flush(hapd->radius_srv); #endif /* RADIUS_SERVER */ + } else if (os_strncmp(buf, "EAPOL_REAUTH ", 13) == 0) { + if (hostapd_ctrl_iface_eapol_reauth(hapd, buf + 13)) + reply_len = -1; + } else if (os_strncmp(buf, "EAPOL_SET ", 10) == 0) { + if (hostapd_ctrl_iface_eapol_set(hapd, buf + 10)) + reply_len = -1; + } else if (os_strncmp(buf, "LOG_LEVEL", 9) == 0) { + reply_len = hostapd_ctrl_iface_log_level( + hapd, buf + 9, reply, reply_size); +#ifdef NEED_AP_MLME + } else if (os_strcmp(buf, "TRACK_STA_LIST") == 0) { + reply_len = hostapd_ctrl_iface_track_sta_list( + hapd, reply, reply_size); +#endif /* NEED_AP_MLME */ } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; @@ -2100,6 +2285,50 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, os_memcpy(reply, "FAIL\n", 5); reply_len = 5; } + + return reply_len; +} + + +static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, + void *sock_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + char buf[4096]; + int res; + struct sockaddr_un from; + socklen_t fromlen = sizeof(from); + char *reply; + const int reply_size = 4096; + int reply_len; + int level = MSG_DEBUG; + + res = recvfrom(sock, buf, sizeof(buf) - 1, 0, + (struct sockaddr *) &from, &fromlen); + if (res < 0) { + wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s", + strerror(errno)); + 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) { + if (sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, + fromlen) < 0) { + wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s", + strerror(errno)); + } + return; + } + + reply_len = hostapd_ctrl_iface_receive_process(hapd, buf, + reply, reply_size, + &from, fromlen); + if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen) < 0) { wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s", @@ -2130,13 +2359,14 @@ static char * hostapd_ctrl_iface_path(struct hostapd_data *hapd) } -static void hostapd_ctrl_iface_msg_cb(void *ctx, int level, int global, +static void hostapd_ctrl_iface_msg_cb(void *ctx, int level, + enum wpa_msg_type type, const char *txt, size_t len) { struct hostapd_data *hapd = ctx; if (hapd == NULL) return; - hostapd_ctrl_iface_send(hapd, level, txt, len); + hostapd_ctrl_iface_send(hapd, level, type, txt, len); } @@ -2359,6 +2589,58 @@ 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, + 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; +} + + +static int hostapd_global_ctrl_iface_detach(struct hapd_interfaces *interfaces, + struct sockaddr_un *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; +} + + static void hostapd_ctrl_iface_flush(struct hapd_interfaces *interfaces) { #ifdef CONFIG_WPS_TESTING @@ -2369,6 +2651,214 @@ static void hostapd_ctrl_iface_flush(struct hapd_interfaces *interfaces) } +#ifdef CONFIG_FST + +static int +hostapd_global_ctrl_iface_fst_attach(struct hapd_interfaces *interfaces, + const char *cmd) +{ + char ifname[IFNAMSIZ + 1]; + struct fst_iface_cfg cfg; + struct hostapd_data *hapd; + struct fst_wpa_obj iface_obj; + + if (!fst_parse_attach_command(cmd, ifname, sizeof(ifname), &cfg)) { + hapd = hostapd_get_iface(interfaces, ifname); + if (hapd) { + if (hapd->iface->fst) { + wpa_printf(MSG_INFO, "FST: Already attached"); + return -1; + } + fst_hostapd_fill_iface_obj(hapd, &iface_obj); + hapd->iface->fst = fst_attach(ifname, hapd->own_addr, + &iface_obj, &cfg); + if (hapd->iface->fst) + return 0; + } + } + + return -EINVAL; +} + + +static int +hostapd_global_ctrl_iface_fst_detach(struct hapd_interfaces *interfaces, + const char *cmd) +{ + char ifname[IFNAMSIZ + 1]; + struct hostapd_data * hapd; + + if (!fst_parse_detach_command(cmd, ifname, sizeof(ifname))) { + hapd = hostapd_get_iface(interfaces, ifname); + if (hapd) { + if (!fst_iface_detach(ifname)) { + hapd->iface->fst = NULL; + hapd->iface->fst_ies = NULL; + return 0; + } + } + } + + return -EINVAL; +} + +#endif /* CONFIG_FST */ + + +static struct hostapd_data * +hostapd_interfaces_get_hapd(struct hapd_interfaces *interfaces, + const char *ifname) +{ + size_t i, j; + + for (i = 0; i < interfaces->count; i++) { + struct hostapd_iface *iface = interfaces->iface[i]; + + for (j = 0; j < iface->num_bss; j++) { + struct hostapd_data *hapd; + + hapd = iface->bss[j]; + if (os_strcmp(ifname, hapd->conf->iface) == 0) + return hapd; + } + } + + return NULL; +} + + +static int hostapd_ctrl_iface_dup_param(struct hostapd_data *src_hapd, + struct hostapd_data *dst_hapd, + const char *param) +{ + int res; + char *value; + + value = os_zalloc(HOSTAPD_CLI_DUP_VALUE_MAX_LEN); + if (!value) { + wpa_printf(MSG_ERROR, + "DUP: cannot allocate buffer to stringify %s", + param); + goto error_return; + } + + if (os_strcmp(param, "wpa") == 0) { + os_snprintf(value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN, "%d", + src_hapd->conf->wpa); + } else if (os_strcmp(param, "wpa_key_mgmt") == 0 && + src_hapd->conf->wpa_key_mgmt) { + res = hostapd_ctrl_iface_get_key_mgmt( + src_hapd, value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN); + if (os_snprintf_error(HOSTAPD_CLI_DUP_VALUE_MAX_LEN, res)) + goto error_stringify; + } else if (os_strcmp(param, "wpa_pairwise") == 0 && + src_hapd->conf->wpa_pairwise) { + res = wpa_write_ciphers(value, + value + HOSTAPD_CLI_DUP_VALUE_MAX_LEN, + src_hapd->conf->wpa_pairwise, " "); + if (res < 0) + goto error_stringify; + } else if (os_strcmp(param, "rsn_pairwise") == 0 && + src_hapd->conf->rsn_pairwise) { + res = wpa_write_ciphers(value, + value + HOSTAPD_CLI_DUP_VALUE_MAX_LEN, + src_hapd->conf->rsn_pairwise, " "); + if (res < 0) + goto error_stringify; + } else if (os_strcmp(param, "wpa_passphrase") == 0 && + src_hapd->conf->ssid.wpa_passphrase) { + os_snprintf(value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN, "%s", + src_hapd->conf->ssid.wpa_passphrase); + } else if (os_strcmp(param, "wpa_psk") == 0 && + src_hapd->conf->ssid.wpa_psk_set) { + wpa_snprintf_hex(value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN, + src_hapd->conf->ssid.wpa_psk->psk, PMK_LEN); + } else { + wpa_printf(MSG_WARNING, "DUP: %s cannot be duplicated", param); + goto error_return; + } + + res = hostapd_set_iface(dst_hapd->iconf, dst_hapd->conf, param, value); + os_free(value); + return res; + +error_stringify: + wpa_printf(MSG_ERROR, "DUP: cannot stringify %s", param); +error_return: + os_free(value); + return -1; +} + + +static int +hostapd_global_ctrl_iface_dup_network(struct hapd_interfaces *interfaces, + char *cmd) +{ + char *p_start = cmd, *p_end; + struct hostapd_data *src_hapd, *dst_hapd; + + /* cmd: "<src ifname> <dst ifname> <variable name> */ + + p_end = os_strchr(p_start, ' '); + if (!p_end) { + wpa_printf(MSG_ERROR, "DUP: no src ifname found in cmd: '%s'", + cmd); + return -1; + } + + *p_end = '\0'; + src_hapd = hostapd_interfaces_get_hapd(interfaces, p_start); + if (!src_hapd) { + wpa_printf(MSG_ERROR, "DUP: no src ifname found: '%s'", + p_start); + return -1; + } + + p_start = p_end + 1; + p_end = os_strchr(p_start, ' '); + if (!p_end) { + wpa_printf(MSG_ERROR, "DUP: no dst ifname found in cmd: '%s'", + cmd); + return -1; + } + + *p_end = '\0'; + dst_hapd = hostapd_interfaces_get_hapd(interfaces, p_start); + if (!dst_hapd) { + wpa_printf(MSG_ERROR, "DUP: no dst ifname found: '%s'", + p_start); + return -1; + } + + p_start = p_end + 1; + return hostapd_ctrl_iface_dup_param(src_hapd, dst_hapd, p_start); +} + + +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, + socklen_t fromlen) +{ + struct hostapd_data *hapd; + + hapd = hostapd_interfaces_get_hapd(interfaces, ifname); + if (hapd == NULL) { + int res; + + res = os_snprintf(reply, reply_size, "FAIL-NO-IFNAME-MATCH\n"); + if (os_snprintf_error(reply_size, res)) + return -1; + return res; + } + + return hostapd_ctrl_iface_receive_process(hapd, buf, reply,reply_size, + from, fromlen); +} + + static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx, void *sock_ctx) { @@ -2377,8 +2867,9 @@ static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx, int res; struct sockaddr_un from; socklen_t fromlen = sizeof(from); - char reply[24]; + char *reply; int reply_len; + const int reply_size = 4096; res = recvfrom(sock, buf, sizeof(buf) - 1, 0, (struct sockaddr *) &from, &fromlen); @@ -2390,9 +2881,31 @@ static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx, buf[res] = '\0'; wpa_printf(MSG_DEBUG, "Global ctrl_iface command: %s", buf); + reply = os_malloc(reply_size); + if (reply == NULL) { + if (sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, + fromlen) < 0) { + wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s", + strerror(errno)); + } + return; + } + os_memcpy(reply, "OK\n", 3); reply_len = 3; + if (os_strncmp(buf, "IFNAME=", 7) == 0) { + char *pos = os_strchr(buf + 7, ' '); + + if (pos) { + *pos++ = '\0'; + reply_len = hostapd_global_ctrl_iface_ifname( + interfaces, buf + 7, pos, reply, reply_size, + &from, fromlen); + goto send_reply; + } + } + if (os_strcmp(buf, "PING") == 0) { os_memcpy(reply, "PONG\n", 5); reply_len = 5; @@ -2407,18 +2920,47 @@ static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx, } else if (os_strncmp(buf, "REMOVE ", 7) == 0) { if (hostapd_ctrl_iface_remove(interfaces, buf + 7) < 0) reply_len = -1; + } else if (os_strcmp(buf, "ATTACH") == 0) { + if (hostapd_global_ctrl_iface_attach(interfaces, &from, + fromlen)) + reply_len = -1; + } else if (os_strcmp(buf, "DETACH") == 0) { + if (hostapd_global_ctrl_iface_detach(interfaces, &from, + fromlen)) + 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 */ +#ifdef CONFIG_FST + } else if (os_strncmp(buf, "FST-ATTACH ", 11) == 0) { + if (!hostapd_global_ctrl_iface_fst_attach(interfaces, buf + 11)) + reply_len = os_snprintf(reply, reply_size, "OK\n"); + else + reply_len = -1; + } else if (os_strncmp(buf, "FST-DETACH ", 11) == 0) { + if (!hostapd_global_ctrl_iface_fst_detach(interfaces, buf + 11)) + reply_len = os_snprintf(reply, reply_size, "OK\n"); + else + reply_len = -1; + } else if (os_strncmp(buf, "FST-MANAGER ", 12) == 0) { + reply_len = fst_ctrl_iface_receive(buf + 12, reply, reply_size); +#endif /* CONFIG_FST */ + } else if (os_strncmp(buf, "DUP_NETWORK ", 12) == 0) { + if (!hostapd_global_ctrl_iface_dup_network(interfaces, + buf + 12)) + reply_len = os_snprintf(reply, reply_size, "OK\n"); + else + reply_len = -1; } else { wpa_printf(MSG_DEBUG, "Unrecognized global ctrl_iface command " "ignored"); reply_len = -1; } +send_reply: if (reply_len < 0) { os_memcpy(reply, "FAIL\n", 5); reply_len = 5; @@ -2429,6 +2971,7 @@ static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx, wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s", strerror(errno)); } + os_free(reply); } @@ -2566,6 +3109,7 @@ fail: void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interfaces) { char *fname = NULL; + struct wpa_ctrl_dst *dst, *prev; if (interfaces->global_ctrl_sock > -1) { eloop_unregister_read_sock(interfaces->global_ctrl_sock); @@ -2590,13 +3134,23 @@ void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interfaces) strerror(errno)); } } - os_free(interfaces->global_iface_path); - interfaces->global_iface_path = NULL; + } + + 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); } } static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, + enum wpa_msg_type type, const char *buf, size_t len) { struct wpa_ctrl_dst *dst, *next; @@ -2604,9 +3158,17 @@ static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, int idx; struct iovec io[2]; char levelstr[10]; + int s; - dst = hapd->ctrl_dst; - if (hapd->ctrl_sock < 0 || dst == NULL) + if (type != WPA_MSG_ONLY_GLOBAL) { + s = hapd->ctrl_sock; + dst = hapd->ctrl_dst; + } else { + s = hapd->iface->interfaces->global_ctrl_sock; + dst = hapd->iface->interfaces->global_ctrl_dst; + } + + if (s < 0 || dst == NULL) return; os_snprintf(levelstr, sizeof(levelstr), "<%d>", level); @@ -2627,16 +3189,22 @@ static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, offsetof(struct sockaddr_un, sun_path)); msg.msg_name = &dst->addr; msg.msg_namelen = dst->addrlen; - if (sendmsg(hapd->ctrl_sock, &msg, 0) < 0) { + if (sendmsg(s, &msg, 0) < 0) { int _errno = errno; wpa_printf(MSG_INFO, "CTRL_IFACE monitor[%d]: " "%d - %s", idx, errno, strerror(errno)); dst->errors++; if (dst->errors > 10 || _errno == ENOENT) { - hostapd_ctrl_iface_detach( - hapd, &dst->addr, - dst->addrlen); + if (type != WPA_MSG_ONLY_GLOBAL) + hostapd_ctrl_iface_detach( + hapd, &dst->addr, + dst->addrlen); + else + hostapd_global_ctrl_iface_detach( + hapd->iface->interfaces, + &dst->addr, + dst->addrlen); } } else dst->errors = 0; diff --git a/contrib/wpa/hostapd/defconfig b/contrib/wpa/hostapd/defconfig index 4cde2b5..430f758 100644 --- a/contrib/wpa/hostapd/defconfig +++ b/contrib/wpa/hostapd/defconfig @@ -240,6 +240,12 @@ CONFIG_IPV6=y # requirements described above. #CONFIG_NO_RANDOM_POOL=y +# 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 TLS implementation # openssl = OpenSSL (default) # gnutls = GnuTLS @@ -283,6 +289,12 @@ CONFIG_IPV6=y # Enable SQLite database support in hlr_auc_gw, EAP-SIM DB, and eap_user_file #CONFIG_SQLITE=y +# Enable Fast Session Transfer (FST) +#CONFIG_FST=y + +# Enable CLI commands for FST testing +#CONFIG_FST_TEST=y + # Testing options # This can be used to enable some testing options (see also the example # configuration file) that are really useful only for testing clients that diff --git a/contrib/wpa/hostapd/hlr_auc_gw.c b/contrib/wpa/hostapd/hlr_auc_gw.c index 42d59db..84d0308 100644 --- a/contrib/wpa/hostapd/hlr_auc_gw.c +++ b/contrib/wpa/hostapd/hlr_auc_gw.c @@ -87,6 +87,7 @@ struct milenage_parameters { u8 amf[2]; u8 sqn[6]; int set; + size_t res_len; }; static struct milenage_parameters *milenage_db = NULL; @@ -96,6 +97,7 @@ static struct milenage_parameters *milenage_db = NULL; #define EAP_AKA_RAND_LEN 16 #define EAP_AKA_AUTN_LEN 16 #define EAP_AKA_AUTS_LEN 14 +#define EAP_AKA_RES_MIN_LEN 4 #define EAP_AKA_RES_MAX_LEN 16 #define EAP_AKA_IK_LEN 16 #define EAP_AKA_CK_LEN 16 @@ -124,7 +126,8 @@ static int db_table_create_milenage(sqlite3 *db) " ki CHAR(32) NOT NULL," " opc CHAR(32) NOT NULL," " amf CHAR(4) NOT NULL," - " sqn CHAR(12) NOT NULL" + " sqn CHAR(12) NOT NULL," + " res_len INTEGER" ");"; printf("Adding database table for milenage information\n"); @@ -190,6 +193,10 @@ static int get_milenage_cb(void *ctx, int argc, char *argv[], char *col[]) printf("Invalid sqn value in database\n"); return -1; } + + if (os_strcmp(col[i], "res_len") == 0 && argv[i]) { + m->res_len = atoi(argv[i]); + } } return 0; @@ -206,8 +213,7 @@ static struct milenage_parameters * db_get_milenage(const char *imsi_txt) os_snprintf(db_tmp_milenage.imsi, sizeof(db_tmp_milenage.imsi), "%llu", imsi); os_snprintf(cmd, sizeof(cmd), - "SELECT ki,opc,amf,sqn FROM milenage WHERE imsi=%llu;", - imsi); + "SELECT * FROM milenage WHERE imsi=%llu;", imsi); if (sqlite3_exec(sqlite_db, cmd, get_milenage_cb, &db_tmp_milenage, NULL) != SQLITE_OK) return NULL; @@ -424,7 +430,7 @@ static int read_milenage(const char *fname) while (fgets(buf, sizeof(buf), f)) { line++; - /* Parse IMSI Ki OPc AMF SQN */ + /* Parse IMSI Ki OPc AMF SQN [RES_len] */ buf[sizeof(buf) - 1] = '\0'; if (buf[0] == '#') continue; @@ -515,7 +521,19 @@ static int read_milenage(const char *fname) ret = -1; break; } - pos = pos2 + 1; + + if (pos2) { + pos = pos2 + 1; + 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); + ret = -1; + break; + } + } m->next = milenage_db; milenage_db = m; @@ -532,7 +550,7 @@ static int read_milenage(const char *fname) static void update_milenage_file(const char *fname) { FILE *f, *f2; - char buf[500], *pos; + char name[500], buf[500], *pos; char *end = buf + sizeof(buf); struct milenage_parameters *m; size_t imsi_len; @@ -543,10 +561,10 @@ static void update_milenage_file(const char *fname) return; } - snprintf(buf, sizeof(buf), "%s.new", fname); - f2 = fopen(buf, "w"); + snprintf(name, sizeof(name), "%s.new", fname); + f2 = fopen(name, "w"); if (f2 == NULL) { - printf("Could not write Milenage data file '%s'\n", buf); + printf("Could not write Milenage data file '%s'\n", name); fclose(f); return; } @@ -588,14 +606,14 @@ static void update_milenage_file(const char *fname) fclose(f2); fclose(f); - snprintf(buf, sizeof(buf), "%s.bak", fname); - if (rename(fname, buf) < 0) { + snprintf(name, sizeof(name), "%s.bak", fname); + if (rename(fname, name) < 0) { perror("rename"); return; } - snprintf(buf, sizeof(buf), "%s.new", fname); - if (rename(buf, fname) < 0) { + snprintf(name, sizeof(name), "%s.new", fname); + if (rename(name, fname) < 0) { perror("rename"); return; } @@ -798,6 +816,10 @@ static int aka_req_auth(char *imsi, char *resp, size_t resp_len) } milenage_generate(m->opc, m->amf, m->ki, m->sqn, _rand, autn, ik, ck, res, &res_len); + if (m->res_len >= EAP_AKA_RES_MIN_LEN && + m->res_len <= EAP_AKA_RES_MAX_LEN && + m->res_len < res_len) + res_len = m->res_len; } else { printf("Unknown IMSI: %s\n", imsi); #ifdef AKA_USE_FIXED_TEST_VALUES diff --git a/contrib/wpa/hostapd/hlr_auc_gw.milenage_db b/contrib/wpa/hostapd/hlr_auc_gw.milenage_db index ecd06d7..c156a29 100644 --- a/contrib/wpa/hostapd/hlr_auc_gw.milenage_db +++ b/contrib/wpa/hostapd/hlr_auc_gw.milenage_db @@ -5,8 +5,10 @@ # authentication. In case of GSM/EAP-SIM, AMF and SQN values are not used, but # dummy values will need to be included in this file. -# IMSI Ki OPc AMF SQN +# IMSI Ki OPc AMF SQN [RES_len] 232010000000000 90dca4eda45b53cf0f12d7c9c3bc6a89 cb9cccc4b9258e6dca4760379fb82581 61df 000000000000 +# Example using truncated 32-bit RES instead of 64-bit default +232010000000001 90dca4eda45b53cf0f12d7c9c3bc6a89 cb9cccc4b9258e6dca4760379fb82581 61df 000000000000 4 # These values are from Test Set 19 which has the AMF separation bit set to 1 # and as such, is suitable for EAP-AKA' test. diff --git a/contrib/wpa/hostapd/hostapd.conf b/contrib/wpa/hostapd/hostapd.conf index 9e81e9e..a0071f7 100644 --- a/contrib/wpa/hostapd/hostapd.conf +++ b/contrib/wpa/hostapd/hostapd.conf @@ -127,7 +127,9 @@ ssid=test # 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) +# 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. # Default: IEEE 802.11b hw_mode=g @@ -170,8 +172,11 @@ channel=1 # Channel list restriction. This option allows hostapd to select one of the # provided channels when a channel should be automatically selected. -# Default: not set (allow any enabled channel to be selected) +# Channel list can be provided as range using hyphen ('-') or individual +# channels can be specified by space (' ') seperated values +# Default: all channels allowed in selected hw_mode #chanlist=100 104 108 112 116 +#chanlist=1 6 11-13 # Beacon interval in kus (1.024 ms) (default: 100; range 15..65535) beacon_int=100 @@ -275,8 +280,9 @@ ignore_broadcast_ssid=0 # (data0 is the highest priority queue) # parameters: # aifs: AIFS (default 2) -# cwmin: cwMin (1, 3, 7, 15, 31, 63, 127, 255, 511, 1023) -# cwmax: cwMax (1, 3, 7, 15, 31, 63, 127, 255, 511, 1023); cwMax >= cwMin +# cwmin: cwMin (1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, +# 16383, 32767) +# cwmax: cwMax (same values as cwMin, cwMax >= cwMin) # burst: maximum length (in milliseconds with precision of up to 0.1 ms) for # bursting # @@ -337,8 +343,9 @@ ignore_broadcast_ssid=0 # note - txop_limit is in units of 32microseconds # note - acm is admission control mandatory flag. 0 = admission control not # required, 1 = mandatory -# note - here cwMin and cmMax are in exponent form. the actual cw value used -# will be (2^n)-1 where n is the value given here +# note - Here cwMin and cmMax are in exponent form. The actual cw value used +# will be (2^n)-1 where n is the value given here. The allowed range for these +# wmm_ac_??_{cwmin,cwmax} is 0..15 with cwmax >= cwmin. # wmm_enabled=1 # @@ -575,14 +582,16 @@ wmm_ac_vo_acm=0 # 0 = Not supported (default) # 1 = Supported # -# Compressed Steering Number of Beamformer Antennas Supported: [BF-ANTENNA-2] +# Compressed Steering Number of Beamformer Antennas Supported: +# [BF-ANTENNA-2] [BF-ANTENNA-3] [BF-ANTENNA-4] # Beamformee's capability indicating the maximum number of beamformer # antennas the beamformee can support when sending compressed beamforming # feedback # If SU beamformer capable, set to maximum value minus 1 # else reserved (default) # -# Number of Sounding Dimensions: [SOUNDING-DIMENSION-2] +# Number of Sounding Dimensions: +# [SOUNDING-DIMENSION-2] [SOUNDING-DIMENSION-3] [SOUNDING-DIMENSION-4] # Beamformer's capability indicating the maximum value of the NUM_STS parameter # in the TXVECTOR of a VHT NDP # If SU beamformer capable, set to maximum value minus 1 @@ -593,11 +602,6 @@ wmm_ac_vo_acm=0 # 0 = Not supported or sent by Non-AP STA (default) # 1 = Supported # -# MU Beamformee Capable: [MU-BEAMFORMEE] -# Indicates support for operation as an MU beamformee -# 0 = Not supported or sent by AP (default) -# 1 = Supported -# # 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 @@ -764,6 +768,12 @@ eap_server=0 # 2 = check all CRLs in the certificate path #check_crl=1 +# TLS Session Lifetime in seconds +# This can be used to allow TLS sessions to be cached and resumed with an +# abbreviated handshake when using EAP-TLS/TTLS/PEAP. +# (default: 0 = session caching and resumption disabled) +#tls_session_lifetime=3600 + # Cached OCSP stapling response (DER encoded) # If set, this file is sent as a certificate status response by the EAP server # if the EAP peer requests certificate status in the ClientHello message. @@ -787,7 +797,7 @@ eap_server=0 # is in DSA parameters format, it will be automatically converted into DH # params. This parameter is required if anonymous EAP-FAST is used. # You can generate DH parameters file with OpenSSL, e.g., -# "openssl dhparam -out /etc/hostapd.dh.pem 1024" +# "openssl dhparam -out /etc/hostapd.dh.pem 2048" #dh_file=/etc/hostapd.dh.pem # OpenSSL cipher string @@ -1247,6 +1257,11 @@ own_ip_addr=127.0.0.1 # 1 = push PMK-R1 to all configured R1KHs whenever a new PMK-R0 is derived #pmk_r1_push=1 +# Whether to enable FT-over-DS +# 0 = FT-over-DS disabled +# 1 = FT-over-DS enabled (default) +#ft_over_ds=1 + ##### Neighbor table ########################################################## # Maximum number of entries kept in AP table (either for neigbor table or for # detecting Overlapping Legacy BSS Condition). The oldest entry will be @@ -1264,6 +1279,43 @@ own_ip_addr=127.0.0.1 # default: 60 #ap_table_expiration_time=3600 +# Maximum number of stations to track on the operating channel +# This can be used to detect dualband capable stations before they have +# associated, e.g., to provide guidance on which colocated BSS to use. +# Default: 0 (disabled) +#track_sta_max_num=100 + +# Maximum age of a station tracking entry in seconds +# Default: 180 +#track_sta_max_age=180 + +# Do not reply to group-addressed Probe Request from a station that was seen on +# another radio. +# Default: Disabled +# +# This can be used with enabled track_sta_max_num configuration on another +# interface controlled by the same hostapd process to restrict Probe Request +# frame handling from replying to group-addressed Probe Request frames from a +# station that has been detected to be capable of operating on another band, +# e.g., to try to reduce likelihood of the station selecting a 2.4 GHz BSS when +# the AP operates both a 2.4 GHz and 5 GHz BSS concurrently. +# +# Note: Enabling this can cause connectivity issues and increase latency for +# discovering the AP. +#no_probe_resp_if_seen_on=wlan1 + +# Reject authentication from a station that was seen on another radio. +# Default: Disabled +# +# This can be used with enabled track_sta_max_num configuration on another +# interface controlled by the same hostapd process to reject authentication +# attempts from a station that has been detected to be capable of operating on +# another band, e.g., to try to reduce likelihood of the station selecting a +# 2.4 GHz BSS when the AP operates both a 2.4 GHz and 5 GHz BSS concurrently. +# +# Note: Enabling this can cause connectivity issues and increase latency for +# connecting with the AP. +#no_auth_if_seen_on=wlan1 ##### Wi-Fi Protected Setup (WPS) ############################################# @@ -1430,7 +1482,7 @@ own_ip_addr=127.0.0.1 # 12-digit, all-numeric code that identifies the consumer package. #upc=123456789012 -# WPS RF Bands (a = 5G, b = 2.4G, g = 2.4G, ag = dual band) +# WPS RF Bands (a = 5G, b = 2.4G, g = 2.4G, ag = dual band, ad = 60 GHz) # This value should be set according to RF band(s) supported by the AP if # hw_mode is not set. For dual band dual concurrent devices, this needs to be # set to ag to allow both RF bands to be advertized. @@ -1490,6 +1542,13 @@ own_ip_addr=127.0.0.1 # 1 = enabled #proxy_arp=1 +# IPv6 Neighbor Advertisement multicast-to-unicast conversion +# This can be used with Proxy ARP to allow multicast NAs to be forwarded to +# associated STAs using link layer unicast delivery. +# 0 = disabled (default) +# 1 = enabled +#na_mcast_to_ucast=0 + ##### IEEE 802.11u-2011 ####################################################### # Enable Interworking service @@ -1738,6 +1797,32 @@ own_ip_addr=127.0.0.1 # #osu_server_uri=... +##### 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. +# +# 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. + +# Identifier of an FST Group the interface belongs to. +#fst_group_id=bond0 + +# Interface priority within the FST Group. +# Announcing a higher priority for an interface means declaring it more +# preferable for FST switch. +# fst_priority is in 1..255 range with 1 being the lowest priority. +#fst_priority=100 + +# Default LLT value for this interface in milliseconds. The value used in case +# no value provided during session setup. Default is 50 ms. +# fst_llt is in 1..4294967 range (due to spec limitation, see 10.32.2.2 +# Transitioning between states). +#fst_llt=100 + ##### TESTING OPTIONS ######################################################### # # The options in this section are only available when the build configuration diff --git a/contrib/wpa/hostapd/hostapd_cli.c b/contrib/wpa/hostapd/hostapd_cli.c index 3f00cbb..46c2f37 100644 --- a/contrib/wpa/hostapd/hostapd_cli.c +++ b/contrib/wpa/hostapd/hostapd_cli.c @@ -10,22 +10,23 @@ #include <dirent.h> #include "common/wpa_ctrl.h" +#include "common/ieee802_11_defs.h" #include "utils/common.h" #include "utils/eloop.h" #include "utils/edit.h" #include "common/version.h" -static const char *hostapd_cli_version = +static const char *const hostapd_cli_version = "hostapd_cli v" VERSION_STR "\n" "Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> and contributors"; -static const char *hostapd_cli_license = +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 *hostapd_cli_full_license = +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" @@ -56,7 +57,7 @@ static const char *hostapd_cli_full_license = "OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" "\n"; -static const char *commands_help = +static const char *const commands_help = "Commands:\n" " mib get MIB variables (dot1x, dot11, radius)\n" " sta <addr> get MIB variables for one station\n" @@ -96,6 +97,7 @@ static int hostapd_cli_attached = 0; #define CONFIG_CTRL_IFACE_DIR "/var/run/hostapd" #endif /* CONFIG_CTRL_IFACE_DIR */ static const char *ctrl_iface_dir = CONFIG_CTRL_IFACE_DIR; +static const char *client_socket_dir = NULL; static char *ctrl_ifname = NULL; static const char *pid_file = NULL; @@ -111,13 +113,15 @@ static void usage(void) "\n" "usage: hostapd_cli [-p<path>] [-i<ifname>] [-hvB] " "[-a<path>] \\\n" - " [-G<ping interval>] [command..]\n" + " [-P<pid file>] [-G<ping interval>] [command..]\n" "\n" "Options:\n" " -h help (show this usage text)\n" " -v shown version information\n" " -p<path> path to find control sockets (default: " "/var/run/hostapd)\n" + " -s<dir_path> dir path to open client sockets (default: " + CONFIG_CTRL_IFACE_DIR ")\n" " -a<file> run in daemon mode executing the action file " "based on events\n" " from hostapd\n" @@ -144,7 +148,14 @@ static struct wpa_ctrl * hostapd_cli_open_connection(const char *ifname) return NULL; snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ifname); - ctrl_conn = wpa_ctrl_open(cfile); + if (client_socket_dir && client_socket_dir[0] && + access(client_socket_dir, F_OK) < 0) { + perror(client_socket_dir); + free(cfile); + return NULL; + } + + ctrl_conn = wpa_ctrl_open2(cfile, client_socket_dir); free(cfile); return ctrl_conn; } @@ -541,7 +552,7 @@ static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc, char *argv[]) { char buf[256]; - char ssid_hex[2 * 32 + 1]; + char ssid_hex[2 * SSID_MAX_LEN + 1]; char key_hex[2 * 64 + 1]; int i; @@ -552,7 +563,7 @@ static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc, } ssid_hex[0] = '\0'; - for (i = 0; i < 32; i++) { + for (i = 0; i < SSID_MAX_LEN; i++) { if (argv[0][i] == '\0') break; os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[0][i]); @@ -921,6 +932,35 @@ static int hostapd_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +#ifdef CONFIG_FST +static int hostapd_cli_cmd_fst(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256]; + int res; + int i; + int total; + + if (argc <= 0) { + printf("FST command: parameters are required.\n"); + return -1; + } + + total = os_snprintf(cmd, sizeof(cmd), "FST-MANAGER"); + + for (i = 0; i < argc; i++) { + res = os_snprintf(cmd + total, sizeof(cmd) - total, " %s", + argv[i]); + if (os_snprintf_error(sizeof(cmd) - total, res)) { + printf("Too long fst command.\n"); + return -1; + } + total += res; + } + return wpa_ctrl_command(ctrl, cmd); +} +#endif /* CONFIG_FST */ + + static int hostapd_cli_cmd_chan_switch(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -1009,12 +1049,31 @@ static int hostapd_cli_cmd_erp_flush(struct wpa_ctrl *ctrl, int argc, } +static int hostapd_cli_cmd_log_level(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + res = os_snprintf(cmd, sizeof(cmd), "LOG_LEVEL%s%s%s%s", + argc >= 1 ? " " : "", + argc >= 1 ? argv[0] : "", + argc == 2 ? " " : "", + argc == 2 ? argv[1] : ""); + if (os_snprintf_error(sizeof(cmd), res)) { + printf("Too long option\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + struct hostapd_cli_cmd { const char *cmd; int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]); }; -static struct hostapd_cli_cmd hostapd_cli_commands[] = { +static const struct hostapd_cli_cmd hostapd_cli_commands[] = { { "ping", hostapd_cli_cmd_ping }, { "mib", hostapd_cli_cmd_mib }, { "relog", hostapd_cli_cmd_relog }, @@ -1048,6 +1107,9 @@ static struct hostapd_cli_cmd hostapd_cli_commands[] = { { "get_config", hostapd_cli_cmd_get_config }, { "help", hostapd_cli_cmd_help }, { "interface", hostapd_cli_cmd_interface }, +#ifdef CONFIG_FST + { "fst", hostapd_cli_cmd_fst }, +#endif /* CONFIG_FST */ { "level", hostapd_cli_cmd_level }, { "license", hostapd_cli_cmd_license }, { "quit", hostapd_cli_cmd_quit }, @@ -1063,13 +1125,14 @@ static struct hostapd_cli_cmd hostapd_cli_commands[] = { { "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 } }; static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - struct hostapd_cli_cmd *cmd, *match = NULL; + const struct hostapd_cli_cmd *cmd, *match = NULL; int count; count = 0; @@ -1284,7 +1347,7 @@ int main(int argc, char *argv[]) return -1; for (;;) { - c = getopt(argc, argv, "a:BhG:i:p:v"); + c = getopt(argc, argv, "a:BhG:i:p:P:s:v"); if (c < 0) break; switch (c) { @@ -1310,6 +1373,12 @@ int main(int argc, char *argv[]) case 'p': ctrl_iface_dir = optarg; break; + case 'P': + pid_file = optarg; + break; + case 's': + client_socket_dir = optarg; + break; default: usage(); return -1; diff --git a/contrib/wpa/hostapd/main.c b/contrib/wpa/hostapd/main.c index dd389a8..6c7406a 100644 --- a/contrib/wpa/hostapd/main.c +++ b/contrib/wpa/hostapd/main.c @@ -24,6 +24,7 @@ #include "ap/hostapd.h" #include "ap/ap_config.h" #include "ap/ap_drv_ops.h" +#include "fst/fst.h" #include "config_file.h" #include "eap_register.h" #include "ctrl_iface.h" @@ -533,6 +534,28 @@ static int gen_uuid(const char *txt_addr) #endif /* CONFIG_WPS */ +#ifndef HOSTAPD_CLEANUP_INTERVAL +#define HOSTAPD_CLEANUP_INTERVAL 10 +#endif /* HOSTAPD_CLEANUP_INTERVAL */ + +static int hostapd_periodic_call(struct hostapd_iface *iface, void *ctx) +{ + hostapd_periodic_iface(iface); + return 0; +} + + +/* Periodic cleanup tasks */ +static void hostapd_periodic(void *eloop_ctx, void *timeout_ctx) +{ + struct hapd_interfaces *interfaces = eloop_ctx; + + eloop_register_timeout(HOSTAPD_CLEANUP_INTERVAL, 0, + hostapd_periodic, interfaces, NULL); + hostapd_for_each_interface(interfaces, hostapd_periodic_call, NULL); +} + + int main(int argc, char *argv[]) { struct hapd_interfaces interfaces; @@ -561,6 +584,7 @@ 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; for (;;) { c = getopt(argc, argv, "b:Bde:f:hKP:Ttu:vg:G:"); @@ -661,10 +685,24 @@ int main(int argc, char *argv[]) } if (hostapd_global_init(&interfaces, entropy_file)) { - wpa_printf(MSG_ERROR, "Failed to initilize global context"); + wpa_printf(MSG_ERROR, "Failed to initialize global context"); return -1; } + eloop_register_timeout(HOSTAPD_CLEANUP_INTERVAL, 0, + hostapd_periodic, &interfaces, NULL); + + if (fst_global_init()) { + wpa_printf(MSG_ERROR, + "Failed to initialize global FST context"); + goto out; + } + +#if defined(CONFIG_FST) && defined(CONFIG_CTRL_IFACE) + if (!fst_global_add_ctrl(fst_ctrl_cli)) + wpa_printf(MSG_WARNING, "Failed to add CLI FST ctrl"); +#endif /* CONFIG_FST && CONFIG_CTRL_IFACE */ + /* Allocate and parse configuration for full interface files */ for (i = 0; i < interfaces.count; i++) { interfaces.iface[i] = hostapd_interface_init(&interfaces, @@ -749,6 +787,7 @@ int main(int argc, char *argv[]) } os_free(interfaces.iface); + eloop_cancel_timeout(hostapd_periodic, &interfaces, NULL); hostapd_global_deinit(pid_file); os_free(pid_file); @@ -758,6 +797,8 @@ int main(int argc, char *argv[]) os_free(bss_config); + fst_global_deinit(); + os_program_deinit(); return ret; diff --git a/contrib/wpa/hs20/client/Makefile b/contrib/wpa/hs20/client/Makefile index ca67b54..94cd5f1 100644 --- a/contrib/wpa/hs20/client/Makefile +++ b/contrib/wpa/hs20/client/Makefile @@ -67,7 +67,13 @@ OBJS += ../../src/crypto/sha256-internal.o CFLAGS += $(shell xml2-config --cflags) LIBS += $(shell xml2-config --libs) + +# Allow static/custom linking of libcurl. +ifdef CUST_CURL_LINKAGE +LIBS += ${CUST_CURL_LINKAGE} +else LIBS += -lcurl +endif CFLAGS += -DEAP_TLS_OPENSSL LIBS += -lssl -lcrypto diff --git a/contrib/wpa/hs20/client/osu_client.c b/contrib/wpa/hs20/client/osu_client.c index de7f351..0315f7b 100644 --- a/contrib/wpa/hs20/client/osu_client.c +++ b/contrib/wpa/hs20/client/osu_client.c @@ -25,6 +25,8 @@ #include "crypto/sha256.h" #include "osu_client.h" +const char *spp_xsd_fname = "spp.xsd"; + void write_result(struct hs20_osu_client *ctx, const char *fmt, ...) { @@ -540,6 +542,7 @@ int hs20_add_pps_mo(struct hs20_osu_client *ctx, const char *uri, uri); write_result(ctx, "Unsupported location for addMO to " "add PPS MO (extra directory): '%s'", uri); + free(fqdn); return -1; } *pos = '\0'; /* remove trailing slash and PPS node name */ @@ -547,8 +550,9 @@ int hs20_add_pps_mo(struct hs20_osu_client *ctx, const char *uri, wpa_printf(MSG_INFO, "SP FQDN: %s", fqdn); if (!server_dnsname_suffix_match(ctx, fqdn)) { - wpa_printf(MSG_INFO, "FQDN '%s' for new PPS MO did not have suffix match with server's dNSName values", - fqdn); + wpa_printf(MSG_INFO, + "FQDN '%s' for new PPS MO did not have suffix match with server's dNSName values, count: %d", + fqdn, (int) ctx->server_dnsname_count); write_result(ctx, "FQDN '%s' for new PPS MO did not have suffix match with server's dNSName values", fqdn); free(fqdn); @@ -2094,10 +2098,14 @@ static int osu_connect(struct hs20_osu_client *ctx, const char *bssid, } ctx->no_reconnect = 1; - if (methods & 0x02) + if (methods & 0x02) { + wpa_printf(MSG_DEBUG, "Calling cmd_prov from osu_connect"); res = cmd_prov(ctx, url); - else if (methods & 0x01) + } else if (methods & 0x01) { + wpa_printf(MSG_DEBUG, + "Calling cmd_oma_dm_prov from osu_connect"); res = cmd_oma_dm_prov(ctx, url); + } wpa_printf(MSG_INFO, "Remove OSU network connection"); write_summary(ctx, "Remove OSU network connection"); @@ -2139,7 +2147,7 @@ static int cmd_osu_select(struct hs20_osu_client *ctx, const char *dir, snprintf(fname, sizeof(fname), "%s/osu-providers.txt", dir); osu = parse_osu_providers(fname, &osu_count); if (osu == NULL) { - wpa_printf(MSG_INFO, "Could not any OSU providers from %s", + wpa_printf(MSG_INFO, "Could not find any OSU providers from %s", fname); write_result(ctx, "No OSU providers available"); return -1; @@ -2290,12 +2298,19 @@ selected: } if (connect == 2) { - if (last->methods & 0x02) + if (last->methods & 0x02) { + wpa_printf(MSG_DEBUG, + "Calling cmd_prov from cmd_osu_select"); ret = cmd_prov(ctx, last->url); - else if (last->methods & 0x01) + } else if (last->methods & 0x01) { + wpa_printf(MSG_DEBUG, + "Calling cmd_oma_dm_prov from cmd_osu_select"); ret = cmd_oma_dm_prov(ctx, last->url); - else + } else { + wpa_printf(MSG_DEBUG, + "No supported OSU provisioning method"); ret = -1; + } } else if (connect) ret = osu_connect(ctx, last->bssid, last->osu_ssid, last->url, last->methods, @@ -2690,7 +2705,7 @@ static char * get_hostname(const char *url) end = os_strchr(pos, '/'); end2 = os_strchr(pos, ':'); - if (end && end2 && end2 < end) + if ((end && end2 && end2 < end) || (!end && end2)) end = end2; if (end) end--; @@ -2720,8 +2735,8 @@ static int osu_cert_cb(void *_ctx, struct http_cert *cert) int found; char *host = NULL; - wpa_printf(MSG_INFO, "osu_cert_cb(osu_cert_validation=%d)", - !ctx->no_osu_cert_validation); + wpa_printf(MSG_INFO, "osu_cert_cb(osu_cert_validation=%d, url=%s)", + !ctx->no_osu_cert_validation, ctx->server_url); host = get_hostname(ctx->server_url); @@ -2810,17 +2825,21 @@ static int osu_cert_cb(void *_ctx, struct http_cert *cert) char *name = ctx->icon_filename[j]; size_t name_len = os_strlen(name); - wpa_printf(MSG_INFO, "Looking for icon file name '%s' match", - name); + wpa_printf(MSG_INFO, + "[%i] Looking for icon file name '%s' match", + j, name); for (i = 0; i < cert->num_logo; i++) { struct http_logo *logo = &cert->logo[i]; size_t uri_len = os_strlen(logo->uri); char *pos; - wpa_printf(MSG_INFO, "Comparing to '%s' uri_len=%d name_len=%d", - logo->uri, (int) uri_len, (int) name_len); - if (uri_len < 1 + name_len) + wpa_printf(MSG_INFO, + "[%i] Comparing to '%s' uri_len=%d name_len=%d", + i, logo->uri, (int) uri_len, (int) name_len); + if (uri_len < 1 + name_len) { + wpa_printf(MSG_INFO, "URI Length is too short"); continue; + } pos = &logo->uri[uri_len - name_len - 1]; if (*pos != '/') continue; @@ -2847,17 +2866,30 @@ static int osu_cert_cb(void *_ctx, struct http_cert *cert) for (i = 0; i < cert->num_logo; i++) { struct http_logo *logo = &cert->logo[i]; - if (logo->hash_len != 32) + if (logo->hash_len != 32) { + wpa_printf(MSG_INFO, + "[%i][%i] Icon hash length invalid (should be 32): %d", + j, i, (int) logo->hash_len); continue; + } if (os_memcmp(logo->hash, ctx->icon_hash[j], 32) == 0) { found = 1; break; } + + wpa_printf(MSG_DEBUG, + "[%u][%u] Icon hash did not match", j, i); + wpa_hexdump_ascii(MSG_DEBUG, "logo->hash", + logo->hash, 32); + wpa_hexdump_ascii(MSG_DEBUG, "ctx->icon_hash[j]", + ctx->icon_hash[j], 32); } if (!found) { - wpa_printf(MSG_INFO, "No icon hash match found"); - write_result(ctx, "No icon hash match found"); + wpa_printf(MSG_INFO, + "No icon hash match (by hash) found"); + write_result(ctx, + "No icon hash match (by hash) found"); return -1; } } @@ -2955,6 +2987,7 @@ static void usage(void) " [-w<wpa_supplicant ctrl_iface dir>] " "[-r<result file>] [-f<debug file>] \\\n" " [-s<summary file>] \\\n" + " [-x<spp.xsd file name>] \\\n" " <command> [arguments..]\n" "commands:\n" "- to_tnds <XML MO> <XML MO in TNDS format> [URN]\n" @@ -2996,7 +3029,7 @@ int main(int argc, char *argv[]) return -1; for (;;) { - c = getopt(argc, argv, "df:hi:KNO:qr:s:S:tw:"); + c = getopt(argc, argv, "df:hKNO:qr:s:S:tw:x:"); if (c < 0) break; switch (c) { @@ -3034,6 +3067,9 @@ int main(int argc, char *argv[]) case 'w': wpas_ctrl_path = optarg; break; + case 'x': + spp_xsd_fname = optarg; + break; case 'h': default: usage(); @@ -3108,6 +3144,7 @@ int main(int argc, char *argv[]) exit(0); } ctx.ca_fname = argv[optind + 2]; + wpa_printf(MSG_DEBUG, "Calling cmd_prov from main"); cmd_prov(&ctx, argv[optind + 1]); } else if (strcmp(argv[optind], "sim_prov") == 0) { if (argc - optind < 2) { diff --git a/contrib/wpa/hs20/client/spp_client.c b/contrib/wpa/hs20/client/spp_client.c index 302a050..c619541 100644 --- a/contrib/wpa/hs20/client/spp_client.c +++ b/contrib/wpa/hs20/client/spp_client.c @@ -21,6 +21,8 @@ #include "osu_client.h" +extern const char *spp_xsd_fname; + static int hs20_spp_update_response(struct hs20_osu_client *ctx, const char *session_id, const char *spp_status, @@ -59,7 +61,7 @@ static int hs20_spp_validate(struct hs20_osu_client *ctx, xml_node_t *node, return -1; } - ret = xml_validate(xctx, node, "spp.xsd", &err); + ret = xml_validate(xctx, node, spp_xsd_fname, &err); if (ret < 0) { wpa_printf(MSG_INFO, "XML schema validation error(s)\n%s", err); write_summary(ctx, "SPP XML schema validation failed"); @@ -77,9 +79,14 @@ static void add_mo_container(struct xml_node_ctx *ctx, xml_namespace_t *ns, xml_node_t *fnode, *tnds; char *str; + errno = 0; fnode = node_from_file(ctx, fname); - if (!fnode) + if (!fnode) { + wpa_printf(MSG_ERROR, + "Failed to create XML node from file: %s, possible error: %s", + fname, strerror(errno)); return; + } tnds = mo_to_tnds(ctx, fnode, 0, urn, "syncml:dmddf1.2"); xml_node_free(ctx, fnode); if (!tnds) @@ -952,7 +959,9 @@ int cmd_prov(struct hs20_osu_client *ctx, const char *url) return -1; } - wpa_printf(MSG_INFO, "Credential provisioning requested"); + wpa_printf(MSG_INFO, + "Credential provisioning requested - URL: %s ca_fname: %s", + url, ctx->ca_fname ? ctx->ca_fname : "N/A"); os_free(ctx->server_url); ctx->server_url = os_strdup(url); diff --git a/contrib/wpa/patches/openssl-0.9.8zf-tls-extensions.patch b/contrib/wpa/patches/openssl-0.9.8zf-tls-extensions.patch new file mode 100644 index 0000000..3a8f90e --- /dev/null +++ b/contrib/wpa/patches/openssl-0.9.8zf-tls-extensions.patch @@ -0,0 +1,398 @@ +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 <akobozev@cisco.com> +(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 7c55146..a096de4 100644 --- a/contrib/wpa/src/ap/accounting.c +++ b/contrib/wpa/src/ap/accounting.c @@ -459,10 +459,14 @@ int accounting_init(struct hostapd_data *hapd) { struct os_time now; - /* Acct-Session-Id should be unique over reboots. If reliable clock is - * not available, this could be replaced with reboot counter, etc. */ + /* 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); - hapd->acct_session_id_hi = now.sec; + if (os_get_random((u8 *) &hapd->acct_session_id_hi, + sizeof(hapd->acct_session_id_hi)) < 0) + hapd->acct_session_id_hi = now.sec; + hapd->acct_session_id_hi ^= now.usec; if (radius_client_register(hapd->radius, RADIUS_ACCT, accounting_receive, hapd)) @@ -475,7 +479,7 @@ int accounting_init(struct hostapd_data *hapd) /** - * accounting_deinit: Deinitilize accounting + * accounting_deinit: Deinitialize accounting * @hapd: hostapd BSS data */ void accounting_deinit(struct hostapd_data *hapd) diff --git a/contrib/wpa/src/ap/acs.c b/contrib/wpa/src/ap/acs.c index ae7f6c3..03d797f 100644 --- a/contrib/wpa/src/ap/acs.c +++ b/contrib/wpa/src/ap/acs.c @@ -479,16 +479,10 @@ static int acs_usable_chan(struct hostapd_channel_data *chan) static int is_in_chanlist(struct hostapd_iface *iface, struct hostapd_channel_data *chan) { - int *entry; - - if (!iface->conf->chanlist) + if (!iface->conf->acs_ch_list.num) return 1; - for (entry = iface->conf->chanlist; *entry != -1; entry++) { - if (*entry == chan->chan) - return 1; - } - return 0; + return freq_range_list_includes(&iface->conf->acs_ch_list, chan->chan); } @@ -900,6 +894,9 @@ static int acs_request_scan(struct hostapd_iface *iface) if (chan->flag & HOSTAPD_CHAN_DISABLED) continue; + if (!is_in_chanlist(iface, chan)) + continue; + *freq++ = chan->freq; } *freq = 0; diff --git a/contrib/wpa/src/ap/ap_config.c b/contrib/wpa/src/ap/ap_config.c index 76011dc..9a96e50 100644 --- a/contrib/wpa/src/ap/ap_config.c +++ b/contrib/wpa/src/ap/ap_config.c @@ -172,6 +172,7 @@ struct hostapd_config * hostapd_config_defaults(void) conf->ap_table_max_size = 255; conf->ap_table_expiration_time = 60; + conf->track_sta_max_age = 180; #ifdef CONFIG_TESTING_OPTIONS conf->ignore_probe_probability = 0.0; @@ -181,6 +182,8 @@ struct hostapd_config * hostapd_config_defaults(void) conf->corrupt_gtk_rekey_mic_probability = 0.0; #endif /* CONFIG_TESTING_OPTIONS */ + conf->acs = 0; + conf->acs_ch_list.num = 0; #ifdef CONFIG_ACS conf->acs_num_scans = 5; #endif /* CONFIG_ACS */ @@ -559,6 +562,13 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(conf->server_id); +#ifdef CONFIG_TESTING_OPTIONS + wpabuf_free(conf->own_ie_override); +#endif /* CONFIG_TESTING_OPTIONS */ + + os_free(conf->no_probe_resp_if_seen_on); + os_free(conf->no_auth_if_seen_on); + os_free(conf); } @@ -579,7 +589,7 @@ void hostapd_config_free(struct hostapd_config *conf) os_free(conf->bss); os_free(conf->supported_rates); os_free(conf->basic_rates); - os_free(conf->chanlist); + os_free(conf->acs_ch_list.range); os_free(conf->driver_params); #ifdef CONFIG_ACS os_free(conf->acs_chan_bias); @@ -817,9 +827,9 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss, if (full_config && bss->wps_state && bss->wpa && (!(bss->wpa & 2) || - !(bss->rsn_pairwise & WPA_CIPHER_CCMP))) { + !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)))) { wpa_printf(MSG_INFO, "WPS: WPA/TKIP configuration without " - "WPA2/CCMP forced WPS to be disabled"); + "WPA2/CCMP/GCMP forced WPS to be disabled"); bss->wps_state = 0; } #endif /* CONFIG_WPS */ @@ -841,6 +851,29 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss, } +static int hostapd_config_check_cw(struct hostapd_config *conf, int queue) +{ + int tx_cwmin = conf->tx_queue[queue].cwmin; + int tx_cwmax = conf->tx_queue[queue].cwmax; + int ac_cwmin = conf->wmm_ac_params[queue].cwmin; + int ac_cwmax = conf->wmm_ac_params[queue].cwmax; + + if (tx_cwmin > tx_cwmax) { + wpa_printf(MSG_ERROR, + "Invalid TX queue cwMin/cwMax values. cwMin(%d) greater than cwMax(%d)", + tx_cwmin, tx_cwmax); + return -1; + } + if (ac_cwmin > ac_cwmax) { + wpa_printf(MSG_ERROR, + "Invalid WMM AC cwMin/cwMax values. cwMin(%d) greater than cwMax(%d)", + ac_cwmin, ac_cwmax); + return -1; + } + return 0; +} + + int hostapd_config_check(struct hostapd_config *conf, int full_config) { size_t i; @@ -870,6 +903,11 @@ int hostapd_config_check(struct hostapd_config *conf, int full_config) return -1; } + for (i = 0; i < NUM_TX_QUEUES; i++) { + if (hostapd_config_check_cw(conf, i)) + return -1; + } + for (i = 0; i < conf->num_bss; i++) { if (hostapd_config_check_bss(conf->bss[i], conf, full_config)) return -1; @@ -937,10 +975,11 @@ void hostapd_set_security_params(struct hostapd_bss_config *bss, bss->rsn_pairwise = WPA_CIPHER_CCMP; } else { bss->ssid.security_policy = SECURITY_PLAINTEXT; - bss->wpa_group = WPA_CIPHER_NONE; - bss->wpa_pairwise = WPA_CIPHER_NONE; - bss->rsn_pairwise = WPA_CIPHER_NONE; - if (full_config) + if (full_config) { + bss->wpa_group = WPA_CIPHER_NONE; + bss->wpa_pairwise = WPA_CIPHER_NONE; + bss->rsn_pairwise = WPA_CIPHER_NONE; bss->wpa_key_mgmt = WPA_KEY_MGMT_NONE; + } } } diff --git a/contrib/wpa/src/ap/ap_config.h b/contrib/wpa/src/ap/ap_config.h index 961d2dd..de470a9 100644 --- a/contrib/wpa/src/ap/ap_config.h +++ b/contrib/wpa/src/ap/ap_config.h @@ -1,6 +1,6 @@ /* * hostapd / Configuration definitions and helpers functions - * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -12,8 +12,10 @@ #include "common/defs.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" /** * mesh_conf - local MBSS state and settings @@ -31,8 +33,8 @@ struct mesh_conf { u8 mesh_sp_id; /* Authentication Protocol Identifier */ u8 mesh_auth_id; - u8 *ies; - int ie_len; + u8 *rsn_ie; + int rsn_ie_len; #define MESH_CONF_SEC_NONE BIT(0) #define MESH_CONF_SEC_AUTH BIT(1) #define MESH_CONF_SEC_AMPE BIT(2) @@ -57,8 +59,6 @@ struct hostapd_radius_servers; struct ft_remote_r0kh; struct ft_remote_r1kh; -#define HOSTAPD_MAX_SSID_LEN 32 - #define NUM_WEP_KEYS 4 struct hostapd_wep_keys { u8 idx; @@ -78,7 +78,7 @@ typedef enum hostap_security_policy { } secpolicy; struct hostapd_ssid { - u8 ssid[HOSTAPD_MAX_SSID_LEN]; + u8 ssid[SSID_MAX_LEN]; size_t ssid_len; unsigned int ssid_set:1; unsigned int utf8_ssid:1; @@ -114,12 +114,10 @@ struct hostapd_vlan { struct hostapd_vlan *next; int vlan_id; /* VLAN ID or -1 (VLAN_ID_WILDCARD) for wildcard entry */ char ifname[IFNAMSIZ + 1]; + int configured; int dynamic_vlan; #ifdef CONFIG_FULL_DYNAMIC_VLAN -#define DVLAN_CLEAN_BR 0x1 -#define DVLAN_CLEAN_VLAN 0x2 -#define DVLAN_CLEAN_VLAN_PORT 0x4 #define DVLAN_CLEAN_WLAN_PORT 0x8 int clean; #endif /* CONFIG_FULL_DYNAMIC_VLAN */ @@ -332,6 +330,7 @@ struct hostapd_bss_config { char *private_key; char *private_key_passwd; int check_crl; + unsigned int tls_session_lifetime; char *ocsp_stapling_response; char *dh_file; char *openssl_ciphers; @@ -490,6 +489,7 @@ struct hostapd_bss_config { int osen; int proxy_arp; + int na_mcast_to_ucast; #ifdef CONFIG_HS20 int hs20; int disable_dgaf; @@ -510,7 +510,7 @@ struct hostapd_bss_config { char file[256]; } *hs20_icons; size_t hs20_icons_count; - u8 osu_ssid[HOSTAPD_MAX_SSID_LEN]; + u8 osu_ssid[SSID_MAX_LEN]; size_t osu_ssid_len; struct hs20_osu_provider { unsigned int friendly_name_count; @@ -545,6 +545,7 @@ struct hostapd_bss_config { #ifdef CONFIG_TESTING_OPTIONS u8 bss_load_test[5]; u8 bss_load_test_set; + struct wpabuf *own_ie_override; #endif /* CONFIG_TESTING_OPTIONS */ #define MESH_ENABLED BIT(0) @@ -553,6 +554,9 @@ struct hostapd_bss_config { int radio_measurements; int vendor_vht; + + char *no_probe_resp_if_seen_on; + char *no_auth_if_seen_on; }; @@ -568,7 +572,8 @@ struct hostapd_config { int fragm_threshold; u8 send_probe_response; u8 channel; - int *chanlist; + u8 acs; + struct wpa_freq_range_list acs_ch_list; enum hostapd_hw_mode hw_mode; /* HOSTAPD_MODE_IEEE80211A, .. */ enum { LONG_PREAMBLE = 0, @@ -584,6 +589,9 @@ struct hostapd_config { int ap_table_max_size; int ap_table_expiration_time; + unsigned int track_sta_max_num; + unsigned int track_sta_max_age; + char country[3]; /* first two octets: country code as described in * ISO/IEC 3166-1. Third octet: * ' ' (ascii 32): all environments @@ -620,6 +628,7 @@ struct hostapd_config { u16 ht_capab; int ieee80211n; int secondary_channel; + int no_pri_sec_switch; int require_ht; int obss_interval; u32 vht_capab; @@ -629,6 +638,10 @@ struct hostapd_config { u8 vht_oper_centr_freq_seg0_idx; u8 vht_oper_centr_freq_seg1_idx; +#ifdef CONFIG_FST + struct fst_iface_cfg fst_cfg; +#endif /* CONFIG_FST */ + #ifdef CONFIG_P2P u8 p2p_go_ctwindow; #endif /* CONFIG_P2P */ diff --git a/contrib/wpa/src/ap/ap_drv_ops.c b/contrib/wpa/src/ap/ap_drv_ops.c index e16306c..6cafcb7 100644 --- a/contrib/wpa/src/ap/ap_drv_ops.c +++ b/contrib/wpa/src/ap/ap_drv_ops.c @@ -81,6 +81,22 @@ int hostapd_build_ap_extra_ies(struct hostapd_data *hapd, wpabuf_put_data(proberesp, buf, pos - buf); } +#ifdef CONFIG_FST + if (hapd->iface->fst_ies) { + size_t add = wpabuf_len(hapd->iface->fst_ies); + + if (wpabuf_resize(&beacon, add) < 0) + goto fail; + wpabuf_put_buf(beacon, hapd->iface->fst_ies); + if (wpabuf_resize(&proberesp, add) < 0) + goto fail; + wpabuf_put_buf(proberesp, hapd->iface->fst_ies); + if (wpabuf_resize(&assocresp, add) < 0) + goto fail; + wpabuf_put_buf(assocresp, hapd->iface->fst_ies); + } +#endif /* CONFIG_FST */ + if (hapd->wps_beacon_ie) { if (wpabuf_resize(&beacon, wpabuf_len(hapd->wps_beacon_ie)) < 0) @@ -217,6 +233,15 @@ void hostapd_free_ap_extra_ies(struct hostapd_data *hapd, } +int hostapd_reset_ap_wps_ie(struct hostapd_data *hapd) +{ + if (hapd->driver == NULL || hapd->driver->set_ap_wps_ie == NULL) + return 0; + + return hapd->driver->set_ap_wps_ie(hapd->drv_priv, NULL, NULL, NULL); +} + + int hostapd_set_ap_wps_ie(struct hostapd_data *hapd) { struct wpabuf *beacon, *proberesp, *assocresp; @@ -281,8 +306,14 @@ int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname, params.wpa = hapd->conf->wpa; params.ieee802_1x = hapd->conf->ieee802_1x; params.wpa_group = hapd->conf->wpa_group; - params.wpa_pairwise = hapd->conf->wpa_pairwise | - hapd->conf->rsn_pairwise; + if ((hapd->conf->wpa & (WPA_PROTO_WPA | WPA_PROTO_RSN)) == + (WPA_PROTO_WPA | WPA_PROTO_RSN)) + params.wpa_pairwise = hapd->conf->wpa_pairwise | + hapd->conf->rsn_pairwise; + else if (hapd->conf->wpa & WPA_PROTO_RSN) + params.wpa_pairwise = hapd->conf->rsn_pairwise; + else if (hapd->conf->wpa & WPA_PROTO_WPA) + params.wpa_pairwise = hapd->conf->wpa_pairwise; params.wpa_key_mgmt = hapd->conf->wpa_key_mgmt; params.rsn_preauth = hapd->conf->rsn_preauth; #ifdef CONFIG_IEEE80211W @@ -618,7 +649,7 @@ int hostapd_drv_send_mlme(struct hostapd_data *hapd, { if (hapd->driver == NULL || hapd->driver->send_mlme == NULL) return 0; - return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack); + return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0); } @@ -712,16 +743,100 @@ int hostapd_drv_set_qos_map(struct hostapd_data *hapd, } +static void hostapd_get_hw_mode_any_channels(struct hostapd_data *hapd, + struct hostapd_hw_modes *mode, + int acs_ch_list_all, + int **freq_list) +{ + int i; + + for (i = 0; i < mode->num_channels; i++) { + struct hostapd_channel_data *chan = &mode->channels[i]; + + if ((acs_ch_list_all || + freq_range_list_includes(&hapd->iface->conf->acs_ch_list, + chan->chan)) && + !(chan->flag & HOSTAPD_CHAN_DISABLED)) + int_array_add_unique(freq_list, chan->freq); + } +} + + int hostapd_drv_do_acs(struct hostapd_data *hapd) { struct drv_acs_params params; + int ret, i, acs_ch_list_all = 0; + u8 *channels = NULL; + unsigned int num_channels = 0; + struct hostapd_hw_modes *mode; + int *freq_list = NULL; if (hapd->driver == NULL || hapd->driver->do_acs == NULL) return 0; + os_memset(¶ms, 0, sizeof(params)); params.hw_mode = hapd->iface->conf->hw_mode; + + /* + * If no chanlist config parameter is provided, include all enabled + * channels of the selected hw_mode. + */ + if (!hapd->iface->conf->acs_ch_list.num) + acs_ch_list_all = 1; + + mode = hapd->iface->current_mode; + if (mode) { + channels = os_malloc(mode->num_channels); + if (channels == NULL) + return -1; + + for (i = 0; i < mode->num_channels; i++) { + struct hostapd_channel_data *chan = &mode->channels[i]; + if (!acs_ch_list_all && + !freq_range_list_includes( + &hapd->iface->conf->acs_ch_list, + chan->chan)) + continue; + if (!(chan->flag & HOSTAPD_CHAN_DISABLED)) { + channels[num_channels++] = chan->chan; + int_array_add_unique(&freq_list, chan->freq); + } + } + } else { + for (i = 0; i < hapd->iface->num_hw_features; i++) { + mode = &hapd->iface->hw_features[i]; + hostapd_get_hw_mode_any_channels(hapd, mode, + acs_ch_list_all, + &freq_list); + } + } + + params.ch_list = channels; + params.ch_list_len = num_channels; + params.freq_list = freq_list; + params.ht_enabled = !!(hapd->iface->conf->ieee80211n); params.ht40_enabled = !!(hapd->iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET); - return hapd->driver->do_acs(hapd->drv_priv, ¶ms); + params.vht_enabled = !!(hapd->iface->conf->ieee80211ac); + params.ch_width = 20; + if (hapd->iface->conf->ieee80211n && params.ht40_enabled) + params.ch_width = 40; + + /* Note: VHT20 is defined by combination of ht_capab & vht_oper_chwidth + */ + if (hapd->iface->conf->ieee80211ac && params.ht40_enabled) { + if (hapd->iface->conf->vht_oper_chwidth == VHT_CHANWIDTH_80MHZ) + params.ch_width = 80; + else if (hapd->iface->conf->vht_oper_chwidth == + VHT_CHANWIDTH_160MHZ || + hapd->iface->conf->vht_oper_chwidth == + VHT_CHANWIDTH_80P80MHZ) + params.ch_width = 160; + } + + ret = hapd->driver->do_acs(hapd->drv_priv, ¶ms); + os_free(channels); + + return ret; } diff --git a/contrib/wpa/src/ap/ap_drv_ops.h b/contrib/wpa/src/ap/ap_drv_ops.h index 5d07e71..82eaf3f 100644 --- a/contrib/wpa/src/ap/ap_drv_ops.h +++ b/contrib/wpa/src/ap/ap_drv_ops.h @@ -24,6 +24,7 @@ int hostapd_build_ap_extra_ies(struct hostapd_data *hapd, void hostapd_free_ap_extra_ies(struct hostapd_data *hapd, struct wpabuf *beacon, struct wpabuf *proberesp, struct wpabuf *assocresp); +int hostapd_reset_ap_wps_ie(struct hostapd_data *hapd); int hostapd_set_ap_wps_ie(struct hostapd_data *hapd); int hostapd_set_authorized(struct hostapd_data *hapd, struct sta_info *sta, int authorized); diff --git a/contrib/wpa/src/ap/ap_list.c b/contrib/wpa/src/ap/ap_list.c index 04a56a9..8bf6dde 100644 --- a/contrib/wpa/src/ap/ap_list.c +++ b/contrib/wpa/src/ap/ap_list.c @@ -193,14 +193,14 @@ void ap_list_process_beacon(struct hostapd_iface *iface, elems->supp_rates, elems->supp_rates_len, elems->ext_supp_rates, elems->ext_supp_rates_len); - if (elems->erp_info && elems->erp_info_len == 1) + if (elems->erp_info) ap->erp = elems->erp_info[0]; else ap->erp = -1; - if (elems->ds_params && elems->ds_params_len == 1) + if (elems->ds_params) ap->channel = elems->ds_params[0]; - else if (elems->ht_operation && elems->ht_operation_len >= 1) + else if (elems->ht_operation) ap->channel = elems->ht_operation[0]; else if (fi) ap->channel = fi->channel; @@ -248,15 +248,12 @@ void ap_list_process_beacon(struct hostapd_iface *iface, } -static void ap_list_timer(void *eloop_ctx, void *timeout_ctx) +void ap_list_timer(struct hostapd_iface *iface) { - struct hostapd_iface *iface = eloop_ctx; struct os_reltime now; struct ap_info *ap; int set_beacon = 0; - eloop_register_timeout(10, 0, ap_list_timer, iface, NULL); - if (!iface->ap_list) return; @@ -305,13 +302,11 @@ static void ap_list_timer(void *eloop_ctx, void *timeout_ctx) int ap_list_init(struct hostapd_iface *iface) { - eloop_register_timeout(10, 0, ap_list_timer, iface, NULL); return 0; } void ap_list_deinit(struct hostapd_iface *iface) { - eloop_cancel_timeout(ap_list_timer, iface, NULL); hostapd_free_aps(iface); } diff --git a/contrib/wpa/src/ap/ap_list.h b/contrib/wpa/src/ap/ap_list.h index 93dc0ed..9e0353c 100644 --- a/contrib/wpa/src/ap/ap_list.h +++ b/contrib/wpa/src/ap/ap_list.h @@ -39,6 +39,7 @@ void ap_list_process_beacon(struct hostapd_iface *iface, #ifdef NEED_AP_MLME int ap_list_init(struct hostapd_iface *iface); void ap_list_deinit(struct hostapd_iface *iface); +void ap_list_timer(struct hostapd_iface *iface); #else /* NEED_AP_MLME */ static inline int ap_list_init(struct hostapd_iface *iface) { @@ -48,6 +49,10 @@ static inline int ap_list_init(struct hostapd_iface *iface) static inline void ap_list_deinit(struct hostapd_iface *iface) { } + +static inline void ap_list_timer(struct hostapd_iface *iface) +{ +} #endif /* NEED_AP_MLME */ #endif /* AP_LIST_H */ diff --git a/contrib/wpa/src/ap/authsrv.c b/contrib/wpa/src/ap/authsrv.c index bd1778e..934dcfc 100644 --- a/contrib/wpa/src/ap/authsrv.c +++ b/contrib/wpa/src/ap/authsrv.c @@ -55,10 +55,11 @@ static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity, { const struct hostapd_eap_user *eap_user; int i; + int rv = -1; eap_user = hostapd_get_eap_user(ctx, identity, identity_len, phase2); if (eap_user == NULL) - return -1; + goto out; if (user == NULL) return 0; @@ -72,7 +73,7 @@ static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity, if (eap_user->password) { user->password = os_malloc(eap_user->password_len); if (user->password == NULL) - return -1; + goto out; os_memcpy(user->password, eap_user->password, eap_user->password_len); user->password_len = eap_user->password_len; @@ -83,8 +84,13 @@ static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity, user->ttls_auth = eap_user->ttls_auth; user->remediation = eap_user->remediation; user->accept_attr = eap_user->accept_attr; + rv = 0; - return 0; +out: + if (rv) + wpa_printf(MSG_DEBUG, "%s: Failed to find user", __func__); + + return rv; } @@ -126,6 +132,7 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd) #endif /* CONFIG_HS20 */ srv.erp = conf->eap_server_erp; srv.erp_domain = conf->erp_domain; + srv.tls_session_lifetime = conf->tls_session_lifetime; hapd->radius_srv = radius_server_init(&srv); if (hapd->radius_srv == NULL) { @@ -145,9 +152,12 @@ int authsrv_init(struct hostapd_data *hapd) if (hapd->conf->eap_server && (hapd->conf->ca_cert || hapd->conf->server_cert || hapd->conf->private_key || hapd->conf->dh_file)) { + struct tls_config conf; struct tls_connection_params params; - hapd->ssl_ctx = tls_init(NULL); + os_memset(&conf, 0, sizeof(conf)); + conf.tls_session_lifetime = hapd->conf->tls_session_lifetime; + hapd->ssl_ctx = tls_init(&conf); if (hapd->ssl_ctx == NULL) { wpa_printf(MSG_ERROR, "Failed to initialize TLS"); authsrv_deinit(hapd); diff --git a/contrib/wpa/src/ap/beacon.c b/contrib/wpa/src/ap/beacon.c index e575b65..5fe8fd5 100644 --- a/contrib/wpa/src/ap/beacon.c +++ b/contrib/wpa/src/ap/beacon.c @@ -360,7 +360,6 @@ static u8 * hostapd_add_csa_elems(struct hostapd_data *hapd, u8 *pos, static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, - struct sta_info *sta, const struct ieee80211_mgmt *req, int is_p2p, size_t *resp_len) { @@ -378,6 +377,10 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, if (hapd->p2p_probe_resp_ie) buflen += wpabuf_len(hapd->p2p_probe_resp_ie); #endif /* CONFIG_P2P */ +#ifdef CONFIG_FST + if (hapd->iface->fst_ies) + buflen += wpabuf_len(hapd->iface->fst_ies); +#endif /* CONFIG_FST */ if (hapd->conf->vendor_elements) buflen += wpabuf_len(hapd->conf->vendor_elements); if (hapd->conf->vendor_vht) { @@ -402,7 +405,7 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, /* hardware or low-level driver will setup seq_ctrl and timestamp */ resp->u.probe_resp.capab_info = - host_to_le16(hostapd_own_capab_info(hapd, sta, 1)); + host_to_le16(hostapd_own_capab_info(hapd)); pos = resp->u.probe_resp.variable; *pos++ = WLAN_EID_SSID; @@ -450,6 +453,15 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, 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), + wpabuf_len(hapd->iface->fst_ies)); + pos += wpabuf_len(hapd->iface->fst_ies); + } +#endif /* CONFIG_FST */ + #ifdef CONFIG_IEEE80211AC if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) { pos = hostapd_eid_vht_capabilities(hapd, pos); @@ -540,6 +552,102 @@ static enum ssid_match_result ssid_match(struct hostapd_data *hapd, } +void sta_track_expire(struct hostapd_iface *iface, int force) +{ + struct os_reltime now; + struct hostapd_sta_info *info; + + if (!iface->num_sta_seen) + return; + + os_get_reltime(&now); + while ((info = dl_list_first(&iface->sta_seen, struct hostapd_sta_info, + list))) { + if (!force && + !os_reltime_expired(&now, &info->last_seen, + iface->conf->track_sta_max_age)) + break; + force = 0; + + wpa_printf(MSG_MSGDUMP, "%s: Expire STA tracking entry for " + MACSTR, iface->bss[0]->conf->iface, + MAC2STR(info->addr)); + dl_list_del(&info->list); + iface->num_sta_seen--; + os_free(info); + } +} + + +static struct hostapd_sta_info * sta_track_get(struct hostapd_iface *iface, + const u8 *addr) +{ + struct hostapd_sta_info *info; + + dl_list_for_each(info, &iface->sta_seen, struct hostapd_sta_info, list) + if (os_memcmp(addr, info->addr, ETH_ALEN) == 0) + return info; + + return NULL; +} + + +void sta_track_add(struct hostapd_iface *iface, const u8 *addr) +{ + struct hostapd_sta_info *info; + + info = sta_track_get(iface, addr); + if (info) { + /* Move the most recent entry to the end of the list */ + dl_list_del(&info->list); + dl_list_add_tail(&iface->sta_seen, &info->list); + os_get_reltime(&info->last_seen); + return; + } + + /* Add a new entry */ + info = os_zalloc(sizeof(*info)); + os_memcpy(info->addr, addr, ETH_ALEN); + os_get_reltime(&info->last_seen); + + if (iface->num_sta_seen >= iface->conf->track_sta_max_num) { + /* Expire oldest entry to make room for a new one */ + sta_track_expire(iface, 1); + } + + wpa_printf(MSG_MSGDUMP, "%s: Add STA tracking entry for " + MACSTR, iface->bss[0]->conf->iface, MAC2STR(addr)); + dl_list_add_tail(&iface->sta_seen, &info->list); + iface->num_sta_seen++; +} + + +struct hostapd_data * +sta_track_seen_on(struct hostapd_iface *iface, const u8 *addr, + const char *ifname) +{ + struct hapd_interfaces *interfaces = iface->interfaces; + size_t i, j; + + for (i = 0; i < interfaces->count; i++) { + struct hostapd_data *hapd = NULL; + + iface = interfaces->iface[i]; + for (j = 0; j < iface->num_bss; j++) { + hapd = iface->bss[j]; + if (os_strcmp(ifname, hapd->conf->iface) == 0) + break; + hapd = NULL; + } + + if (hapd && sta_track_get(iface, addr)) + return hapd; + } + + return NULL; +} + + void handle_probe_req(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, int ssi_signal) @@ -548,7 +656,6 @@ void handle_probe_req(struct hostapd_data *hapd, struct ieee802_11_elems elems; const u8 *ie; size_t ie_len; - struct sta_info *sta = NULL; size_t i, resp_len; int noack; enum ssid_match_result res; @@ -556,6 +663,8 @@ void handle_probe_req(struct hostapd_data *hapd, ie = mgmt->u.probe_req.variable; if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)) return; + if (hapd->iconf->track_sta_max_num) + sta_track_add(hapd->iface, mgmt->sa); ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)); for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) @@ -590,7 +699,7 @@ void handle_probe_req(struct hostapd_data *hapd, * is less likely to see them (Probe Request frame sent on a * neighboring, but partially overlapping, channel). */ - if (elems.ds_params && elems.ds_params_len == 1 && + if (elems.ds_params && hapd->iface->current_mode && (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G || hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211B) && @@ -635,8 +744,6 @@ void handle_probe_req(struct hostapd_data *hapd, return; } - sta = ap_get_sta(hapd, mgmt->sa); - #ifdef CONFIG_P2P if ((hapd->conf->p2p & P2P_GROUP_OWNER) && elems.ssid_len == P2P_WILDCARD_SSID_LEN && @@ -649,10 +756,7 @@ void handle_probe_req(struct hostapd_data *hapd, res = ssid_match(hapd, elems.ssid, elems.ssid_len, elems.ssid_list, elems.ssid_list_len); - if (res != NO_SSID_MATCH) { - if (sta) - sta->ssid_probe = &hapd->conf->ssid; - } else { + if (res == NO_SSID_MATCH) { if (!(mgmt->da[0] & 0x01)) { wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for foreign SSID '%s' (DA " MACSTR ")%s", @@ -709,6 +813,18 @@ void handle_probe_req(struct hostapd_data *hapd, /* TODO: verify that supp_rates contains at least one matching rate * with AP configuration */ + if (hapd->conf->no_probe_resp_if_seen_on && + is_multicast_ether_addr(mgmt->da) && + is_multicast_ether_addr(mgmt->bssid) && + sta_track_seen_on(hapd->iface, mgmt->sa, + hapd->conf->no_probe_resp_if_seen_on)) { + wpa_printf(MSG_MSGDUMP, "%s: Ignore Probe Request from " MACSTR + " since STA has been seen on %s", + hapd->conf->iface, MAC2STR(mgmt->sa), + hapd->conf->no_probe_resp_if_seen_on); + return; + } + #ifdef CONFIG_TESTING_OPTIONS if (hapd->iconf->ignore_probe_probability > 0.0 && drand48() < hapd->iconf->ignore_probe_probability) { @@ -719,7 +835,7 @@ void handle_probe_req(struct hostapd_data *hapd, } #endif /* CONFIG_TESTING_OPTIONS */ - resp = hostapd_gen_probe_resp(hapd, sta, mgmt, elems.p2p != NULL, + resp = hostapd_gen_probe_resp(hapd, mgmt, elems.p2p != NULL, &resp_len); if (resp == NULL) return; @@ -774,7 +890,7 @@ static u8 * hostapd_probe_resp_offloads(struct hostapd_data *hapd, "this"); /* Generate a Probe Response template for the non-P2P case */ - return hostapd_gen_probe_resp(hapd, NULL, NULL, 0, resp_len); + return hostapd_gen_probe_resp(hapd, NULL, 0, resp_len); } #endif /* NEED_AP_MLME */ @@ -804,6 +920,10 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, if (hapd->p2p_beacon_ie) tail_len += wpabuf_len(hapd->p2p_beacon_ie); #endif /* CONFIG_P2P */ +#ifdef CONFIG_FST + if (hapd->iface->fst_ies) + tail_len += wpabuf_len(hapd->iface->fst_ies); +#endif /* CONFIG_FST */ if (hapd->conf->vendor_elements) tail_len += wpabuf_len(hapd->conf->vendor_elements); @@ -833,7 +953,7 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, host_to_le16(hapd->iconf->beacon_int); /* hardware or low-level driver will setup seq_ctrl and timestamp */ - capab_info = hostapd_own_capab_info(hapd, NULL, 0); + capab_info = hostapd_own_capab_info(hapd); head->u.beacon.capab_info = host_to_le16(capab_info); pos = &head->u.beacon.variable[0]; @@ -902,6 +1022,15 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, 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) { + os_memcpy(tailpos, wpabuf_head(hapd->iface->fst_ies), + wpabuf_len(hapd->iface->fst_ies)); + tailpos += wpabuf_len(hapd->iface->fst_ies); + } +#endif /* CONFIG_FST */ + #ifdef CONFIG_IEEE80211AC if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) { tailpos = hostapd_eid_vht_capabilities(hapd, tailpos); @@ -963,8 +1092,14 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, params->basic_rates = hapd->iface->basic_rates; params->ssid = hapd->conf->ssid.ssid; params->ssid_len = hapd->conf->ssid.ssid_len; - params->pairwise_ciphers = hapd->conf->wpa_pairwise | - hapd->conf->rsn_pairwise; + if ((hapd->conf->wpa & (WPA_PROTO_WPA | WPA_PROTO_RSN)) == + (WPA_PROTO_WPA | WPA_PROTO_RSN)) + params->pairwise_ciphers = hapd->conf->wpa_pairwise | + hapd->conf->rsn_pairwise; + else if (hapd->conf->wpa & WPA_PROTO_RSN) + params->pairwise_ciphers = hapd->conf->rsn_pairwise; + else if (hapd->conf->wpa & WPA_PROTO_WPA) + params->pairwise_ciphers = hapd->conf->wpa_pairwise; params->group_cipher = hapd->conf->wpa_group; params->key_mgmt_suites = hapd->conf->wpa_key_mgmt; params->auth_algs = hapd->conf->auth_algs; diff --git a/contrib/wpa/src/ap/beacon.h b/contrib/wpa/src/ap/beacon.h index 722159a..d98f42e 100644 --- a/contrib/wpa/src/ap/beacon.h +++ b/contrib/wpa/src/ap/beacon.h @@ -21,5 +21,10 @@ int ieee802_11_update_beacons(struct hostapd_iface *iface); 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_expire(struct hostapd_iface *iface, int force); +struct hostapd_data * +sta_track_seen_on(struct hostapd_iface *iface, const u8 *addr, + const char *ifname); #endif /* BEACON_H */ diff --git a/contrib/wpa/src/ap/ctrl_iface_ap.c b/contrib/wpa/src/ap/ctrl_iface_ap.c index 41ab988..c98978f 100644 --- a/contrib/wpa/src/ap/ctrl_iface_ap.c +++ b/contrib/wpa/src/ap/ctrl_iface_ap.c @@ -12,6 +12,7 @@ #include "common/ieee802_11_defs.h" #include "common/sae.h" #include "eapol_auth/eapol_auth_sm.h" +#include "fst/fst_ctrl_iface.h" #include "hostapd.h" #include "ieee802_1x.h" #include "wpa_auth.h" @@ -153,6 +154,13 @@ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd, } #endif /* CONFIG_SAE */ + if (sta->vlan_id > 0) { + res = os_snprintf(buf + len, buflen - len, "vlan_id=%d\n", + sta->vlan_id); + if (!os_snprintf_error(buflen - len, res)) + len += res; + } + return len; } @@ -199,7 +207,10 @@ int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, const char *txtaddr, return -1; } - return hostapd_ctrl_iface_sta_mib(hapd, sta, buf, buflen); + ret = hostapd_ctrl_iface_sta_mib(hapd, sta, buf, buflen); + ret += fst_ctrl_iface_mb_info(addr, buf + ret, buflen - ret); + + return ret; } diff --git a/contrib/wpa/src/ap/dfs.c b/contrib/wpa/src/ap/dfs.c index da6fd46..715f19b 100644 --- a/contrib/wpa/src/ap/dfs.c +++ b/contrib/wpa/src/ap/dfs.c @@ -122,6 +122,20 @@ static int dfs_is_chan_allowed(struct hostapd_channel_data *chan, int n_chans) } +static struct hostapd_channel_data * +dfs_get_chan_data(struct hostapd_hw_modes *mode, int freq, int first_chan_idx) +{ + int i; + + for (i = first_chan_idx; i < mode->num_channels; i++) { + if (mode->channels[i].freq == freq) + return &mode->channels[i]; + } + + return NULL; +} + + static int dfs_chan_range_available(struct hostapd_hw_modes *mode, int first_chan_idx, int num_chans, int skip_radar) @@ -129,15 +143,15 @@ static int dfs_chan_range_available(struct hostapd_hw_modes *mode, struct hostapd_channel_data *first_chan, *chan; int i; - if (first_chan_idx + num_chans >= mode->num_channels) + if (first_chan_idx + num_chans > mode->num_channels) return 0; first_chan = &mode->channels[first_chan_idx]; for (i = 0; i < num_chans; i++) { - chan = &mode->channels[first_chan_idx + i]; - - if (first_chan->freq + i * 20 != chan->freq) + chan = dfs_get_chan_data(mode, first_chan->freq + i * 20, + first_chan_idx); + if (!chan) return 0; if (!dfs_channel_available(chan, skip_radar)) @@ -151,16 +165,10 @@ static int dfs_chan_range_available(struct hostapd_hw_modes *mode, static int is_in_chanlist(struct hostapd_iface *iface, struct hostapd_channel_data *chan) { - int *entry; - - if (!iface->conf->chanlist) + if (!iface->conf->acs_ch_list.num) return 1; - for (entry = iface->conf->chanlist; *entry != -1; entry++) { - if (*entry == chan->chan) - return 1; - } - return 0; + return freq_range_list_includes(&iface->conf->acs_ch_list, chan->chan); } diff --git a/contrib/wpa/src/ap/drv_callbacks.c b/contrib/wpa/src/ap/drv_callbacks.c index a0adc67..ca8b75c 100644 --- a/contrib/wpa/src/ap/drv_callbacks.c +++ b/contrib/wpa/src/ap/drv_callbacks.c @@ -18,6 +18,7 @@ #include "crypto/random.h" #include "p2p/p2p.h" #include "wps/wps.h" +#include "fst/fst.h" #include "wnm_ap.h" #include "hostapd.h" #include "ieee802_11.h" @@ -42,10 +43,10 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, struct ieee802_11_elems elems; const u8 *ie; size_t ielen; -#ifdef CONFIG_IEEE80211R +#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) u8 buf[sizeof(struct ieee80211_mgmt) + 1024]; u8 *p = buf; -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */ u16 reason = WLAN_REASON_UNSPECIFIED; u16 status = WLAN_STATUS_SUCCESS; const u8 *p2p_dev_addr = NULL; @@ -58,8 +59,8 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, * running, so better make sure we stop processing such an * event here. */ - wpa_printf(MSG_DEBUG, "hostapd_notif_assoc: Skip event with " - "no address"); + wpa_printf(MSG_DEBUG, + "hostapd_notif_assoc: Skip event with no address"); return -1; } random_add_randomness(addr, ETH_ALEN); @@ -89,8 +90,8 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, } else { ie = NULL; ielen = 0; - wpa_printf(MSG_DEBUG, "STA did not include WPS/RSN/WPA IE in " - "(Re)AssocReq"); + wpa_printf(MSG_DEBUG, + "STA did not include WPS/RSN/WPA IE in (Re)AssocReq"); } sta = ap_get_sta(hapd, addr); @@ -126,8 +127,6 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, #ifdef CONFIG_IEEE80211N #ifdef NEED_AP_MLME if (elems.ht_capabilities && - elems.ht_capabilities_len >= - sizeof(struct ieee80211_ht_capabilities) && (hapd->iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) { struct ieee80211_ht_capabilities *ht_cap = @@ -157,13 +156,20 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, sta->hs20_ie = NULL; #endif /* CONFIG_HS20 */ +#ifdef CONFIG_FST + wpabuf_free(sta->mb_ies); + if (hapd->iface->fst) + sta->mb_ies = mb_ies_by_info(&elems.mb_ies); + else + sta->mb_ies = NULL; +#endif /* CONFIG_FST */ + if (hapd->conf->wpa) { if (ie == NULL || ielen == 0) { #ifdef CONFIG_WPS if (hapd->conf->wps_state) { - wpa_printf(MSG_DEBUG, "STA did not include " - "WPA/RSN IE in (Re)Association " - "Request - possible WPS use"); + wpa_printf(MSG_DEBUG, + "STA did not include WPA/RSN IE in (Re)Association Request - possible WPS use"); sta->flags |= WLAN_STA_MAYBE_WPS; goto skip_wpa_check; } @@ -176,13 +182,14 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, if (hapd->conf->wps_state && ie[0] == 0xdd && ie[1] >= 4 && os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) { struct wpabuf *wps; + sta->flags |= WLAN_STA_WPS; wps = ieee802_11_vendor_ie_concat(ie, ielen, WPS_IE_VENDOR_TYPE); if (wps) { if (wps_is_20(wps)) { - wpa_printf(MSG_DEBUG, "WPS: STA " - "supports WPS 2.0"); + wpa_printf(MSG_DEBUG, + "WPS: STA supports WPS 2.0"); sta->flags |= WLAN_STA_WPS2; } wpabuf_free(wps); @@ -196,16 +203,17 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, sta->addr, p2p_dev_addr); if (sta->wpa_sm == NULL) { - wpa_printf(MSG_ERROR, "Failed to initialize WPA state " - "machine"); + wpa_printf(MSG_ERROR, + "Failed to initialize WPA state machine"); return -1; } res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, ie, ielen, elems.mdie, elems.mdie_len); if (res != WPA_IE_OK) { - wpa_printf(MSG_DEBUG, "WPA/RSN information element " - "rejected? (res %u)", res); + wpa_printf(MSG_DEBUG, + "WPA/RSN information element rejected? (res %u)", + res); wpa_hexdump(MSG_DEBUG, "IE", ie, ielen); if (res == WPA_INVALID_GROUP) { reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID; @@ -248,14 +256,12 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, if (sta->sa_query_count == 0) ap_sta_start_sa_query(hapd, sta); -#ifdef CONFIG_IEEE80211R status = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY; p = hostapd_eid_assoc_comeback_time(hapd, sta, p); hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf); -#endif /* CONFIG_IEEE80211R */ return 0; } @@ -283,6 +289,7 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, } else if (hapd->conf->wps_state) { #ifdef CONFIG_WPS struct wpabuf *wps; + if (req_ies) wps = ieee802_11_vendor_ie_concat(req_ies, req_ies_len, WPS_IE_VENDOR_TYPE); @@ -299,8 +306,8 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, if (wps) { sta->flags |= WLAN_STA_WPS; if (wps_is_20(wps)) { - wpa_printf(MSG_DEBUG, "WPS: STA supports " - "WPS 2.0"); + wpa_printf(MSG_DEBUG, + "WPS: STA supports WPS 2.0"); sta->flags |= WLAN_STA_WPS2; } } else @@ -322,8 +329,8 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr, NULL); if (sta->wpa_sm == NULL) { - wpa_printf(MSG_WARNING, "Failed to initialize WPA " - "state machine"); + wpa_printf(MSG_WARNING, + "Failed to initialize WPA state machine"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } if (wpa_validate_osen(hapd->wpa_auth, sta->wpa_sm, @@ -395,8 +402,8 @@ void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr) * was running, so better make sure we stop processing such an * event here. */ - wpa_printf(MSG_DEBUG, "hostapd_notif_disassoc: Skip event " - "with no address"); + wpa_printf(MSG_DEBUG, + "hostapd_notif_disassoc: Skip event with no address"); return; } @@ -405,8 +412,9 @@ void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr) sta = ap_get_sta(hapd, addr); if (sta == NULL) { - wpa_printf(MSG_DEBUG, "Disassociation notification for " - "unknown STA " MACSTR, MAC2STR(addr)); + wpa_printf(MSG_DEBUG, + "Disassociation notification for unknown STA " + MACSTR, MAC2STR(addr)); return; } @@ -427,8 +435,8 @@ void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr) return; hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_INFO, "disconnected due to excessive " - "missing ACKs"); + HOSTAPD_LEVEL_INFO, + "disconnected due to excessive missing ACKs"); hostapd_drv_sta_disassoc(hapd, addr, WLAN_REASON_DISASSOC_LOW_ACK); if (sta) ap_sta_disassociate(hapd, sta, WLAN_REASON_DISASSOC_LOW_ACK); @@ -452,8 +460,8 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, channel = hostapd_hw_get_channel(hapd, freq); if (!channel) { hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_WARNING, "driver switched to " - "bad channel!"); + HOSTAPD_LEVEL_WARNING, + "driver switched to bad channel!"); return; } @@ -532,10 +540,9 @@ void hostapd_event_connect_failed_reason(struct hostapd_data *hapd, #ifdef CONFIG_ACS static void hostapd_acs_channel_selected(struct hostapd_data *hapd, - u8 pri_channel, u8 sec_channel) + struct acs_selected_channels *acs_res) { - int channel; - int ret; + int ret, i; if (hapd->iconf->channel) { wpa_printf(MSG_INFO, "ACS: Channel was already set to %d", @@ -543,29 +550,73 @@ static void hostapd_acs_channel_selected(struct hostapd_data *hapd, return; } - hapd->iface->freq = hostapd_hw_get_freq(hapd, pri_channel); + if (!hapd->iface->current_mode) { + for (i = 0; i < hapd->iface->num_hw_features; i++) { + struct hostapd_hw_modes *mode = + &hapd->iface->hw_features[i]; - channel = pri_channel; - if (!channel) { + if (mode->mode == acs_res->hw_mode) { + hapd->iface->current_mode = mode; + break; + } + } + if (!hapd->iface->current_mode) { + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, + "driver selected to bad hw_mode"); + return; + } + } + + hapd->iface->freq = hostapd_hw_get_freq(hapd, acs_res->pri_channel); + + if (!acs_res->pri_channel) { hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_WARNING, "driver switched to bad channel"); return; } - hapd->iconf->channel = channel; + hapd->iconf->channel = acs_res->pri_channel; + hapd->iconf->acs = 1; - if (sec_channel == 0) + if (acs_res->sec_channel == 0) hapd->iconf->secondary_channel = 0; - else if (sec_channel < pri_channel) + else if (acs_res->sec_channel < acs_res->pri_channel) hapd->iconf->secondary_channel = -1; - else if (sec_channel > pri_channel) + else if (acs_res->sec_channel > acs_res->pri_channel) hapd->iconf->secondary_channel = 1; else { wpa_printf(MSG_ERROR, "Invalid secondary channel!"); return; } + if (hapd->iface->conf->ieee80211ac) { + /* set defaults for backwards compatibility */ + hapd->iconf->vht_oper_centr_freq_seg1_idx = 0; + hapd->iconf->vht_oper_centr_freq_seg0_idx = 0; + hapd->iconf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT; + if (acs_res->ch_width == 80) { + hapd->iconf->vht_oper_centr_freq_seg0_idx = + acs_res->vht_seg0_center_ch; + hapd->iconf->vht_oper_chwidth = VHT_CHANWIDTH_80MHZ; + } else if (acs_res->ch_width == 160) { + if (acs_res->vht_seg1_center_ch == 0) { + hapd->iconf->vht_oper_centr_freq_seg0_idx = + acs_res->vht_seg0_center_ch; + hapd->iconf->vht_oper_chwidth = + VHT_CHANWIDTH_160MHZ; + } else { + hapd->iconf->vht_oper_centr_freq_seg0_idx = + acs_res->vht_seg0_center_ch; + hapd->iconf->vht_oper_centr_freq_seg1_idx = + acs_res->vht_seg1_center_ch; + hapd->iconf->vht_oper_chwidth = + VHT_CHANWIDTH_80P80MHZ; + } + } + } + ret = hostapd_acs_completed(hapd->iface, 0); if (ret) { wpa_printf(MSG_ERROR, @@ -647,8 +698,8 @@ static void hostapd_notif_auth(struct hostapd_data *hapd, sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr, NULL); if (sta->wpa_sm == NULL) { - wpa_printf(MSG_DEBUG, "FT: Failed to initialize WPA " - "state machine"); + wpa_printf(MSG_DEBUG, + "FT: Failed to initialize WPA state machine"); status = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } @@ -683,7 +734,7 @@ static void hostapd_action_rx(struct hostapd_data *hapd, if (WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_ACTION) return; /* handled by the driver */ - wpa_printf(MSG_DEBUG, "RX_ACTION cat %d action plen %d", + wpa_printf(MSG_DEBUG, "RX_ACTION cat %d action plen %d", mgmt->u.action.category, (int) plen); sta = ap_get_sta(hapd, mgmt->sa); @@ -694,6 +745,7 @@ static void hostapd_action_rx(struct hostapd_data *hapd, #ifdef CONFIG_IEEE80211R if (mgmt->u.action.category == WLAN_ACTION_FT) { const u8 *payload = drv_mgmt->frame + 24 + 1; + wpa_ft_action_rx(sta->wpa_sm, payload, plen); } #endif /* CONFIG_IEEE80211R */ @@ -710,6 +762,13 @@ static void hostapd_action_rx(struct hostapd_data *hapd, ieee802_11_rx_wnm_action_ap(hapd, mgmt, drv_mgmt->frame_len); } #endif /* CONFIG_WNM */ +#ifdef CONFIG_FST + if (mgmt->u.action.category == WLAN_ACTION_FST && hapd->iface->fst) { + fst_rx_action(hapd->iface->fst, mgmt, drv_mgmt->frame_len); + return; + } +#endif /* CONFIG_FST */ + } @@ -761,6 +820,7 @@ static int hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt) if (hapd->ext_mgmt_frame_handling) { size_t hex_len = 2 * rx_mgmt->frame_len + 1; char *hex = os_malloc(hex_len); + if (hex) { wpa_snprintf_hex(hex, hex_len, rx_mgmt->frame, rx_mgmt->frame_len); @@ -778,8 +838,7 @@ static int hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt) hapd = get_hapd_bssid(iface, bssid); if (hapd == NULL) { - u16 fc; - fc = le_to_host16(hdr->frame_control); + u16 fc = le_to_host16(hdr->frame_control); /* * Drop frames to unknown BSSIDs except for Beacon frames which @@ -798,6 +857,7 @@ static int hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt) if (hapd == HAPD_BROADCAST) { size_t i; + ret = 0; for (i = 0; i < iface->num_bss; i++) { /* if bss is set, driver will call this function for @@ -824,6 +884,7 @@ static void hostapd_mgmt_tx_cb(struct hostapd_data *hapd, const u8 *buf, size_t len, u16 stype, int ok) { struct ieee80211_hdr *hdr; + hdr = (struct ieee80211_hdr *) buf; hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len)); if (hapd == NULL || hapd == HAPD_BROADCAST) @@ -837,6 +898,7 @@ static void hostapd_mgmt_tx_cb(struct hostapd_data *hapd, const u8 *buf, static int hostapd_event_new_sta(struct hostapd_data *hapd, const u8 *addr) { struct sta_info *sta = ap_get_sta(hapd, addr); + if (sta) return 0; @@ -863,11 +925,10 @@ static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src, size_t j; for (j = 0; j < iface->num_bss; j++) { - if ((sta = ap_get_sta(iface->bss[j], src))) { - if (sta->flags & WLAN_STA_ASSOC) { - hapd = iface->bss[j]; - break; - } + sta = ap_get_sta(iface->bss[j], src); + if (sta && sta->flags & WLAN_STA_ASSOC) { + hapd = iface->bss[j]; + break; } } @@ -927,7 +988,8 @@ static void hostapd_single_channel_get_survey(struct hostapd_iface *iface, if (!chan || chan->flag & HOSTAPD_CHAN_DISABLED) return; - wpa_printf(MSG_DEBUG, "Single Channel Survey: (freq=%d channel_time=%ld channel_time_busy=%ld)", + wpa_printf(MSG_DEBUG, + "Single Channel Survey: (freq=%d channel_time=%ld channel_time_busy=%ld)", survey->freq, (unsigned long int) survey->channel_time, (unsigned long int) survey->channel_time_busy); @@ -1061,6 +1123,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, data->rx_mgmt.frame_len >= 24) { const struct ieee80211_hdr *hdr; u16 fc; + hdr = (const struct ieee80211_hdr *) data->rx_mgmt.frame; fc = le_to_host16(hdr->frame_control); if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && @@ -1248,9 +1311,8 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, break; #ifdef CONFIG_ACS case EVENT_ACS_CHANNEL_SELECTED: - hostapd_acs_channel_selected( - hapd, data->acs_selected_channels.pri_channel, - data->acs_selected_channels.sec_channel); + hostapd_acs_channel_selected(hapd, + &data->acs_selected_channels); break; #endif /* CONFIG_ACS */ default: diff --git a/contrib/wpa/src/ap/eap_user_db.c b/contrib/wpa/src/ap/eap_user_db.c index 559d77f..082d0f5 100644 --- a/contrib/wpa/src/ap/eap_user_db.c +++ b/contrib/wpa/src/ap/eap_user_db.c @@ -138,8 +138,12 @@ eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity, char id_str[256], cmd[300]; size_t i; - if (identity_len >= sizeof(id_str)) + if (identity_len >= sizeof(id_str)) { + wpa_printf(MSG_DEBUG, "%s: identity len too big: %d >= %d", + __func__, (int) identity_len, + (int) (sizeof(id_str))); return NULL; + } os_memcpy(id_str, identity, identity_len); id_str[identity_len] = '\0'; for (i = 0; i < identity_len; i++) { @@ -182,7 +186,9 @@ eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity, wpa_printf(MSG_DEBUG, "DB: %s", cmd); if (sqlite3_exec(db, cmd, get_user_cb, &hapd->tmp_eap_user, NULL) != SQLITE_OK) { - wpa_printf(MSG_DEBUG, "DB: Failed to complete SQL operation"); + wpa_printf(MSG_DEBUG, + "DB: Failed to complete SQL operation: %s db: %s", + sqlite3_errmsg(db), hapd->conf->eap_user_sqlite); } else if (hapd->tmp_eap_user.next) user = &hapd->tmp_eap_user; @@ -192,8 +198,10 @@ eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity, wpa_printf(MSG_DEBUG, "DB: %s", cmd); if (sqlite3_exec(db, cmd, get_wildcard_cb, &hapd->tmp_eap_user, NULL) != SQLITE_OK) { - wpa_printf(MSG_DEBUG, "DB: Failed to complete SQL " - "operation"); + wpa_printf(MSG_DEBUG, + "DB: Failed to complete SQL operation: %s db: %s", + sqlite3_errmsg(db), + hapd->conf->eap_user_sqlite); } else if (hapd->tmp_eap_user.next) { user = &hapd->tmp_eap_user; os_free(user->identity); diff --git a/contrib/wpa/src/ap/hostapd.c b/contrib/wpa/src/ap/hostapd.c index 3e4e16b..c09c17a 100644 --- a/contrib/wpa/src/ap/hostapd.c +++ b/contrib/wpa/src/ap/hostapd.c @@ -17,6 +17,7 @@ #include "eap_server/tncs.h" #include "eapol_auth/eapol_auth_sm.h" #include "eapol_auth/eapol_auth_sm_i.h" +#include "fst/fst.h" #include "hostapd.h" #include "authsrv.h" #include "sta_info.h" @@ -179,6 +180,7 @@ int hostapd_reload_config(struct hostapd_iface *iface) hapd = iface->bss[j]; hapd->iconf = newconf; hapd->iconf->channel = oldconf->channel; + hapd->iconf->acs = oldconf->acs; hapd->iconf->secondary_channel = oldconf->secondary_channel; hapd->iconf->ieee80211n = oldconf->ieee80211n; hapd->iconf->ieee80211ac = oldconf->ieee80211ac; @@ -259,6 +261,7 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd) { os_free(hapd->probereq_cb); hapd->probereq_cb = NULL; + hapd->num_probereq_cb = 0; #ifdef CONFIG_P2P wpabuf_free(hapd->p2p_beacon_ie); @@ -353,6 +356,22 @@ static void hostapd_cleanup(struct hostapd_data *hapd) } +static void sta_track_deinit(struct hostapd_iface *iface) +{ + struct hostapd_sta_info *info; + + if (!iface->num_sta_seen) + return; + + while ((info = dl_list_first(&iface->sta_seen, struct hostapd_sta_info, + list))) { + dl_list_del(&info->list); + iface->num_sta_seen--; + os_free(info); + } +} + + static void hostapd_cleanup_iface_partial(struct hostapd_iface *iface) { wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface); @@ -368,6 +387,7 @@ static void hostapd_cleanup_iface_partial(struct hostapd_iface *iface) os_free(iface->basic_rates); iface->basic_rates = NULL; ap_list_deinit(iface); + sta_track_deinit(iface); } @@ -861,7 +881,7 @@ hostapd_das_disconnect(void *ctx, struct radius_das_attrs *attr) static int hostapd_setup_bss(struct hostapd_data *hapd, int first) { struct hostapd_bss_config *conf = hapd->conf; - u8 ssid[HOSTAPD_MAX_SSID_LEN + 1]; + u8 ssid[SSID_MAX_LEN + 1]; int ssid_len, set_ssid; char force_ifname[IFNAMSIZ]; u8 if_addr[ETH_ALEN]; @@ -1363,6 +1383,132 @@ fail: } +#ifdef CONFIG_FST + +static const u8 * fst_hostapd_get_bssid_cb(void *ctx) +{ + struct hostapd_data *hapd = ctx; + + return hapd->own_addr; +} + + +static void fst_hostapd_get_channel_info_cb(void *ctx, + enum hostapd_hw_mode *hw_mode, + u8 *channel) +{ + struct hostapd_data *hapd = ctx; + + *hw_mode = ieee80211_freq_to_chan(hapd->iface->freq, channel); +} + + +static void fst_hostapd_set_ies_cb(void *ctx, const struct wpabuf *fst_ies) +{ + struct hostapd_data *hapd = ctx; + + if (hapd->iface->fst_ies != fst_ies) { + hapd->iface->fst_ies = fst_ies; + if (ieee802_11_set_beacon(hapd)) + wpa_printf(MSG_WARNING, "FST: Cannot set beacon"); + } +} + + +static int fst_hostapd_send_action_cb(void *ctx, const u8 *da, + struct wpabuf *buf) +{ + struct hostapd_data *hapd = ctx; + + return hostapd_drv_send_action(hapd, hapd->iface->freq, 0, da, + wpabuf_head(buf), wpabuf_len(buf)); +} + + +static const struct wpabuf * fst_hostapd_get_mb_ie_cb(void *ctx, const u8 *addr) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta = ap_get_sta(hapd, addr); + + return sta ? sta->mb_ies : NULL; +} + + +static void fst_hostapd_update_mb_ie_cb(void *ctx, const u8 *addr, + const u8 *buf, size_t size) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta = ap_get_sta(hapd, addr); + + if (sta) { + struct mb_ies_info info; + + if (!mb_ies_info_by_ies(&info, buf, size)) { + wpabuf_free(sta->mb_ies); + sta->mb_ies = mb_ies_by_info(&info); + } + } +} + + +static const u8 * fst_hostapd_get_sta(struct fst_get_peer_ctx **get_ctx, + Boolean mb_only) +{ + struct sta_info *s = (struct sta_info *) *get_ctx; + + if (mb_only) { + for (; s && !s->mb_ies; s = s->next) + ; + } + + if (s) { + *get_ctx = (struct fst_get_peer_ctx *) s->next; + + return s->addr; + } + + *get_ctx = NULL; + return NULL; +} + + +static const u8 * fst_hostapd_get_peer_first(void *ctx, + struct fst_get_peer_ctx **get_ctx, + Boolean mb_only) +{ + struct hostapd_data *hapd = ctx; + + *get_ctx = (struct fst_get_peer_ctx *) hapd->sta_list; + + return fst_hostapd_get_sta(get_ctx, mb_only); +} + + +static const u8 * fst_hostapd_get_peer_next(void *ctx, + struct fst_get_peer_ctx **get_ctx, + Boolean mb_only) +{ + return fst_hostapd_get_sta(get_ctx, mb_only); +} + + +void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd, + struct fst_wpa_obj *iface_obj) +{ + iface_obj->ctx = hapd; + iface_obj->get_bssid = fst_hostapd_get_bssid_cb; + iface_obj->get_channel_info = fst_hostapd_get_channel_info_cb; + iface_obj->set_ies = fst_hostapd_set_ies_cb; + iface_obj->send_action = fst_hostapd_send_action_cb; + iface_obj->get_mb_ie = fst_hostapd_get_mb_ie_cb; + iface_obj->update_mb_ie = fst_hostapd_update_mb_ie_cb; + iface_obj->get_peer_first = fst_hostapd_get_peer_first; + iface_obj->get_peer_next = fst_hostapd_get_peer_next; +} + +#endif /* CONFIG_FST */ + + /** * hostapd_setup_interface_complete - Complete interface setup * @@ -1495,6 +1641,7 @@ 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); @@ -1528,6 +1675,22 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err) #ifdef NEED_AP_MLME dfs_offload: #endif /* NEED_AP_MLME */ + +#ifdef CONFIG_FST + if (hapd->iconf->fst_cfg.group_id[0]) { + struct fst_wpa_obj iface_obj; + + fst_hostapd_fill_iface_obj(hapd, &iface_obj); + iface->fst = fst_attach(hapd->conf->iface, hapd->own_addr, + &iface_obj, &hapd->iconf->fst_cfg); + if (!iface->fst) { + wpa_printf(MSG_ERROR, "Could not attach to FST %s", + hapd->iconf->fst_cfg.group_id); + goto fail; + } + } +#endif /* CONFIG_FST */ + hostapd_set_state(iface, HAPD_IFACE_ENABLED); wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_ENABLED); if (hapd->setup_complete_cb) @@ -1544,6 +1707,12 @@ fail: wpa_printf(MSG_ERROR, "Interface initialization failed"); hostapd_set_state(iface, HAPD_IFACE_DISABLED); wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED); +#ifdef CONFIG_FST + if (iface->fst) { + fst_detach(iface->fst); + iface->fst = NULL; + } +#endif /* CONFIG_FST */ if (iface->interfaces && iface->interfaces->terminate_on_error) eloop_terminate(); return -1; @@ -1643,6 +1812,13 @@ void hostapd_interface_deinit(struct hostapd_iface *iface) eloop_cancel_timeout(channel_list_update_timeout, iface, NULL); iface->wait_channel_update = 0; +#ifdef CONFIG_FST + if (iface->fst) { + fst_detach(iface->fst); + iface->fst = NULL; + } +#endif /* CONFIG_FST */ + for (j = iface->num_bss - 1; j >= 0; j--) hostapd_bss_deinit(iface->bss[j]); } @@ -2029,7 +2205,7 @@ hostapd_iface_alloc(struct hapd_interfaces *interfaces) static struct hostapd_config * hostapd_config_alloc(struct hapd_interfaces *interfaces, const char *ifname, - const char *ctrl_iface) + const char *ctrl_iface, const char *driver) { struct hostapd_bss_config *bss; struct hostapd_config *conf; @@ -2042,6 +2218,21 @@ hostapd_config_alloc(struct hapd_interfaces *interfaces, const char *ifname, return NULL; } + if (driver) { + int j; + + for (j = 0; wpa_drivers[j]; j++) { + if (os_strcmp(driver, wpa_drivers[j]->name) == 0) { + conf->driver = wpa_drivers[j]; + goto skip; + } + } + + wpa_printf(MSG_ERROR, + "Invalid/unknown driver '%s' - registering the default driver", + driver); + } + conf->driver = wpa_drivers[0]; if (conf->driver == NULL) { wpa_printf(MSG_ERROR, "No driver wrappers registered!"); @@ -2049,6 +2240,7 @@ hostapd_config_alloc(struct hapd_interfaces *interfaces, const char *ifname, return NULL; } +skip: bss = conf->last_bss = conf->bss[0]; os_strlcpy(bss->iface, ifname, sizeof(bss->iface)); @@ -2209,8 +2401,14 @@ int hostapd_add_iface(struct hapd_interfaces *interfaces, char *buf) if (conf && conf->bss) os_strlcpy(conf->bss[0]->iface, buf, sizeof(conf->bss[0]->iface)); - } else - conf = hostapd_config_alloc(interfaces, buf, ptr); + } else { + char *driver = os_strchr(ptr, ' '); + + if (driver) + *driver++ = '\0'; + conf = hostapd_config_alloc(interfaces, buf, ptr, driver); + } + if (conf == NULL || conf->bss == NULL) { wpa_printf(MSG_ERROR, "%s: Failed to allocate memory " "for configuration", __func__); @@ -2722,4 +2920,43 @@ hostapd_switch_channel_fallback(struct hostapd_iface *iface, hostapd_enable_iface(iface); } + +struct hostapd_data * hostapd_get_iface(struct hapd_interfaces *interfaces, + const char *ifname) +{ + size_t i, j; + + for (i = 0; i < interfaces->count; i++) { + struct hostapd_iface *iface = interfaces->iface[i]; + + for (j = 0; j < iface->num_bss; j++) { + struct hostapd_data *hapd = iface->bss[j]; + + if (os_strcmp(ifname, hapd->conf->iface) == 0) + return hapd; + } + } + + return NULL; +} + #endif /* NEED_AP_MLME */ + + +void hostapd_periodic_iface(struct hostapd_iface *iface) +{ + size_t i; + + ap_list_timer(iface); + + for (i = 0; i < iface->num_bss; i++) { + struct hostapd_data *hapd = iface->bss[i]; + + if (!hapd->started) + continue; + +#ifndef CONFIG_NO_RADIUS + hostapd_acl_expire(hapd); +#endif /* CONFIG_NO_RADIUS */ + } +} diff --git a/contrib/wpa/src/ap/hostapd.h b/contrib/wpa/src/ap/hostapd.h index 75cc24e..dcf51f0 100644 --- a/contrib/wpa/src/ap/hostapd.h +++ b/contrib/wpa/src/ap/hostapd.h @@ -41,6 +41,7 @@ struct hapd_interfaces { size_t count; int global_ctrl_sock; + struct wpa_ctrl_dst *global_ctrl_dst; char *global_iface_path; char *global_iface_name; #ifndef CONFIG_NATIVE_WINDOWS @@ -49,6 +50,9 @@ struct hapd_interfaces { struct hostapd_iface **iface; size_t terminate_on_error; +#ifndef CONFIG_NO_VLAN + struct dynamic_iface *vlan_priv; +#endif /* CONFIG_NO_VLAN */ }; enum hostapd_chan_status { @@ -265,6 +269,7 @@ struct hostapd_data { /** Key used for generating SAE anti-clogging tokens */ u8 sae_token_key[8]; struct os_reltime last_sae_token_key_update; + int dot11RSNASAERetransPeriod; /* msec */ #endif /* CONFIG_SAE */ #ifdef CONFIG_TESTING_OPTIONS @@ -276,6 +281,12 @@ struct hostapd_data { }; +struct hostapd_sta_info { + struct dl_list list; + u8 addr[ETH_ALEN]; + struct os_reltime last_seen; +}; + /** * struct hostapd_iface - hostapd per-interface data structure */ @@ -305,6 +316,10 @@ struct hostapd_iface { unsigned int wait_channel_update:1; unsigned int cac_started:1; +#ifdef CONFIG_FST + struct fst_iface *fst; + const struct wpabuf *fst_ies; +#endif /* CONFIG_FST */ /* * When set, indicates that the driver will handle the AP @@ -400,6 +415,9 @@ struct hostapd_iface { void (*scan_cb)(struct hostapd_iface *iface); int num_ht40_scan_tries; + + struct dl_list sta_seen; /* struct hostapd_sta_info */ + unsigned int num_sta_seen; }; /* hostapd.c */ @@ -437,6 +455,7 @@ void hostapd_switch_channel_fallback(struct hostapd_iface *iface, const struct hostapd_freq_params *freq_params); void hostapd_cleanup_cs_params(struct hostapd_data *hapd); +void hostapd_periodic_iface(struct hostapd_iface *iface); /* utils.c */ int hostapd_register_probereq_cb(struct hostapd_data *hapd, @@ -464,4 +483,12 @@ const struct hostapd_eap_user * hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity, size_t identity_len, int phase2); +struct hostapd_data * hostapd_get_iface(struct hapd_interfaces *interfaces, + const char *ifname); + +#ifdef CONFIG_FST +void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd, + struct fst_wpa_obj *iface_obj); +#endif /* CONFIG_FST */ + #endif /* HOSTAPD_H */ diff --git a/contrib/wpa/src/ap/hw_features.c b/contrib/wpa/src/ap/hw_features.c index 05431d3..fc8786d 100644 --- a/contrib/wpa/src/ap/hw_features.c +++ b/contrib/wpa/src/ap/hw_features.c @@ -260,8 +260,14 @@ static int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface, res = check_40mhz_5g(iface->current_mode, scan_res, pri_chan, sec_chan); - if (res == 2) - ieee80211n_switch_pri_sec(iface); + if (res == 2) { + if (iface->conf->no_pri_sec_switch) { + wpa_printf(MSG_DEBUG, + "Cannot switch PRI/SEC channels due to local constraint"); + } else { + ieee80211n_switch_pri_sec(iface); + } + } return !!res; } @@ -347,8 +353,13 @@ static void ieee80211n_scan_channels_2g4(struct hostapd_iface *iface, sec_freq = pri_freq + 20; else sec_freq = pri_freq - 20; - affected_start = (pri_freq + sec_freq) / 2 - 25; - affected_end = (pri_freq + sec_freq) / 2 + 25; + /* + * Note: Need to find the PRI channel also in cases where the affected + * channel is the SEC channel of a 40 MHz BSS, so need to include the + * scanning coverage here to be 40 MHz from the center frequency. + */ + affected_start = (pri_freq + sec_freq) / 2 - 40; + affected_end = (pri_freq + sec_freq) / 2 + 40; wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz", affected_start, affected_end); @@ -510,7 +521,11 @@ static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface) return 0; } - if ((conf & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) && + /* + * Driver ACS chosen channel may not be HT40 due to internal driver + * restrictions. + */ + if (!iface->conf->acs && (conf & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) && !(hw & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) { wpa_printf(MSG_ERROR, "Driver does not support configured " "HT capability [HT40*]"); @@ -717,6 +732,15 @@ int hostapd_check_ht_capab(struct hostapd_iface *iface) int ret; if (!iface->conf->ieee80211n) return 0; + + if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211B && + iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G && + (iface->conf->ht_capab & HT_CAP_INFO_DSSS_CCK40MHZ)) { + wpa_printf(MSG_DEBUG, + "Disable HT capability [DSSS_CCK-40] on 5 GHz band"); + iface->conf->ht_capab &= ~HT_CAP_INFO_DSSS_CCK40MHZ; + } + if (!ieee80211n_supported_ht_capab(iface)) return -1; #ifdef CONFIG_IEEE80211AC @@ -740,6 +764,9 @@ static int hostapd_is_usable_chan(struct hostapd_iface *iface, int i; struct hostapd_channel_data *chan; + if (!iface->current_mode) + return 0; + for (i = 0; i < iface->current_mode->num_channels; i++) { chan = &iface->current_mode->channels[i]; if (chan->chan != channel) @@ -801,6 +828,12 @@ hostapd_check_chans(struct hostapd_iface *iface) static void hostapd_notify_bad_chans(struct hostapd_iface *iface) { + if (!iface->current_mode) { + hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, + "Hardware does not support configured mode"); + return; + } hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_WARNING, @@ -891,14 +924,18 @@ int hostapd_select_hw_mode(struct hostapd_iface *iface) } if (iface->current_mode == NULL) { - wpa_printf(MSG_ERROR, "Hardware does not support configured " - "mode"); - hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_WARNING, - "Hardware does not support configured mode " - "(%d) (hw_mode in hostapd.conf)", - (int) iface->conf->hw_mode); - return -2; + if (!(iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) || + !(iface->drv_flags & WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY)) + { + wpa_printf(MSG_ERROR, + "Hardware does not support configured mode"); + hostapd_logger(iface->bss[0], NULL, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, + "Hardware does not support configured mode (%d) (hw_mode in hostapd.conf)", + (int) iface->conf->hw_mode); + return -2; + } } switch (hostapd_check_chans(iface)) { diff --git a/contrib/wpa/src/ap/hw_features.h b/contrib/wpa/src/ap/hw_features.h index 0f67ab8..ca7f22b 100644 --- a/contrib/wpa/src/ap/hw_features.h +++ b/contrib/wpa/src/ap/hw_features.h @@ -36,6 +36,11 @@ static inline int hostapd_get_hw_features(struct hostapd_iface *iface) return -1; } +static inline int hostapd_acs_completed(struct hostapd_iface *iface, int err) +{ + return -1; +} + static inline int hostapd_select_hw_mode(struct hostapd_iface *iface) { return -100; diff --git a/contrib/wpa/src/ap/ieee802_11.c b/contrib/wpa/src/ap/ieee802_11.c index 89911b1..7bb18c0 100644 --- a/contrib/wpa/src/ap/ieee802_11.c +++ b/contrib/wpa/src/ap/ieee802_11.c @@ -23,6 +23,7 @@ #include "radius/radius_client.h" #include "p2p/p2p.h" #include "wps/wps.h" +#include "fst/fst.h" #include "hostapd.h" #include "beacon.h" #include "ieee802_11_auth.h" @@ -38,6 +39,7 @@ #include "p2p_hostapd.h" #include "ap_drv_ops.h" #include "wnm_ap.h" +#include "hw_features.h" #include "ieee802_11.h" #include "dfs.h" @@ -132,8 +134,7 @@ u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid) } -u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, - int probe) +u16 hostapd_own_capab_info(struct hostapd_data *hapd) { int capab = WLAN_CAPABILITY_ESS; int privacy; @@ -166,20 +167,6 @@ u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, privacy = 1; #endif /* CONFIG_HS20 */ - if (sta) { - int policy, def_klen; - if (probe && sta->ssid_probe) { - policy = sta->ssid_probe->security_policy; - def_klen = sta->ssid_probe->wep.default_len; - } else { - policy = sta->ssid->security_policy; - def_klen = sta->ssid->wep.default_len; - } - privacy = policy != SECURITY_PLAINTEXT; - if (policy == SECURITY_IEEE_802_1X && def_klen == 0) - privacy = 0; - } - if (privacy) capab |= WLAN_CAPABILITY_PRIVACY; @@ -206,6 +193,7 @@ u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, } +#ifndef CONFIG_NO_RC4 static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta, u16 auth_transaction, const u8 *challenge, int iswep) @@ -259,6 +247,7 @@ static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta, return 0; } +#endif /* CONFIG_NO_RC4 */ static void send_auth_reply(struct hostapd_data *hapd, @@ -328,7 +317,6 @@ static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid, #ifdef CONFIG_SAE -#define dot11RSNASAERetransPeriod 40 /* msec */ #define dot11RSNASAESync 5 /* attempts */ @@ -511,12 +499,14 @@ static void auth_sae_retransmit_timer(void *eloop_ctx, void *eloop_data) switch (sta->sae->state) { case SAE_COMMITTED: ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0); - eloop_register_timeout(0, dot11RSNASAERetransPeriod * 1000, + eloop_register_timeout(0, + hapd->dot11RSNASAERetransPeriod * 1000, auth_sae_retransmit_timer, hapd, sta); break; case SAE_CONFIRMED: ret = auth_sae_send_confirm(hapd, sta, hapd->own_addr); - eloop_register_timeout(0, dot11RSNASAERetransPeriod * 1000, + eloop_register_timeout(0, + hapd->dot11RSNASAERetransPeriod * 1000, auth_sae_retransmit_timer, hapd, sta); break; default: @@ -542,7 +532,7 @@ static void sae_set_retransmit_timer(struct hostapd_data *hapd, return; eloop_cancel_timeout(auth_sae_retransmit_timer, hapd, sta); - eloop_register_timeout(0, dot11RSNASAERetransPeriod * 1000, + eloop_register_timeout(0, hapd->dot11RSNASAERetransPeriod * 1000, auth_sae_retransmit_timer, hapd, sta); } @@ -624,7 +614,7 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, return WLAN_STATUS_SUCCESS; sta->sae->sync++; - ret = auth_sae_send_commit(hapd, sta, bssid, 1); + ret = auth_sae_send_commit(hapd, sta, bssid, 0); if (ret) return ret; @@ -784,6 +774,12 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, ((const u8 *) mgmt) + len - mgmt->u.auth.variable, &token, &token_len, hapd->conf->sae_groups); + if (resp == SAE_SILENTLY_DISCARD) { + wpa_printf(MSG_DEBUG, + "SAE: Drop commit message from " MACSTR " due to reflection attack", + MAC2STR(sta->addr)); + return; + } if (token && check_sae_token(hapd, sta->addr, token, token_len) < 0) { wpa_printf(MSG_DEBUG, "SAE: Drop commit message with " @@ -934,6 +930,16 @@ static void handle_auth(struct hostapd_data *hapd, challenge ? " challenge" : "", seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : ""); +#ifdef CONFIG_NO_RC4 + if (auth_alg == WLAN_AUTH_SHARED_KEY) { + wpa_printf(MSG_INFO, + "Unsupported authentication algorithm (%d)", + auth_alg); + resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG; + goto fail; + } +#endif /* CONFIG_NO_RC4 */ + if (hapd->tkip_countermeasures) { resp = WLAN_REASON_MICHAEL_MIC_FAILURE; goto fail; @@ -972,6 +978,61 @@ static void handle_auth(struct hostapd_data *hapd, goto fail; } + if (hapd->conf->no_auth_if_seen_on) { + struct hostapd_data *other; + + other = sta_track_seen_on(hapd->iface, mgmt->sa, + hapd->conf->no_auth_if_seen_on); + if (other) { + u8 *pos; + u32 info; + u8 op_class, channel, phytype; + + wpa_printf(MSG_DEBUG, "%s: Reject authentication from " + MACSTR " since STA has been seen on %s", + hapd->conf->iface, MAC2STR(mgmt->sa), + hapd->conf->no_auth_if_seen_on); + + resp = WLAN_STATUS_REJECTED_WITH_SUGGESTED_BSS_TRANSITION; + pos = &resp_ies[0]; + *pos++ = WLAN_EID_NEIGHBOR_REPORT; + *pos++ = 13; + os_memcpy(pos, other->own_addr, ETH_ALEN); + pos += ETH_ALEN; + info = 0; /* TODO: BSSID Information */ + WPA_PUT_LE32(pos, info); + pos += 4; + if (other->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD) + phytype = 8; /* dmg */ + else if (other->iconf->ieee80211ac) + phytype = 9; /* vht */ + else if (other->iconf->ieee80211n) + phytype = 7; /* ht */ + else if (other->iconf->hw_mode == + HOSTAPD_MODE_IEEE80211A) + phytype = 4; /* ofdm */ + else if (other->iconf->hw_mode == + HOSTAPD_MODE_IEEE80211G) + phytype = 6; /* erp */ + else + phytype = 5; /* hrdsss */ + if (ieee80211_freq_to_channel_ext( + hostapd_hw_get_freq(other, + other->iconf->channel), + other->iconf->secondary_channel, + other->iconf->ieee80211ac, + &op_class, &channel) == NUM_HOSTAPD_MODES) { + op_class = 0; + channel = other->iconf->channel; + } + *pos++ = op_class; + *pos++ = channel; + *pos++ = phytype; + resp_ies_len = pos - &resp_ies[0]; + goto fail; + } + } + res = hostapd_allowed_address(hapd, mgmt->sa, (u8 *) mgmt, len, &session_timeout, &acct_interim_interval, &vlan_id, @@ -1081,6 +1142,7 @@ static void handle_auth(struct hostapd_data *hapd, sta->auth_alg = WLAN_AUTH_OPEN; mlme_authenticate_indication(hapd, sta); break; +#ifndef CONFIG_NO_RC4 case WLAN_AUTH_SHARED_KEY: resp = auth_shared_key(hapd, sta, auth_transaction, challenge, fc & WLAN_FC_ISWEP); @@ -1094,6 +1156,7 @@ static void handle_auth(struct hostapd_data *hapd, resp_ies_len = 2 + WLAN_AUTH_CHALLENGE_LEN; } break; +#endif /* CONFIG_NO_RC4 */ #ifdef CONFIG_IEEE80211R case WLAN_AUTH_FT: sta->auth_alg = WLAN_AUTH_FT; @@ -1297,8 +1360,7 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, if (resp != WLAN_STATUS_SUCCESS) return resp; #ifdef CONFIG_IEEE80211N - resp = copy_sta_ht_capab(hapd, sta, elems.ht_capabilities, - elems.ht_capabilities_len); + resp = copy_sta_ht_capab(hapd, sta, elems.ht_capabilities); if (resp != WLAN_STATUS_SUCCESS) return resp; if (hapd->iconf->ieee80211n && hapd->iconf->require_ht && @@ -1311,14 +1373,15 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, #endif /* CONFIG_IEEE80211N */ #ifdef CONFIG_IEEE80211AC - resp = copy_sta_vht_capab(hapd, sta, elems.vht_capabilities, - elems.vht_capabilities_len); - if (resp != WLAN_STATUS_SUCCESS) - return resp; + if (hapd->iconf->ieee80211ac) { + resp = copy_sta_vht_capab(hapd, sta, elems.vht_capabilities); + if (resp != WLAN_STATUS_SUCCESS) + return resp; - resp = set_sta_vht_opmode(hapd, sta, elems.vht_opmode_notif); - if (resp != WLAN_STATUS_SUCCESS) - return resp; + resp = set_sta_vht_opmode(hapd, sta, elems.vht_opmode_notif); + if (resp != WLAN_STATUS_SUCCESS) + return resp; + } if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht && !(sta->flags & WLAN_STA_VHT)) { @@ -1546,6 +1609,14 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, sta->hs20_ie = NULL; #endif /* CONFIG_HS20 */ +#ifdef CONFIG_FST + wpabuf_free(sta->mb_ies); + if (hapd->iface->fst) + sta->mb_ies = mb_ies_by_info(&elems.mb_ies); + else + sta->mb_ies = NULL; +#endif /* CONFIG_FST */ + return WLAN_STATUS_SUCCESS; } @@ -1594,7 +1665,7 @@ static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, send_len = IEEE80211_HDRLEN; send_len += sizeof(reply->u.assoc_resp); reply->u.assoc_resp.capab_info = - host_to_le16(hostapd_own_capab_info(hapd, sta, 0)); + host_to_le16(hostapd_own_capab_info(hapd)); reply->u.assoc_resp.status_code = host_to_le16(status_code); reply->u.assoc_resp.aid = host_to_le16(sta->aid | BIT(14) | BIT(15)); /* Supported rates */ @@ -1634,6 +1705,14 @@ static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, if (sta->qos_map_enabled) p = hostapd_eid_qos_map_set(hapd, p); +#ifdef CONFIG_FST + if (hapd->iface->fst_ies) { + os_memcpy(p, wpabuf_head(hapd->iface->fst_ies), + wpabuf_len(hapd->iface->fst_ies)); + p += wpabuf_len(hapd->iface->fst_ies); + } +#endif /* CONFIG_FST */ + #ifdef CONFIG_IEEE80211AC if (hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT)) p = hostapd_eid_vendor_vht(hapd, p); @@ -2112,10 +2191,20 @@ static int handle_action(struct hostapd_data *hapd, ieee802_11_rx_wnm_action_ap(hapd, mgmt, len); return 1; #endif /* CONFIG_WNM */ +#ifdef CONFIG_FST + case WLAN_ACTION_FST: + if (hapd->iface->fst) + fst_rx_action(hapd->iface->fst, mgmt, len); + else + wpa_printf(MSG_DEBUG, + "FST: Ignore FST Action frame - no FST attached"); + return 1; +#endif /* CONFIG_FST */ case WLAN_ACTION_PUBLIC: case WLAN_ACTION_PROTECTED_DUAL: #ifdef CONFIG_IEEE80211N - if (mgmt->u.action.u.public_action.action == + if (len >= IEEE80211_HDRLEN + 2 && + mgmt->u.action.u.public_action.action == WLAN_PA_20_40_BSS_COEX) { wpa_printf(MSG_DEBUG, "HT20/40 coex mgmt frame received from STA " @@ -2248,6 +2337,9 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, return 0; } + if (hapd->iconf->track_sta_max_num) + sta_track_add(hapd->iface, mgmt->sa); + switch (stype) { case WLAN_FC_STYPE_AUTH: wpa_printf(MSG_DEBUG, "mgmt::auth"); @@ -2335,7 +2427,7 @@ static void hostapd_set_wds_encryption(struct hostapd_data *hapd, char *ifname_wds) { int i; - struct hostapd_ssid *ssid = sta->ssid; + struct hostapd_ssid *ssid = &hapd->conf->ssid; if (hapd->conf->ieee802_1x || hapd->conf->wpa) return; @@ -2473,11 +2565,11 @@ static void handle_assoc_cb(struct hostapd_data *hapd, * so bind it to the selected VLAN interface now, since the * interface selection is not going to change anymore. */ - if (ap_sta_bind_vlan(hapd, sta, 0) < 0) + if (ap_sta_bind_vlan(hapd, sta) < 0) return; } else if (sta->vlan_id) { /* VLAN ID already set (e.g., by PMKSA caching), so bind STA */ - if (ap_sta_bind_vlan(hapd, sta, 0) < 0) + if (ap_sta_bind_vlan(hapd, sta) < 0) return; } diff --git a/contrib/wpa/src/ap/ieee802_11.h b/contrib/wpa/src/ap/ieee802_11.h index 41c27d9..44c1bff 100644 --- a/contrib/wpa/src/ap/ieee802_11.h +++ b/contrib/wpa/src/ap/ieee802_11.h @@ -14,6 +14,7 @@ struct hostapd_data; struct sta_info; struct hostapd_frame_info; struct ieee80211_ht_capabilities; +struct ieee80211_vht_capabilities; struct ieee80211_mgmt; int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, @@ -40,8 +41,7 @@ static inline int ieee802_11_get_mib_sta(struct hostapd_data *hapd, return 0; } #endif /* NEED_AP_MLME */ -u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, - int probe); +u16 hostapd_own_capab_info(struct hostapd_data *hapd); void ap_ht2040_timeout(void *eloop_data, void *user_data); u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid); @@ -62,7 +62,7 @@ void hostapd_get_vht_capab(struct hostapd_data *hapd, struct ieee80211_vht_capabilities *vht_cap, struct ieee80211_vht_capabilities *neg_vht_cap); u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta, - const u8 *ht_capab, size_t ht_capab_len); + const u8 *ht_capab); u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ie, size_t len); @@ -70,7 +70,7 @@ void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta); void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta); void ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta); u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta, - const u8 *vht_capab, size_t vht_capab_len); + const u8 *vht_capab); u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta, const u8 *vht_opmode); void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr, diff --git a/contrib/wpa/src/ap/ieee802_11_auth.c b/contrib/wpa/src/ap/ieee802_11_auth.c index 56c3ce0..531a67d 100644 --- a/contrib/wpa/src/ap/ieee802_11_auth.c +++ b/contrib/wpa/src/ap/ieee802_11_auth.c @@ -399,19 +399,15 @@ static void hostapd_acl_expire_queries(struct hostapd_data *hapd, /** * hostapd_acl_expire - ACL cache expiration callback - * @eloop_ctx: struct hostapd_data * - * @timeout_ctx: Not used + * @hapd: struct hostapd_data * */ -static void hostapd_acl_expire(void *eloop_ctx, void *timeout_ctx) +void hostapd_acl_expire(struct hostapd_data *hapd) { - struct hostapd_data *hapd = eloop_ctx; struct os_reltime now; os_get_reltime(&now); hostapd_acl_expire_cache(hapd, &now); hostapd_acl_expire_queries(hapd, &now); - - eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL); } @@ -561,6 +557,19 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, if (hapd->conf->wpa_psk_radius == PSK_RADIUS_REQUIRED && !cache->psk) cache->accepted = HOSTAPD_ACL_REJECT; + + if (cache->vlan_id && + !hostapd_vlan_id_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; + } + if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_REQUIRED && + !cache->vlan_id) + cache->accepted = HOSTAPD_ACL_REJECT; } else cache->accepted = HOSTAPD_ACL_REJECT; cache->next = hapd->acl_cache; @@ -602,8 +611,6 @@ int hostapd_acl_init(struct hostapd_data *hapd) if (radius_client_register(hapd->radius, RADIUS_AUTH, hostapd_acl_recv_radius, hapd)) return -1; - - eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL); #endif /* CONFIG_NO_RADIUS */ return 0; @@ -619,8 +626,6 @@ void hostapd_acl_deinit(struct hostapd_data *hapd) struct hostapd_acl_query_data *query, *prev; #ifndef CONFIG_NO_RADIUS - eloop_cancel_timeout(hostapd_acl_expire, hapd, NULL); - hostapd_acl_cache_free(hapd->acl_cache); #endif /* CONFIG_NO_RADIUS */ diff --git a/contrib/wpa/src/ap/ieee802_11_auth.h b/contrib/wpa/src/ap/ieee802_11_auth.h index 2bc1065..b66f244 100644 --- a/contrib/wpa/src/ap/ieee802_11_auth.h +++ b/contrib/wpa/src/ap/ieee802_11_auth.h @@ -24,5 +24,6 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, int hostapd_acl_init(struct hostapd_data *hapd); void hostapd_acl_deinit(struct hostapd_data *hapd); void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk); +void hostapd_acl_expire(struct hostapd_data *hapd); #endif /* IEEE802_11_AUTH_H */ diff --git a/contrib/wpa/src/ap/ieee802_11_ht.c b/contrib/wpa/src/ap/ieee802_11_ht.c index 4b0653d..11fde2a 100644 --- a/contrib/wpa/src/ap/ieee802_11_ht.c +++ b/contrib/wpa/src/ap/ieee802_11_ht.c @@ -209,7 +209,7 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd, struct hostapd_iface *iface = hapd->iface; struct ieee80211_2040_bss_coex_ie *bc_ie; struct ieee80211_2040_intol_chan_report *ic_report; - int is_ht_allowed = 1; + int is_ht40_allowed = 1; int i; const u8 *start = (const u8 *) mgmt; const u8 *data = start + IEEE80211_HDRLEN + 2; @@ -242,7 +242,7 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "20 MHz BSS width request bit is set in BSS coexistence information field"); - is_ht_allowed = 0; + is_ht40_allowed = 0; } if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_40MHZ_INTOL) { @@ -250,7 +250,7 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "40 MHz intolerant bit is set in BSS coexistence information field"); - is_ht_allowed = 0; + is_ht40_allowed = 0; } if (start + len - data >= 3 && @@ -276,13 +276,13 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd, HOSTAPD_LEVEL_DEBUG, "20_40_INTOLERANT channel %d reported", chan); - is_ht_allowed = 0; + is_ht40_allowed = 0; } } - wpa_printf(MSG_DEBUG, "is_ht_allowed=%d num_sta_ht40_intolerant=%d", - is_ht_allowed, iface->num_sta_ht40_intolerant); + wpa_printf(MSG_DEBUG, "is_ht40_allowed=%d num_sta_ht40_intolerant=%d", + is_ht40_allowed, iface->num_sta_ht40_intolerant); - if (!is_ht_allowed && + if (!is_ht40_allowed && (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) { if (iface->conf->secondary_channel) { hostapd_logger(hapd, mgmt->sa, @@ -310,12 +310,15 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd, u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta, - const u8 *ht_capab, size_t ht_capab_len) + const u8 *ht_capab) { - /* Disable HT caps for STAs associated to no-HT BSSes. */ + /* + * Disable HT caps for STAs associated to no-HT BSSes, or for stations + * that did not specify a valid WMM IE in the (Re)Association Request + * frame. + */ if (!ht_capab || - ht_capab_len < sizeof(struct ieee80211_ht_capabilities) || - hapd->conf->disable_11n) { + !(sta->flags & WLAN_STA_WMM) || hapd->conf->disable_11n) { sta->flags &= ~WLAN_STA_HT; os_free(sta->ht_capabilities); sta->ht_capabilities = NULL; diff --git a/contrib/wpa/src/ap/ieee802_11_vht.c b/contrib/wpa/src/ap/ieee802_11_vht.c index 171538a..5bf1b5d 100644 --- a/contrib/wpa/src/ap/ieee802_11_vht.c +++ b/contrib/wpa/src/ap/ieee802_11_vht.c @@ -132,11 +132,10 @@ static int check_valid_vht_mcs(struct hostapd_hw_modes *mode, u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta, - const u8 *vht_capab, size_t vht_capab_len) + const u8 *vht_capab) { /* Disable VHT caps for STAs associated to no-VHT BSSes. */ if (!vht_capab || - vht_capab_len < sizeof(struct ieee80211_vht_capabilities) || hapd->conf->disable_11ac || !check_valid_vht_mcs(hapd->iface->current_mode, vht_capab)) { sta->flags &= ~WLAN_STA_VHT; diff --git a/contrib/wpa/src/ap/ieee802_1x.c b/contrib/wpa/src/ap/ieee802_1x.c index 79dc0f9..0f2d428 100644 --- a/contrib/wpa/src/ap/ieee802_1x.c +++ b/contrib/wpa/src/ap/ieee802_1x.c @@ -125,6 +125,9 @@ void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd, } +#ifndef CONFIG_FIPS +#ifndef CONFIG_NO_RC4 + static void ieee802_1x_tx_key_one(struct hostapd_data *hapd, struct sta_info *sta, int idx, int broadcast, @@ -204,7 +207,7 @@ static void ieee802_1x_tx_key_one(struct hostapd_data *hapd, } -void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) +static void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) { struct eapol_authenticator *eapol = hapd->eapol_auth; struct eapol_state_machine *sm = sta->eapol_sm; @@ -259,6 +262,9 @@ void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) } } +#endif /* CONFIG_NO_RC4 */ +#endif /* CONFIG_FIPS */ + const char *radius_mode_txt(struct hostapd_data *hapd) { @@ -346,7 +352,8 @@ static int add_common_radius_sta_attr_rsn(struct hostapd_data *hapd, return -1; } - suite = wpa_cipher_to_suite((hapd->conf->wpa & 0x2) ? + suite = wpa_cipher_to_suite(((hapd->conf->wpa & 0x2) || + hapd->conf->osen) ? WPA_PROTO_RSN : WPA_PROTO_WPA, hapd->conf->wpa_group); if (!hostapd_config_get_radius_attr(req_attr, @@ -453,7 +460,7 @@ static int add_common_radius_sta_attr(struct hostapd_data *hapd, } #endif /* CONFIG_IEEE80211R */ - if (hapd->conf->wpa && sta->wpa_sm && + if ((hapd->conf->wpa || hapd->conf->osen) && sta->wpa_sm && add_common_radius_sta_attr_rsn(hapd, req_attr, sta, msg) < 0) return -1; @@ -599,7 +606,7 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, goto fail; } - if (eap && !radius_msg_add_eap(msg, eap, len)) { + if (!radius_msg_add_eap(msg, eap, len)) { wpa_printf(MSG_INFO, "Could not add EAP-Message"); goto fail; } @@ -1108,8 +1115,6 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm); if (pmksa) { - int old_vlanid; - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "PMK from PMKSA cache - skip IEEE 802.1X/EAP"); @@ -1123,11 +1128,8 @@ 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); - old_vlanid = sta->vlan_id; pmksa_cache_to_eapol_data(pmksa, sta->eapol_sm); - if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED) - sta->vlan_id = 0; - ap_sta_bind_vlan(hapd, sta, old_vlanid); + ap_sta_bind_vlan(hapd, sta); } else { if (reassoc) { /* @@ -1290,7 +1292,7 @@ static void ieee802_1x_store_radius_class(struct hostapd_data *hapd, struct sta_info *sta, struct radius_msg *msg) { - u8 *class; + u8 *attr_class; size_t class_len; struct eapol_state_machine *sm = sta->eapol_sm; int count, i; @@ -1312,12 +1314,12 @@ static void ieee802_1x_store_radius_class(struct hostapd_data *hapd, nclass_count = 0; - class = NULL; + attr_class = NULL; for (i = 0; i < count; i++) { do { if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CLASS, - &class, &class_len, - class) < 0) { + &attr_class, &class_len, + attr_class) < 0) { i = count; break; } @@ -1327,7 +1329,7 @@ static void ieee802_1x_store_radius_class(struct hostapd_data *hapd, if (nclass[nclass_count].data == NULL) break; - os_memcpy(nclass[nclass_count].data, class, class_len); + os_memcpy(nclass[nclass_count].data, attr_class, class_len); nclass[nclass_count].len = class_len; nclass_count++; } @@ -1590,7 +1592,7 @@ 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, old_vlanid = 0; + int session_timeout_set, vlan_id = 0; struct eapol_state_machine *sm; int override_eapReq = 0; struct radius_hdr *hdr = radius_msg_get_hdr(msg); @@ -1657,20 +1659,27 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, switch (hdr->code) { case RADIUS_CODE_ACCESS_ACCEPT: - if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED) - sta->vlan_id = 0; + if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED) + vlan_id = 0; #ifndef CONFIG_NO_VLAN - else { - old_vlanid = sta->vlan_id; - sta->vlan_id = radius_msg_get_vlanid(msg); - } - if (sta->vlan_id > 0 && - hostapd_vlan_id_valid(hapd->conf->vlan, sta->vlan_id)) { + 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", sta->vlan_id); - } else if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_REQUIRED) { + "VLAN ID %d", vlan_id); + } else if (vlan_id > 0) { + sta->eapol_sm->authFail = TRUE; + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, + "Invalid VLAN ID %d received from RADIUS server", + vlan_id); + break; + } else if (hapd->conf->ssid.dynamic_vlan == + DYNAMIC_VLAN_REQUIRED) { sta->eapol_sm->authFail = TRUE; hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, @@ -1681,7 +1690,9 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, } #endif /* CONFIG_NO_VLAN */ - if (ap_sta_bind_vlan(hapd, sta, old_vlanid) < 0) + sta->vlan_id = vlan_id; + if ((sta->flags & WLAN_STA_ASSOC) && + ap_sta_bind_vlan(hapd, sta) < 0) break; sta->session_timeout_set = !!session_timeout_set; @@ -1926,10 +1937,11 @@ static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity, struct hostapd_data *hapd = ctx; const struct hostapd_eap_user *eap_user; int i; + int rv = -1; eap_user = hostapd_get_eap_user(hapd, identity, identity_len, phase2); if (eap_user == NULL) - return -1; + goto out; os_memset(user, 0, sizeof(*user)); user->phase2 = phase2; @@ -1941,7 +1953,7 @@ static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity, if (eap_user->password) { user->password = os_malloc(eap_user->password_len); if (user->password == NULL) - return -1; + goto out; os_memcpy(user->password, eap_user->password, eap_user->password_len); user->password_len = eap_user->password_len; @@ -1951,8 +1963,13 @@ static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity, user->macacl = eap_user->macacl; user->ttls_auth = eap_user->ttls_auth; user->remediation = eap_user->remediation; + rv = 0; - return 0; +out: + if (rv) + wpa_printf(MSG_DEBUG, "%s: Failed to find user", __func__); + + return rv; } @@ -2012,9 +2029,13 @@ static void _ieee802_1x_abort_auth(void *ctx, void *sta_ctx) static void _ieee802_1x_tx_key(void *ctx, void *sta_ctx) { +#ifndef CONFIG_FIPS +#ifndef CONFIG_NO_RC4 struct hostapd_data *hapd = ctx; struct sta_info *sta = sta_ctx; ieee802_1x_tx_key(hapd, sta); +#endif /* CONFIG_NO_RC4 */ +#endif /* CONFIG_FIPS */ } @@ -2085,6 +2106,7 @@ int ieee802_1x_init(struct hostapd_data *hapd) conf.erp_send_reauth_start = hapd->conf->erp_send_reauth_start; conf.erp_domain = hapd->conf->erp_domain; conf.erp = hapd->conf->eap_server_erp; + conf.tls_session_lifetime = hapd->conf->tls_session_lifetime; conf.pac_opaque_encr_key = hapd->conf->pac_opaque_encr_key; conf.eap_fast_a_id = hapd->conf->eap_fast_a_id; conf.eap_fast_a_id_len = hapd->conf->eap_fast_a_id_len; @@ -2332,9 +2354,9 @@ void ieee802_1x_notify_pre_auth(struct eapol_state_machine *sm, int pre_auth) } -static const char * bool_txt(Boolean bool) +static const char * bool_txt(Boolean val) { - return bool ? "TRUE" : "FALSE"; + return val ? "TRUE" : "FALSE"; } diff --git a/contrib/wpa/src/ap/ieee802_1x.h b/contrib/wpa/src/ap/ieee802_1x.h index de6e0e7..14d6955 100644 --- a/contrib/wpa/src/ap/ieee802_1x.h +++ b/contrib/wpa/src/ap/ieee802_1x.h @@ -23,7 +23,6 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, 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_tx_key(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, struct sta_info *sta, int authorized); diff --git a/contrib/wpa/src/ap/ndisc_snoop.c b/contrib/wpa/src/ap/ndisc_snoop.c index b0d42dc..4a87721 100644 --- a/contrib/wpa/src/ap/ndisc_snoop.c +++ b/contrib/wpa/src/ap/ndisc_snoop.c @@ -81,12 +81,24 @@ static int sta_has_ip6addr(struct sta_info *sta, struct in6_addr *addr) } +static void ucast_to_stas(struct hostapd_data *hapd, const u8 *buf, size_t len) +{ + struct sta_info *sta; + + for (sta = hapd->sta_list; sta; sta = sta->next) { + if (!(sta->flags & WLAN_STA_AUTHORIZED)) + continue; + x_snoop_mcast_to_ucast_convert_send(hapd, sta, (u8 *) buf, len); + } +} + + static void handle_ndisc(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) { struct hostapd_data *hapd = ctx; struct icmpv6_ndmsg *msg; - struct in6_addr *saddr; + struct in6_addr saddr; struct sta_info *sta; int res; char addrtxt[INET6_ADDRSTRLEN + 1]; @@ -101,25 +113,30 @@ static void handle_ndisc(void *ctx, const u8 *src_addr, const u8 *buf, if (msg->opt_type != SOURCE_LL_ADDR) return; - saddr = &msg->ipv6h.ip6_src; - if (!(saddr->s6_addr32[0] == 0 && saddr->s6_addr32[1] == 0 && - saddr->s6_addr32[2] == 0 && saddr->s6_addr32[3] == 0)) { + /* + * IPv6 header may not be 32-bit aligned in the buffer, so use + * a local copy to avoid unaligned reads. + */ + os_memcpy(&saddr, &msg->ipv6h.ip6_src, sizeof(saddr)); + if (!(saddr.s6_addr32[0] == 0 && saddr.s6_addr32[1] == 0 && + saddr.s6_addr32[2] == 0 && saddr.s6_addr32[3] == 0)) { if (len < ETH_HLEN + sizeof(*msg) + ETH_ALEN) return; sta = ap_get_sta(hapd, msg->opt_lladdr); if (!sta) return; - if (sta_has_ip6addr(sta, saddr)) + if (sta_has_ip6addr(sta, &saddr)) return; - if (inet_ntop(AF_INET6, saddr, addrtxt, sizeof(addrtxt)) - == NULL) + if (inet_ntop(AF_INET6, &saddr, addrtxt, + sizeof(addrtxt)) == NULL) addrtxt[0] = '\0'; wpa_printf(MSG_DEBUG, "ndisc_snoop: Learned new IPv6 address %s for " MACSTR, addrtxt, MAC2STR(sta->addr)); - hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) saddr); - res = hostapd_drv_br_add_ip_neigh(hapd, 6, (u8 *) saddr, + hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) &saddr); + res = hostapd_drv_br_add_ip_neigh(hapd, 6, + (u8 *) &saddr, 128, sta->addr); if (res) { wpa_printf(MSG_ERROR, @@ -128,21 +145,17 @@ static void handle_ndisc(void *ctx, const u8 *src_addr, const u8 *buf, return; } - if (sta_ip6addr_add(sta, saddr)) + if (sta_ip6addr_add(sta, &saddr)) return; } break; case ROUTER_ADVERTISEMENT: - if (!hapd->conf->disable_dgaf) - return; - /* fall through */ + if (hapd->conf->disable_dgaf) + ucast_to_stas(hapd, buf, len); + break; case NEIGHBOR_ADVERTISEMENT: - for (sta = hapd->sta_list; sta; sta = sta->next) { - if (!(sta->flags & WLAN_STA_AUTHORIZED)) - continue; - x_snoop_mcast_to_ucast_convert_send(hapd, sta, - (u8 *) buf, len); - } + if (hapd->conf->na_mcast_to_ucast) + ucast_to_stas(hapd, buf, len); break; default: break; diff --git a/contrib/wpa/src/ap/sta_info.c b/contrib/wpa/src/ap/sta_info.c index 7e75e1a..d64307c 100644 --- a/contrib/wpa/src/ap/sta_info.c +++ b/contrib/wpa/src/ap/sta_info.c @@ -16,6 +16,7 @@ #include "radius/radius.h" #include "radius/radius_client.h" #include "p2p/p2p.h" +#include "fst/fst.h" #include "hostapd.h" #include "accounting.h" #include "ieee802_1x.h" @@ -171,6 +172,19 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) !(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); + } +#endif /* CONFIG_NO_VLAN */ + ap_sta_hash_del(hapd, sta); ap_sta_list_del(hapd, sta); @@ -283,6 +297,9 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) wpabuf_free(sta->wps_ie); wpabuf_free(sta->p2p_ie); wpabuf_free(sta->hs20_ie); +#ifdef CONFIG_FST + wpabuf_free(sta->mb_ies); +#endif /* CONFIG_FST */ os_free(sta->ht_capabilities); os_free(sta->vht_capabilities); @@ -619,7 +636,6 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr) hapd->sta_list = sta; hapd->num_sta++; ap_sta_hash_add(hapd, sta); - sta->ssid = &hapd->conf->ssid; ap_sta_remove_in_other_bss(hapd, sta); sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ; dl_list_init(&sta->ip6addr); @@ -768,26 +784,19 @@ int ap_sta_wps_cancel(struct hostapd_data *hapd, #endif /* CONFIG_WPS */ -int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, - int old_vlanid) +int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta) { #ifndef CONFIG_NO_VLAN const char *iface; struct hostapd_vlan *vlan = NULL; int ret; - - /* - * Do not proceed furthur if the vlan id remains same. We do not want - * duplicate dynamic vlan entries. - */ - if (sta->vlan_id == old_vlanid) - return 0; + int old_vlanid = sta->vlan_id_bound; iface = hapd->conf->iface; - if (sta->ssid->vlan[0]) - iface = sta->ssid->vlan; + if (hapd->conf->ssid.vlan[0]) + iface = hapd->conf->ssid.vlan; - if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED) + 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; @@ -805,6 +814,14 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, iface = vlan->ifname; } + /* + * Do not increment ref counters if the VLAN ID remains same, but do + * not skip hostapd_drv_set_sta_vlan() as hostapd_drv_sta_remove() might + * have been called before. + */ + if (sta->vlan_id == old_vlanid) + goto skip_counting; + if (sta->vlan_id > 0 && vlan == NULL) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "could not find VLAN for " @@ -825,7 +842,7 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, } iface = vlan->ifname; - if (vlan_setup_encryption_dyn(hapd, sta->ssid, iface) != 0) { + if (vlan_setup_encryption_dyn(hapd, iface) != 0) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "could not " @@ -838,7 +855,7 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, HOSTAPD_LEVEL_DEBUG, "added new dynamic VLAN " "interface '%s'", iface); } else if (vlan && vlan->vlan_id == sta->vlan_id) { - if (sta->vlan_id > 0) { + if (vlan->dynamic_vlan > 0) { vlan->dynamic_vlan++; hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, @@ -852,7 +869,7 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, * 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, sta->ssid, iface) != 0) { + if (vlan_setup_encryption_dyn(hapd, iface) != 0) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "could not " @@ -862,6 +879,10 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, } } + /* ref counters have been increased, so mark the station */ + sta->vlan_id_bound = sta->vlan_id; + +skip_counting: hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "binding station to interface " "'%s'", iface); @@ -876,10 +897,10 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, "entry to vlan_id=%d", sta->vlan_id); } -done: /* During 1x reauth, if the vlan id changes, then remove the old id. */ - if (old_vlanid > 0) + if (old_vlanid > 0 && old_vlanid != sta->vlan_id) vlan_remove_dynamic(hapd, old_vlanid); +done: return ret; #else /* CONFIG_NO_VLAN */ @@ -1043,6 +1064,16 @@ void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta, wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO, AP_STA_DISCONNECTED "%s", buf); } + +#ifdef CONFIG_FST + if (hapd->iface->fst) { + if (authorized) + fst_notify_peer_connected(hapd->iface->fst, sta->addr); + else + fst_notify_peer_disconnected(hapd->iface->fst, + sta->addr); + } +#endif /* CONFIG_FST */ } diff --git a/contrib/wpa/src/ap/sta_info.h b/contrib/wpa/src/ap/sta_info.h index 57551ab..420d64e 100644 --- a/contrib/wpa/src/ap/sta_info.h +++ b/contrib/wpa/src/ap/sta_info.h @@ -117,10 +117,8 @@ struct sta_info { struct wpa_state_machine *wpa_sm; struct rsn_preauth_interface *preauth_iface; - struct hostapd_ssid *ssid; /* SSID selection based on (Re)AssocReq */ - struct hostapd_ssid *ssid_probe; /* SSID selection based on ProbeReq */ - - int vlan_id; + int vlan_id; /* 0: none, >0: VID */ + int vlan_id_bound; /* updated by ap_sta_bind_vlan() */ /* PSKs from RADIUS authentication server */ struct hostapd_sta_wpa_psk_short *psk; @@ -155,6 +153,9 @@ struct sta_info { struct wpabuf *hs20_deauth_req; char *hs20_session_info_url; int hs20_disassoc_timer; +#ifdef CONFIG_FST + struct wpabuf *mb_ies; /* MB IEs from (Re)Association Request */ +#endif /* CONFIG_FST */ struct os_reltime connected_time; @@ -218,8 +219,7 @@ void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta, 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 old_vlanid); +int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta); 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); diff --git a/contrib/wpa/src/ap/utils.c b/contrib/wpa/src/ap/utils.c index 931968c..fcb371b 100644 --- a/contrib/wpa/src/ap/utils.c +++ b/contrib/wpa/src/ap/utils.c @@ -10,6 +10,7 @@ #include "common.h" #include "common/ieee802_11_defs.h" +#include "fst/fst.h" #include "sta_info.h" #include "hostapd.h" @@ -55,10 +56,20 @@ static int prune_associations(struct hostapd_iface *iface, void *ctx) ohapd = iface->bss[j]; if (ohapd == data->hapd) continue; +#ifdef CONFIG_FST + /* Don't prune STAs belong to same FST */ + if (ohapd->iface->fst && + data->hapd->iface->fst && + fst_are_ifaces_aggregated(ohapd->iface->fst, + data->hapd->iface->fst)) + continue; +#endif /* CONFIG_FST */ osta = ap_get_sta(ohapd, data->addr); if (!osta) continue; + wpa_printf(MSG_INFO, "%s: Prune association for " MACSTR, + ohapd->conf->iface, MAC2STR(osta->addr)); ap_sta_disassociate(ohapd, osta, WLAN_REASON_UNSPECIFIED); } diff --git a/contrib/wpa/src/ap/vlan_init.c b/contrib/wpa/src/ap/vlan_init.c index dc65019..fd1c8dd 100644 --- a/contrib/wpa/src/ap/vlan_init.c +++ b/contrib/wpa/src/ap/vlan_init.c @@ -9,6 +9,13 @@ */ #include "utils/includes.h" +#ifdef CONFIG_FULL_DYNAMIC_VLAN +#include <net/if.h> +#include <sys/ioctl.h> +#include <linux/sockios.h> +#include <linux/if_vlan.h> +#include <linux/if_bridge.h> +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ #include "utils/common.h" #include "hostapd.h" @@ -20,12 +27,6 @@ #ifdef CONFIG_FULL_DYNAMIC_VLAN -#include <net/if.h> -#include <sys/ioctl.h> -#include <linux/sockios.h> -#include <linux/if_vlan.h> -#include <linux/if_bridge.h> - #include "drivers/priv_netlink.h" #include "utils/eloop.h" @@ -34,6 +35,90 @@ 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_helper(const char *if_name, int up) { @@ -481,11 +566,13 @@ static void vlan_newlink(char *ifname, struct hostapd_data *hapd) 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) { + 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", @@ -500,8 +587,8 @@ static void vlan_newlink(char *ifname, struct hostapd_data *hapd) "brvlan%d", vlan->vlan_id); } - if (!br_addbr(br_name)) - vlan->clean |= DVLAN_CLEAN_BR; + dyn_iface_get(hapd, br_name, + br_addbr(br_name) ? 0 : DVLAN_CLEAN_BR); ifconfig_up(br_name); @@ -517,13 +604,16 @@ static void vlan_newlink(char *ifname, struct hostapd_data *hapd) sizeof(vlan_ifname), "vlan%d", vlan->vlan_id); + clean = 0; ifconfig_up(tagged_interface); if (!vlan_add(tagged_interface, vlan->vlan_id, vlan_ifname)) - vlan->clean |= DVLAN_CLEAN_VLAN; + clean |= DVLAN_CLEAN_VLAN; if (!br_addif(br_name, vlan_ifname)) - vlan->clean |= DVLAN_CLEAN_VLAN_PORT; + clean |= DVLAN_CLEAN_VLAN_PORT; + + dyn_iface_get(hapd, vlan_ifname, clean); ifconfig_up(vlan_ifname); } @@ -547,13 +637,15 @@ static void vlan_dellink(char *ifname, struct hostapd_data *hapd) 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) { + 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, @@ -581,20 +673,27 @@ static void vlan_dellink(char *ifname, struct hostapd_data *hapd) os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d", vlan->vlan_id); - if (vlan->clean & DVLAN_CLEAN_VLAN_PORT) + + clean = dyn_iface_put(hapd, vlan_ifname); + + if (clean & DVLAN_CLEAN_VLAN_PORT) br_delif(br_name, vlan_ifname); - ifconfig_down(vlan_ifname); - if (vlan->clean & DVLAN_CLEAN_VLAN) + if (clean & DVLAN_CLEAN_VLAN) { + ifconfig_down(vlan_ifname); vlan_rem(vlan_ifname); + } } - if ((vlan->clean & DVLAN_CLEAN_BR) && + 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 { @@ -651,6 +750,11 @@ vlan_read_ifnames(struct nlmsghdr *h, size_t len, int del, 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)", @@ -778,8 +882,7 @@ static void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv) #endif /* CONFIG_FULL_DYNAMIC_VLAN */ -int vlan_setup_encryption_dyn(struct hostapd_data *hapd, - struct hostapd_ssid *mssid, const char *dyn_vlan) +int vlan_setup_encryption_dyn(struct hostapd_data *hapd, const char *dyn_vlan) { int i; @@ -789,10 +892,11 @@ int vlan_setup_encryption_dyn(struct hostapd_data *hapd, /* 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 (mssid->wep.key[i] && + if (hapd->conf->ssid.wep.key[i] && hostapd_drv_set_key(dyn_vlan, hapd, WPA_ALG_WEP, NULL, i, - i == mssid->wep.idx, NULL, 0, - mssid->wep.key[i], mssid->wep.len[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"); @@ -953,7 +1057,8 @@ int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id) if (vlan_id <= 0 || vlan_id > MAX_VLAN_ID) return 1; - wpa_printf(MSG_DEBUG, "VLAN: %s(vlan_id=%d)", __func__, vlan_id); + wpa_printf(MSG_DEBUG, "VLAN: %s(ifname=%s vlan_id=%d)", + __func__, hapd->conf->iface, vlan_id); vlan = hapd->conf->vlan; while (vlan) { @@ -967,8 +1072,12 @@ int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id) if (vlan == NULL) return 1; - if (vlan->dynamic_vlan == 0) + if (vlan->dynamic_vlan == 0) { hostapd_vlan_if_remove(hapd, vlan->ifname); +#ifdef CONFIG_FULL_DYNAMIC_VLAN + vlan_dellink(vlan->ifname, hapd); +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ + } return 0; } diff --git a/contrib/wpa/src/ap/vlan_init.h b/contrib/wpa/src/ap/vlan_init.h index 781eaac..fc39443 100644 --- a/contrib/wpa/src/ap/vlan_init.h +++ b/contrib/wpa/src/ap/vlan_init.h @@ -18,7 +18,6 @@ struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, int vlan_id); int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id); int vlan_setup_encryption_dyn(struct hostapd_data *hapd, - struct hostapd_ssid *mssid, const char *dyn_vlan); #else /* CONFIG_NO_VLAN */ static inline int vlan_init(struct hostapd_data *hapd) @@ -43,7 +42,6 @@ static inline int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id) } static inline int vlan_setup_encryption_dyn(struct hostapd_data *hapd, - struct hostapd_ssid *mssid, const char *dyn_vlan) { return -1; diff --git a/contrib/wpa/src/ap/vlan_util.c b/contrib/wpa/src/ap/vlan_util.c index cc54051..d4e0efb 100644 --- a/contrib/wpa/src/ap/vlan_util.c +++ b/contrib/wpa/src/ap/vlan_util.c @@ -31,7 +31,7 @@ */ int vlan_add(const char *if_name, int vid, const char *vlan_if_name) { - int ret = -1; + int err, ret = -1; struct nl_sock *handle = NULL; struct nl_cache *cache = NULL; struct rtnl_link *rlink = NULL; @@ -58,14 +58,18 @@ int vlan_add(const char *if_name, int vid, const char *vlan_if_name) goto vlan_add_error; } - if (nl_connect(handle, NETLINK_ROUTE) < 0) { - wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink"); + err = nl_connect(handle, NETLINK_ROUTE); + if (err < 0) { + wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink: %s", + nl_geterror(err)); goto vlan_add_error; } - if (rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache) < 0) { + err = rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache); + if (err < 0) { cache = NULL; - wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache"); + wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache: %s", + nl_geterror(err)); goto vlan_add_error; } @@ -92,23 +96,29 @@ int vlan_add(const char *if_name, int vid, const char *vlan_if_name) goto vlan_add_error; } - if (rtnl_link_set_type(rlink, "vlan") < 0) { - wpa_printf(MSG_ERROR, "VLAN: failed to set link type"); + err = rtnl_link_set_type(rlink, "vlan"); + if (err < 0) { + wpa_printf(MSG_ERROR, "VLAN: failed to set link type: %s", + nl_geterror(err)); goto vlan_add_error; } rtnl_link_set_link(rlink, if_idx); rtnl_link_set_name(rlink, vlan_if_name); - if (rtnl_link_vlan_set_id(rlink, vid) < 0) { - wpa_printf(MSG_ERROR, "VLAN: failed to set link vlan id"); + err = rtnl_link_vlan_set_id(rlink, vid); + if (err < 0) { + wpa_printf(MSG_ERROR, "VLAN: failed to set link vlan id: %s", + nl_geterror(err)); goto vlan_add_error; } - if (rtnl_link_add(handle, rlink, NLM_F_CREATE) < 0) { + err = rtnl_link_add(handle, rlink, NLM_F_CREATE); + if (err < 0) { wpa_printf(MSG_ERROR, "VLAN: failed to create link %s for " - "vlan %d on %s (%d)", - vlan_if_name, vid, if_name, if_idx); + "vlan %d on %s (%d): %s", + vlan_if_name, vid, if_name, if_idx, + nl_geterror(err)); goto vlan_add_error; } @@ -127,7 +137,7 @@ vlan_add_error: int vlan_rem(const char *if_name) { - int ret = -1; + int err, ret = -1; struct nl_sock *handle = NULL; struct nl_cache *cache = NULL; struct rtnl_link *rlink = NULL; @@ -140,14 +150,18 @@ int vlan_rem(const char *if_name) goto vlan_rem_error; } - if (nl_connect(handle, NETLINK_ROUTE) < 0) { - wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink"); + err = nl_connect(handle, NETLINK_ROUTE); + if (err < 0) { + wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink: %s", + nl_geterror(err)); goto vlan_rem_error; } - if (rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache) < 0) { + err = rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache); + if (err < 0) { cache = NULL; - wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache"); + wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache: %s", + nl_geterror(err)); goto vlan_rem_error; } @@ -158,9 +172,10 @@ int vlan_rem(const char *if_name) goto vlan_rem_error; } - if (rtnl_link_delete(handle, rlink) < 0) { - wpa_printf(MSG_ERROR, "VLAN: failed to remove link %s", - if_name); + err = rtnl_link_delete(handle, rlink); + if (err < 0) { + wpa_printf(MSG_ERROR, "VLAN: failed to remove link %s: %s", + if_name, nl_geterror(err)); goto vlan_rem_error; } diff --git a/contrib/wpa/src/ap/wmm.c b/contrib/wpa/src/ap/wmm.c index 6d4177c..314e244 100644 --- a/contrib/wpa/src/ap/wmm.c +++ b/contrib/wpa/src/ap/wmm.c @@ -274,6 +274,9 @@ void hostapd_wmm_action(struct hostapd_data *hapd, return; } + if (left < 0) + return; /* not a valid WMM Action frame */ + /* extract the tspec info element */ if (ieee802_11_parse_elems(pos, left, &elems, 1) == ParseFailed) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, diff --git a/contrib/wpa/src/ap/wpa_auth.c b/contrib/wpa/src/ap/wpa_auth.c index 9c5f609..2760a3f 100644 --- a/contrib/wpa/src/ap/wpa_auth.c +++ b/contrib/wpa/src/ap/wpa_auth.c @@ -45,6 +45,12 @@ 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); +static void wpa_group_free(struct wpa_authenticator *wpa_auth, + struct wpa_group *group); +static void wpa_group_get(struct wpa_authenticator *wpa_auth, + struct wpa_group *group); +static void wpa_group_put(struct wpa_authenticator *wpa_auth, + struct wpa_group *group); static const u32 dot11RSNAConfigGroupUpdateCount = 4; static const u32 dot11RSNAConfigPairwiseUpdateCount = 4; @@ -67,6 +73,14 @@ static inline int wpa_auth_mic_failure_report( } +static inline void wpa_auth_psk_failure_report( + struct wpa_authenticator *wpa_auth, const u8 *addr) +{ + if (wpa_auth->cb.psk_failure_report) + wpa_auth->cb.psk_failure_report(wpa_auth->cb.ctx, addr); +} + + static inline void wpa_auth_set_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr, wpa_eapol_variable var, int value) @@ -254,15 +268,22 @@ static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx) static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx) { struct wpa_authenticator *wpa_auth = eloop_ctx; - struct wpa_group *group; + struct wpa_group *group, *next; wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "rekeying GTK"); - for (group = wpa_auth->group; group; group = group->next) { + group = wpa_auth->group; + while (group) { + wpa_group_get(wpa_auth, group); + group->GTKReKey = TRUE; do { group->changed = FALSE; wpa_group_sm_step(wpa_auth, group); } while (group->changed); + + next = group->next; + wpa_group_put(wpa_auth, group); + group = next; } if (wpa_auth->conf.wpa_group_rekey) { @@ -565,6 +586,7 @@ wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr, sm->wpa_auth = wpa_auth; sm->group = wpa_auth->group; + wpa_group_get(sm->wpa_auth, sm->group); return sm; } @@ -643,6 +665,7 @@ static void wpa_free_sta_sm(struct wpa_state_machine *sm) #endif /* CONFIG_IEEE80211R */ os_free(sm->last_rx_eapol_key); os_free(sm->wpa_ie); + wpa_group_put(sm->wpa_auth, sm->group); os_free(sm); } @@ -1517,6 +1540,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, else WPA_PUT_BE16(key->key_data_length, key_data_len); +#ifndef CONFIG_NO_RC4 } else if (sm->PTK.kek_len == 16) { u8 ek[32]; os_memcpy(key->key_iv, @@ -1532,6 +1556,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, else WPA_PUT_BE16(key->key_data_length, key_data_len); +#endif /* CONFIG_NO_RC4 */ } else { os_free(hdr); os_free(buf); @@ -1646,7 +1671,7 @@ void wpa_remove_ptk(struct wpa_state_machine *sm) } -int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event) +int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event) { int remove_ptk = 1; @@ -1734,6 +1759,14 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event) wpa_remove_ptk(sm); } + if (sm->in_step_loop) { + /* + * wpa_sm_step() is already running - avoid recursive call to + * it by making the existing loop process the new update. + */ + sm->changed = TRUE; + return 0; + } return wpa_sm_step(sm); } @@ -1818,9 +1851,13 @@ static void wpa_group_ensure_init(struct wpa_authenticator *wpa_auth, group->reject_4way_hs_for_entropy = FALSE; } - wpa_group_init_gmk_and_counter(wpa_auth, group); - wpa_gtk_update(wpa_auth, group); - wpa_group_config_group_keys(wpa_auth, group); + if (wpa_group_init_gmk_and_counter(wpa_auth, group) < 0 || + wpa_gtk_update(wpa_auth, group) < 0 || + wpa_group_config_group_keys(wpa_auth, group) < 0) { + wpa_printf(MSG_INFO, "WPA: GMK/GTK setup failed"); + group->first_sta_seen = FALSE; + group->reject_4way_hs_for_entropy = TRUE; + } } @@ -1985,7 +2022,7 @@ static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) { struct wpa_ptk PTK; - int ok = 0; + int ok = 0, psk_found = 0; const u8 *pmk = NULL; SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk); @@ -2001,6 +2038,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) sm->p2p_dev_addr, pmk); if (pmk == NULL) break; + psk_found = 1; } else pmk = sm->PMK; @@ -2020,6 +2058,8 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) if (!ok) { wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "invalid MIC in msg 2/4 of 4-Way Handshake"); + if (psk_found) + wpa_auth_psk_failure_report(sm->wpa_auth, sm->addr); return; } @@ -2983,9 +3023,9 @@ void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth) } -static const char * wpa_bool_txt(int bool) +static const char * wpa_bool_txt(int val) { - return bool ? "TRUE" : "FALSE"; + return val ? "TRUE" : "FALSE"; } @@ -3270,6 +3310,63 @@ void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth, } +/* + * 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 + * gets modified. + */ +static void wpa_group_free(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + struct wpa_group *prev = wpa_auth->group; + + wpa_printf(MSG_DEBUG, "WPA: Remove group state machine for VLAN-ID %d", + group->vlan_id); + + while (prev) { + if (prev->next == group) { + /* This never frees the special first group as needed */ + prev->next = group->next; + os_free(group); + break; + } + prev = prev->next; + } + +} + + +/* Increase the reference counter for group */ +static void wpa_group_get(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + /* Skip the special first group */ + if (wpa_auth->group == group) + return; + + group->references++; +} + + +/* Decrease the reference counter and maybe free the group */ +static void wpa_group_put(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + /* Skip the special first group */ + if (wpa_auth->group == group) + return; + + group->references--; + if (group->references) + return; + wpa_group_free(wpa_auth, group); +} + + +/* + * Add a group that has its references counter set to zero. Caller needs to + * call wpa_group_get() on the return value to mark the entry in use. + */ static struct wpa_group * wpa_auth_add_group(struct wpa_authenticator *wpa_auth, int vlan_id) { @@ -3320,7 +3417,10 @@ int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id) wpa_printf(MSG_DEBUG, "WPA: Moving STA " MACSTR " to use group state " "machine for VLAN ID %d", MAC2STR(sm->addr), vlan_id); + wpa_group_get(sm->wpa_auth, group); + wpa_group_put(sm->wpa_auth, sm->group); sm->group = group; + return 0; } diff --git a/contrib/wpa/src/ap/wpa_auth.h b/contrib/wpa/src/ap/wpa_auth.h index 2788e65..fd04f16 100644 --- a/contrib/wpa/src/ap/wpa_auth.h +++ b/contrib/wpa/src/ap/wpa_auth.h @@ -1,6 +1,6 @@ /* * hostapd - IEEE 802.11i-2004 / WPA Authenticator - * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -12,6 +12,9 @@ #include "common/defs.h" #include "common/eapol_common.h" #include "common/wpa_common.h" +#include "common/ieee802_11_defs.h" + +#define MAX_OWN_IE_OVERRIDE 256 #ifdef _MSC_VER #pragma pack(push, 1) @@ -146,8 +149,7 @@ struct wpa_auth_config { int group_mgmt_cipher; #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_IEEE80211R -#define SSID_LEN 32 - u8 ssid[SSID_LEN]; + u8 ssid[SSID_MAX_LEN]; size_t ssid_len; u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; u8 r0_key_holder[FT_R0KH_ID_MAX_LEN]; @@ -164,6 +166,8 @@ struct wpa_auth_config { int ap_mlme; #ifdef CONFIG_TESTING_OPTIONS double corrupt_gtk_rekey_mic_probability; + u8 own_ie_override[MAX_OWN_IE_OVERRIDE]; + size_t own_ie_override_len; #endif /* CONFIG_TESTING_OPTIONS */ #ifdef CONFIG_P2P u8 ip_addr_go[4]; @@ -189,6 +193,7 @@ struct wpa_auth_callbacks { const char *txt); void (*disconnect)(void *ctx, const u8 *addr, u16 reason); int (*mic_failure_report)(void *ctx, const u8 *addr); + void (*psk_failure_report)(void *ctx, const u8 *addr); void (*set_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var, int value); int (*get_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var); @@ -251,12 +256,12 @@ void wpa_auth_sta_deinit(struct wpa_state_machine *sm); void wpa_receive(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, u8 *data, size_t data_len); -typedef enum { +enum wpa_event { WPA_AUTH, WPA_ASSOC, WPA_DISASSOC, WPA_DEAUTH, WPA_REAUTH, WPA_REAUTH_EAPOL, WPA_ASSOC_FT -} wpa_event; +}; void wpa_remove_ptk(struct wpa_state_machine *sm); -int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event); +int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event); void wpa_auth_sm_notify(struct wpa_state_machine *sm); void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth); int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen); diff --git a/contrib/wpa/src/ap/wpa_auth_ft.c b/contrib/wpa/src/ap/wpa_auth_ft.c index ef3249a..eeaffbf 100644 --- a/contrib/wpa/src/ap/wpa_auth_ft.c +++ b/contrib/wpa/src/ap/wpa_auth_ft.c @@ -534,10 +534,8 @@ static u8 * wpa_ft_process_rdie(struct wpa_state_machine *sm, return pos; } -#ifdef NEED_AP_MLME - if (parse.wmm_tspec && sm->wpa_auth->conf.ap_mlme) { + if (parse.wmm_tspec) { struct wmm_tspec_element *tspec; - int res; if (parse.wmm_tspec_len + 2 < (int) sizeof(*tspec)) { wpa_printf(MSG_DEBUG, "FT: Too short WMM TSPEC IE " @@ -555,7 +553,13 @@ static u8 * wpa_ft_process_rdie(struct wpa_state_machine *sm, } tspec = (struct wmm_tspec_element *) pos; os_memcpy(tspec, parse.wmm_tspec - 2, sizeof(*tspec)); - res = wmm_process_tspec(tspec); + } + +#ifdef NEED_AP_MLME + if (parse.wmm_tspec && sm->wpa_auth->conf.ap_mlme) { + int res; + + res = wmm_process_tspec((struct wmm_tspec_element *) pos); wpa_printf(MSG_DEBUG, "FT: ADDTS processing result: %d", res); if (res == WMM_ADDTS_STATUS_INVALID_PARAMETERS) rdie->status_code = @@ -566,20 +570,17 @@ static u8 * wpa_ft_process_rdie(struct wpa_state_machine *sm, else { /* TSPEC accepted; include updated TSPEC in response */ rdie->descr_count = 1; - pos += sizeof(*tspec); + pos += sizeof(struct wmm_tspec_element); } return pos; } #endif /* NEED_AP_MLME */ if (parse.wmm_tspec && !sm->wpa_auth->conf.ap_mlme) { - struct wmm_tspec_element *tspec; int res; - tspec = (struct wmm_tspec_element *) pos; - os_memcpy(tspec, parse.wmm_tspec - 2, sizeof(*tspec)); res = wpa_ft_add_tspec(sm->wpa_auth, sm->addr, pos, - sizeof(*tspec)); + sizeof(struct wmm_tspec_element)); if (res >= 0) { if (res) rdie->status_code = host_to_le16(res); @@ -587,7 +588,7 @@ static u8 * wpa_ft_process_rdie(struct wpa_state_machine *sm, /* TSPEC accepted; include updated TSPEC in * response */ rdie->descr_count = 1; - pos += sizeof(*tspec); + pos += sizeof(struct wmm_tspec_element); } return pos; } diff --git a/contrib/wpa/src/ap/wpa_auth_glue.c b/contrib/wpa/src/ap/wpa_auth_glue.c index 7f83207..f98cc50 100644 --- a/contrib/wpa/src/ap/wpa_auth_glue.c +++ b/contrib/wpa/src/ap/wpa_auth_glue.c @@ -11,6 +11,7 @@ #include "utils/common.h" #include "common/ieee802_11_defs.h" #include "common/sae.h" +#include "common/wpa_ctrl.h" #include "eapol_auth/eapol_auth_sm.h" #include "eapol_auth/eapol_auth_sm_i.h" #include "eap_server/eap.h" @@ -53,8 +54,8 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_IEEE80211R wconf->ssid_len = conf->ssid.ssid_len; - if (wconf->ssid_len > SSID_LEN) - wconf->ssid_len = SSID_LEN; + if (wconf->ssid_len > SSID_MAX_LEN) + wconf->ssid_len = SSID_MAX_LEN; os_memcpy(wconf->ssid, conf->ssid.ssid, wconf->ssid_len); os_memcpy(wconf->mobility_domain, conf->mobility_domain, MOBILITY_DOMAIN_ID_LEN); @@ -91,6 +92,13 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, #ifdef CONFIG_TESTING_OPTIONS wconf->corrupt_gtk_rekey_mic_probability = iconf->corrupt_gtk_rekey_mic_probability; + if (conf->own_ie_override && + wpabuf_len(conf->own_ie_override) <= MAX_OWN_IE_OVERRIDE) { + wconf->own_ie_override_len = wpabuf_len(conf->own_ie_override); + os_memcpy(wconf->own_ie_override, + wpabuf_head(conf->own_ie_override), + wconf->own_ie_override_len); + } #endif /* CONFIG_TESTING_OPTIONS */ #ifdef CONFIG_P2P os_memcpy(wconf->ip_addr_go, conf->ip_addr_go, 4); @@ -144,6 +152,14 @@ static int hostapd_wpa_auth_mic_failure_report(void *ctx, const u8 *addr) } +static void hostapd_wpa_auth_psk_failure_report(void *ctx, const u8 *addr) +{ + struct hostapd_data *hapd = ctx; + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_POSSIBLE_PSK_MISMATCH MACSTR, + MAC2STR(addr)); +} + + static void hostapd_wpa_auth_set_eapol(void *ctx, const u8 *addr, wpa_eapol_variable var, int value) { @@ -579,6 +595,7 @@ int hostapd_setup_wpa(struct hostapd_data *hapd) cb.logger = hostapd_wpa_auth_logger; cb.disconnect = hostapd_wpa_auth_disconnect; cb.mic_failure_report = hostapd_wpa_auth_mic_failure_report; + cb.psk_failure_report = hostapd_wpa_auth_psk_failure_report; cb.set_eapol = hostapd_wpa_auth_set_eapol; cb.get_eapol = hostapd_wpa_auth_get_eapol; cb.get_psk = hostapd_wpa_auth_get_psk; @@ -620,7 +637,8 @@ int hostapd_setup_wpa(struct hostapd_data *hapd) } #ifdef CONFIG_IEEE80211R - if (!hostapd_drv_none(hapd)) { + if (!hostapd_drv_none(hapd) && hapd->conf->ft_over_ds && + wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt)) { hapd->l2 = l2_packet_init(hapd->conf->bridge[0] ? hapd->conf->bridge : hapd->conf->iface, NULL, ETH_P_RRB, diff --git a/contrib/wpa/src/ap/wpa_auth_i.h b/contrib/wpa/src/ap/wpa_auth_i.h index 7b2cd3e..57b098f 100644 --- a/contrib/wpa/src/ap/wpa_auth_i.h +++ b/contrib/wpa/src/ap/wpa_auth_i.h @@ -169,6 +169,8 @@ struct wpa_group { u8 IGTK[2][WPA_IGTK_MAX_LEN]; int GN_igtk, GM_igtk; #endif /* CONFIG_IEEE80211W */ + /* Number of references except those in struct wpa_group->next */ + unsigned int references; }; diff --git a/contrib/wpa/src/ap/wpa_auth_ie.c b/contrib/wpa/src/ap/wpa_auth_ie.c index f287297..eafb828 100644 --- a/contrib/wpa/src/ap/wpa_auth_ie.c +++ b/contrib/wpa/src/ap/wpa_auth_ie.c @@ -261,7 +261,8 @@ 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) { + if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION && + conf->group_mgmt_cipher != WPA_CIPHER_AES_128_CMAC) { if (pos + 2 + 4 > buf + len) return -1; if (pmkid == NULL) { @@ -377,6 +378,23 @@ int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth) u8 *pos, buf[128]; int res; +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_auth->conf.own_ie_override_len) { + wpa_hexdump(MSG_DEBUG, "WPA: Forced own IE(s) for testing", + wpa_auth->conf.own_ie_override, + wpa_auth->conf.own_ie_override_len); + os_free(wpa_auth->wpa_ie); + wpa_auth->wpa_ie = + os_malloc(wpa_auth->conf.own_ie_override_len); + if (wpa_auth->wpa_ie == NULL) + return -1; + os_memcpy(wpa_auth->wpa_ie, wpa_auth->conf.own_ie_override, + wpa_auth->conf.own_ie_override_len); + wpa_auth->wpa_ie_len = wpa_auth->conf.own_ie_override_len; + return 0; + } +#endif /* CONFIG_TESTING_OPTIONS */ + pos = buf; if (wpa_auth->conf.wpa == WPA_PROTO_OSEN) { diff --git a/contrib/wpa/src/ap/wps_hostapd.c b/contrib/wpa/src/ap/wps_hostapd.c index b0e8b0b..cde31e6 100644 --- a/contrib/wpa/src/ap/wps_hostapd.c +++ b/contrib/wpa/src/ap/wps_hostapd.c @@ -324,7 +324,7 @@ static int hapd_wps_reconfig_in_memory(struct hostapd_data *hapd, wpa_printf(MSG_DEBUG, "WPS: Updating in-memory configuration"); bss->wps_state = 2; - if (cred->ssid_len <= HOSTAPD_MAX_SSID_LEN) { + if (cred->ssid_len <= SSID_MAX_LEN) { os_memcpy(bss->ssid.ssid, cred->ssid, cred->ssid_len); bss->ssid.ssid_len = cred->ssid_len; bss->ssid.ssid_set = 1; @@ -347,8 +347,12 @@ static int hapd_wps_reconfig_in_memory(struct hostapd_data *hapd, bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK; bss->wpa_pairwise = 0; - if (cred->encr_type & WPS_ENCR_AES) - bss->wpa_pairwise |= WPA_CIPHER_CCMP; + if (cred->encr_type & WPS_ENCR_AES) { + if (hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD) + bss->wpa_pairwise |= WPA_CIPHER_GCMP; + else + bss->wpa_pairwise |= WPA_CIPHER_CCMP; + } if (cred->encr_type & WPS_ENCR_TKIP) bss->wpa_pairwise |= WPA_CIPHER_TKIP; bss->rsn_pairwise = bss->wpa_pairwise; @@ -448,6 +452,11 @@ static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx) os_free(hapd->wps->network_key); hapd->wps->network_key = NULL; hapd->wps->network_key_len = 0; + } else if ((cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) && + (cred->key_len < 8 || cred->key_len > 2 * PMK_LEN)) { + wpa_printf(MSG_INFO, "WPS: Invalid key length %lu for WPA/WPA2", + (unsigned long) cred->key_len); + return -1; } else { if (hapd->wps->network_key == NULL || hapd->wps->network_key_len < cred->key_len) { @@ -530,7 +539,11 @@ static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx) fprintf(nconf, "wpa_pairwise="); prefix = ""; if (cred->encr_type & WPS_ENCR_AES) { - fprintf(nconf, "CCMP"); + if (hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD) + fprintf(nconf, "GCMP"); + else + fprintf(nconf, "CCMP"); + prefix = " "; } if (cred->encr_type & WPS_ENCR_TKIP) { @@ -844,7 +857,9 @@ static int hostapd_wps_rf_band_cb(void *ctx) struct hostapd_data *hapd = ctx; return hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ? - WPS_RF_50GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */ + WPS_RF_50GHZ : + hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD ? + WPS_RF_60GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */ } @@ -856,8 +871,10 @@ static void hostapd_wps_clear_ies(struct hostapd_data *hapd, int deinit_only) wpabuf_free(hapd->wps_probe_resp_ie); hapd->wps_probe_resp_ie = NULL; - if (deinit_only) + if (deinit_only) { + hostapd_reset_ap_wps_ie(hapd); return; + } hostapd_set_ap_wps_ie(hapd); } @@ -1039,7 +1056,9 @@ int hostapd_init_wps(struct hostapd_data *hapd, } else { wps->dev.rf_bands = hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ? - WPS_RF_50GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */ + WPS_RF_50GHZ : + hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD ? + WPS_RF_60GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */ } if (conf->wpa & WPA_PROTO_RSN) { @@ -1285,30 +1304,53 @@ int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *addr, } +struct wps_button_pushed_ctx { + const u8 *p2p_dev_addr; + unsigned int count; +}; + static int wps_button_pushed(struct hostapd_data *hapd, void *ctx) { - const u8 *p2p_dev_addr = ctx; - if (hapd->wps == NULL) - return -1; - return wps_registrar_button_pushed(hapd->wps->registrar, p2p_dev_addr); + struct wps_button_pushed_ctx *data = ctx; + + if (hapd->wps) { + data->count++; + return wps_registrar_button_pushed(hapd->wps->registrar, + data->p2p_dev_addr); + } + + return 0; } int hostapd_wps_button_pushed(struct hostapd_data *hapd, const u8 *p2p_dev_addr) { - return hostapd_wps_for_each(hapd, wps_button_pushed, - (void *) p2p_dev_addr); + struct wps_button_pushed_ctx ctx; + int ret; + + os_memset(&ctx, 0, sizeof(ctx)); + ctx.p2p_dev_addr = p2p_dev_addr; + ret = hostapd_wps_for_each(hapd, wps_button_pushed, &ctx); + if (ret == 0 && !ctx.count) + ret = -1; + return ret; } +struct wps_cancel_ctx { + unsigned int count; +}; + static int wps_cancel(struct hostapd_data *hapd, void *ctx) { - if (hapd->wps == NULL) - return -1; + struct wps_cancel_ctx *data = ctx; - wps_registrar_wps_cancel(hapd->wps->registrar); - ap_for_each_sta(hapd, ap_sta_wps_cancel, NULL); + if (hapd->wps) { + data->count++; + wps_registrar_wps_cancel(hapd->wps->registrar); + ap_for_each_sta(hapd, ap_sta_wps_cancel, NULL); + } return 0; } @@ -1316,7 +1358,14 @@ static int wps_cancel(struct hostapd_data *hapd, void *ctx) int hostapd_wps_cancel(struct hostapd_data *hapd) { - return hostapd_wps_for_each(hapd, wps_cancel, NULL); + struct wps_cancel_ctx ctx; + int ret; + + os_memset(&ctx, 0, sizeof(ctx)); + ret = hostapd_wps_for_each(hapd, wps_cancel, &ctx); + if (ret == 0 && !ctx.count) + ret = -1; + return ret; } @@ -1546,6 +1595,10 @@ struct wps_ap_pin_data { static int wps_ap_pin_set(struct hostapd_data *hapd, void *ctx) { struct wps_ap_pin_data *data = ctx; + + if (!hapd->wps) + return 0; + os_free(hapd->conf->ap_pin); hapd->conf->ap_pin = os_strdup(data->pin_txt); #ifdef CONFIG_WPS_UPNP diff --git a/contrib/wpa/src/ap/x_snoop.c b/contrib/wpa/src/ap/x_snoop.c index 8f77015..aef9a53 100644 --- a/contrib/wpa/src/ap/x_snoop.c +++ b/contrib/wpa/src/ap/x_snoop.c @@ -51,6 +51,14 @@ int x_snoop_init(struct hostapd_data *hapd) return -1; } +#ifdef CONFIG_IPV6 + if (hostapd_drv_br_set_net_param(hapd, DRV_BR_MULTICAST_SNOOPING, 1)) { + wpa_printf(MSG_DEBUG, + "x_snoop: Failed to enable multicast snooping on the bridge"); + return -1; + } +#endif /* CONFIG_IPV6 */ + return 0; } diff --git a/contrib/wpa/src/common/common_module_tests.c b/contrib/wpa/src/common/common_module_tests.c index 56b1122..d69448b 100644 --- a/contrib/wpa/src/common/common_module_tests.c +++ b/contrib/wpa/src/common/common_module_tests.c @@ -1,6 +1,6 @@ /* * common module tests - * Copyright (c) 2014, Jouni Malinen <j@w1.fi> + * Copyright (c) 2014-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -10,6 +10,8 @@ #include "utils/common.h" #include "ieee802_11_common.h" +#include "ieee802_11_defs.h" +#include "gas.h" #include "wpa_common.h" @@ -46,6 +48,10 @@ static const struct ieee802_11_parse_test_data parse_tests[] = { { (u8 *) "\x6e\x00", 2, ParseOK, 1 }, { (u8 *) "\xc7\x00", 2, ParseOK, 1 }, { (u8 *) "\xc7\x01\x00", 3, ParseOK, 1 }, + { (u8 *) "\x03\x00\x2a\x00\x36\x00\x37\x00\x38\x00\x2d\x00\x3d\x00\xbf\x00\xc0\x00", + 18, ParseOK, 9 }, + { (u8 *) "\x8b\x00", 2, ParseOK, 1 }, + { (u8 *) "\xdd\x04\x00\x90\x4c\x04", 6, ParseUnknown, 1 }, { NULL, 0, ParseOK, 0 } }; @@ -158,6 +164,34 @@ static int rsn_ie_parse_tests(void) } +static int gas_tests(void) +{ + struct wpabuf *buf; + + wpa_printf(MSG_INFO, "gas tests"); + gas_anqp_set_len(NULL); + + buf = wpabuf_alloc(1); + if (buf == NULL) + return -1; + gas_anqp_set_len(buf); + wpabuf_free(buf); + + buf = wpabuf_alloc(20); + if (buf == NULL) + return -1; + wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC); + wpabuf_put_u8(buf, WLAN_PA_GAS_INITIAL_REQ); + wpabuf_put_u8(buf, 0); + wpabuf_put_be32(buf, 0); + wpabuf_put_u8(buf, 0); + gas_anqp_set_len(buf); + wpabuf_free(buf); + + return 0; +} + + int common_module_tests(void) { int ret = 0; @@ -165,6 +199,7 @@ int common_module_tests(void) wpa_printf(MSG_INFO, "common module tests"); if (ieee802_11_parse_tests() < 0 || + gas_tests() < 0 || rsn_ie_parse_tests() < 0) ret = -1; diff --git a/contrib/wpa/src/common/defs.h b/contrib/wpa/src/common/defs.h index b5f4f80..6aea375 100644 --- a/contrib/wpa/src/common/defs.h +++ b/contrib/wpa/src/common/defs.h @@ -174,7 +174,7 @@ enum wpa_states { /** * WPA_INTERFACE_DISABLED - Interface disabled * - * This stat eis entered if the network interface is disabled, e.g., + * This state is entered if the network interface is disabled, e.g., * due to rfkill. wpa_supplicant refuses any new operations that would * use the radio until the interface has been enabled. */ @@ -295,6 +295,7 @@ enum hostapd_hw_mode { HOSTAPD_MODE_IEEE80211G, HOSTAPD_MODE_IEEE80211A, HOSTAPD_MODE_IEEE80211AD, + HOSTAPD_MODE_IEEE80211ANY, NUM_HOSTAPD_MODES }; @@ -310,6 +311,7 @@ enum wpa_ctrl_req_type { WPA_CTRL_REQ_EAP_OTP, WPA_CTRL_REQ_EAP_PASSPHRASE, WPA_CTRL_REQ_SIM, + WPA_CTRL_REQ_PSK_PASSPHRASE, NUM_WPA_CTRL_REQS }; @@ -326,4 +328,10 @@ enum mesh_plink_state { PLINK_BLOCKED, }; +enum set_band { + WPA_SETBAND_AUTO, + WPA_SETBAND_5G, + WPA_SETBAND_2G +}; + #endif /* DEFS_H */ diff --git a/contrib/wpa/src/common/hw_features_common.c b/contrib/wpa/src/common/hw_features_common.c index e8babb5..9c37ea6 100644 --- a/contrib/wpa/src/common/hw_features_common.c +++ b/contrib/wpa/src/common/hw_features_common.c @@ -88,8 +88,8 @@ int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan, int sec_chan) { int ok, j, first; - int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157, - 184, 192 }; + int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 140, + 149, 157, 184, 192 }; size_t k; if (pri_chan == sec_chan || !sec_chan) @@ -152,8 +152,7 @@ void get_pri_sec_chan(struct wpa_scan_res *bss, int *pri_chan, int *sec_chan) *pri_chan = *sec_chan = 0; ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0); - if (elems.ht_operation && - elems.ht_operation_len >= sizeof(*oper)) { + if (elems.ht_operation) { oper = (struct ieee80211_ht_operation *) elems.ht_operation; *pri_chan = oper->primary_chan; if (oper->ht_param & HT_INFO_HT_PARAM_STA_CHNL_WIDTH) { @@ -177,10 +176,8 @@ int check_40mhz_5g(struct hostapd_hw_modes *mode, size_t i; int match; - if (!mode || !scan_res || !pri_chan || !sec_chan) - return 0; - - if (pri_chan == sec_chan) + if (!mode || !scan_res || !pri_chan || !sec_chan || + pri_chan == sec_chan) return 0; pri_freq = hw_get_freq(mode, pri_chan); @@ -238,7 +235,8 @@ int check_40mhz_5g(struct hostapd_hw_modes *mode, } -int check_20mhz_bss(struct wpa_scan_res *bss, int pri_freq, int start, int end) +static int check_20mhz_bss(struct wpa_scan_res *bss, int pri_freq, int start, + int end) { struct ieee802_11_elems elems; struct ieee80211_ht_operation *oper; @@ -253,8 +251,7 @@ int check_20mhz_bss(struct wpa_scan_res *bss, int pri_freq, int start, int end) return 1; } - if (elems.ht_operation && - elems.ht_operation_len >= sizeof(*oper)) { + if (elems.ht_operation) { oper = (struct ieee80211_ht_operation *) elems.ht_operation; if (oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK) return 0; @@ -275,10 +272,8 @@ int check_40mhz_2g4(struct hostapd_hw_modes *mode, int affected_start, affected_end; size_t i; - if (!mode || !scan_res || !pri_chan || !sec_chan) - return 0; - - if (pri_chan == sec_chan) + if (!mode || !scan_res || !pri_chan || !sec_chan || + pri_chan == sec_chan) return 0; pri_freq = hw_get_freq(mode, pri_chan); @@ -335,9 +330,7 @@ int check_40mhz_2g4(struct hostapd_hw_modes *mode, ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0); - if (elems.ht_capabilities && - elems.ht_capabilities_len >= - sizeof(struct ieee80211_ht_capabilities)) { + if (elems.ht_capabilities) { struct ieee80211_ht_capabilities *ht_cap = (struct ieee80211_ht_capabilities *) elems.ht_capabilities; @@ -363,8 +356,6 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data, int vht_oper_chwidth, int center_segment0, int center_segment1, u32 vht_caps) { - int tmp; - os_memset(data, 0, sizeof(*data)); data->mode = mode; data->freq = freq; @@ -378,11 +369,10 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data, if (data->vht_enabled) switch (vht_oper_chwidth) { case VHT_CHANWIDTH_USE_HT: - if (center_segment1) - return -1; - if (center_segment0 != 0 && - 5000 + center_segment0 * 5 != data->center_freq1 && - 2407 + center_segment0 * 5 != data->center_freq1) + if (center_segment1 || + (center_segment0 != 0 && + 5000 + center_segment0 * 5 != data->center_freq1 && + 2407 + center_segment0 * 5 != data->center_freq1)) return -1; break; case VHT_CHANWIDTH_80P80MHZ: @@ -398,19 +388,38 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data, /* fall through */ case VHT_CHANWIDTH_80MHZ: data->bandwidth = 80; - if (vht_oper_chwidth == 1 && center_segment1) + if ((vht_oper_chwidth == 1 && center_segment1) || + (vht_oper_chwidth == 3 && !center_segment1) || + !sec_channel_offset) return -1; - if (vht_oper_chwidth == 3 && !center_segment1) - return -1; - if (!sec_channel_offset) - return -1; - /* primary 40 part must match the HT configuration */ - tmp = (30 + freq - 5000 - center_segment0 * 5) / 20; - tmp /= 2; - if (data->center_freq1 != 5000 + - center_segment0 * 5 - 20 + 40 * tmp) - return -1; - data->center_freq1 = 5000 + center_segment0 * 5; + if (!center_segment0) { + if (channel <= 48) + center_segment0 = 42; + else if (channel <= 64) + center_segment0 = 58; + else if (channel <= 112) + center_segment0 = 106; + else if (channel <= 128) + center_segment0 = 122; + else if (channel <= 144) + center_segment0 = 138; + else if (channel <= 161) + center_segment0 = 155; + data->center_freq1 = 5000 + center_segment0 * 5; + } else { + /* + * Note: HT/VHT config and params are coupled. Check if + * HT40 channel band is in VHT80 Pri channel band + * configuration. + */ + if (center_segment0 == channel + 6 || + center_segment0 == channel + 2 || + center_segment0 == channel - 2 || + center_segment0 == channel - 6) + data->center_freq1 = 5000 + center_segment0 * 5; + else + return -1; + } break; case VHT_CHANWIDTH_160MHZ: data->bandwidth = 160; @@ -424,13 +433,21 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data, return -1; if (!sec_channel_offset) return -1; - /* primary 40 part must match the HT configuration */ - tmp = (70 + freq - 5000 - center_segment0 * 5) / 20; - tmp /= 2; - if (data->center_freq1 != 5000 + - center_segment0 * 5 - 60 + 40 * tmp) + /* + * Note: HT/VHT config and params are coupled. Check if + * HT40 channel band is in VHT160 channel band configuration. + */ + if (center_segment0 == channel + 14 || + center_segment0 == channel + 10 || + center_segment0 == channel + 6 || + center_segment0 == channel + 2 || + center_segment0 == channel - 2 || + center_segment0 == channel - 6 || + center_segment0 == channel - 10 || + center_segment0 == channel - 14) + data->center_freq1 = 5000 + center_segment0 * 5; + else return -1; - data->center_freq1 = 5000 + center_segment0 * 5; break; } diff --git a/contrib/wpa/src/common/hw_features_common.h b/contrib/wpa/src/common/hw_features_common.h index 7f43d00..7360b4e 100644 --- a/contrib/wpa/src/common/hw_features_common.h +++ b/contrib/wpa/src/common/hw_features_common.h @@ -26,7 +26,6 @@ void get_pri_sec_chan(struct wpa_scan_res *bss, int *pri_chan, int *sec_chan); int check_40mhz_5g(struct hostapd_hw_modes *mode, struct wpa_scan_results *scan_res, int pri_chan, int sec_chan); -int check_20mhz_bss(struct wpa_scan_res *bss, int pri_freq, int start, int end); int check_40mhz_2g4(struct hostapd_hw_modes *mode, struct wpa_scan_results *scan_res, int pri_chan, int sec_chan); diff --git a/contrib/wpa/src/common/ieee802_11_common.c b/contrib/wpa/src/common/ieee802_11_common.c index aca0b73..d07a316 100644 --- a/contrib/wpa/src/common/ieee802_11_common.c +++ b/contrib/wpa/src/common/ieee802_11_common.c @@ -1,6 +1,6 @@ /* * IEEE 802.11 Common routines - * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -10,6 +10,8 @@ #include "common.h" #include "defs.h" +#include "wpa_common.h" +#include "qca-vendor.h" #include "ieee802_11_defs.h" #include "ieee802_11_common.h" @@ -146,6 +148,20 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen, } break; + case OUI_QCA: + switch (pos[3]) { + case QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST: + elems->pref_freq_list = pos; + elems->pref_freq_list_len = elen; + break; + default: + wpa_printf(MSG_EXCESSIVE, + "Unknown QCA information element ignored (type=%d len=%lu)", + pos[3], (unsigned long) elen); + return -1; + } + break; + default: wpa_printf(MSG_EXCESSIVE, "unknown vendor specific " "information element ignored (vendor OUI " @@ -196,6 +212,12 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, switch (id) { case WLAN_EID_SSID: + if (elen > SSID_MAX_LEN) { + wpa_printf(MSG_DEBUG, + "Ignored too long SSID element (elen=%u)", + elen); + break; + } elems->ssid = pos; elems->ssid_len = elen; break; @@ -204,8 +226,9 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, elems->supp_rates_len = elen; break; case WLAN_EID_DS_PARAMS: + if (elen < 1) + break; elems->ds_params = pos; - elems->ds_params_len = elen; break; case WLAN_EID_CF_PARAMS: case WLAN_EID_TIM: @@ -215,8 +238,9 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, elems->challenge_len = elen; break; case WLAN_EID_ERP_INFO: + if (elen < 1) + break; elems->erp_info = pos; - elems->erp_info_len = elen; break; case WLAN_EID_EXT_SUPP_RATES: elems->ext_supp_rates = pos; @@ -239,24 +263,31 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, elems->supp_channels_len = elen; break; case WLAN_EID_MOBILITY_DOMAIN: + if (elen < sizeof(struct rsn_mdie)) + break; elems->mdie = pos; elems->mdie_len = elen; break; case WLAN_EID_FAST_BSS_TRANSITION: + if (elen < sizeof(struct rsn_ftie)) + break; elems->ftie = pos; elems->ftie_len = elen; break; case WLAN_EID_TIMEOUT_INTERVAL: + if (elen != 5) + break; elems->timeout_int = pos; - elems->timeout_int_len = elen; break; case WLAN_EID_HT_CAP: + if (elen < sizeof(struct ieee80211_ht_capabilities)) + break; elems->ht_capabilities = pos; - elems->ht_capabilities_len = elen; break; case WLAN_EID_HT_OPERATION: + if (elen < sizeof(struct ieee80211_ht_operation)) + break; elems->ht_operation = pos; - elems->ht_operation_len = elen; break; case WLAN_EID_MESH_CONFIG: elems->mesh_config = pos; @@ -271,12 +302,14 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, elems->peer_mgmt_len = elen; break; case WLAN_EID_VHT_CAP: + if (elen < sizeof(struct ieee80211_vht_capabilities)) + break; elems->vht_capabilities = pos; - elems->vht_capabilities_len = elen; break; case WLAN_EID_VHT_OPERATION: + if (elen < sizeof(struct ieee80211_vht_operation)) + break; elems->vht_operation = pos; - elems->vht_operation_len = elen; break; case WLAN_EID_VHT_OPERATING_MODE_NOTIFICATION: if (elen != 1) @@ -321,6 +354,18 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, /* after mic everything is encrypted, so stop. */ left = elen; break; + case WLAN_EID_MULTI_BAND: + if (elems->mb_ies.nof_ies >= MAX_NOF_MB_IES_SUPPORTED) { + wpa_printf(MSG_MSGDUMP, + "IEEE 802.11 element parse ignored MB IE (id=%d elen=%d)", + id, elen); + break; + } + + elems->mb_ies.ies[elems->mb_ies.nof_ies].ie = pos; + elems->mb_ies.ies[elems->mb_ies.nof_ies].ie_len = elen; + elems->mb_ies.nof_ies++; + break; default: unknown++; if (!show_errors) @@ -486,14 +531,14 @@ int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[], ac->aifs = v; } else if (os_strcmp(pos, "cwmin") == 0) { v = atoi(val); - if (v < 0 || v > 12) { + if (v < 0 || v > 15) { wpa_printf(MSG_ERROR, "Invalid cwMin value %d", v); return -1; } ac->cwmin = v; } else if (os_strcmp(pos, "cwmax") == 0) { v = atoi(val); - if (v < 0 || v > 12) { + if (v < 0 || v > 15) { wpa_printf(MSG_ERROR, "Invalid cwMax value %d", v); return -1; } @@ -523,50 +568,163 @@ int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[], enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel) { - enum hostapd_hw_mode mode = NUM_HOSTAPD_MODES; + u8 op_class; + + return ieee80211_freq_to_channel_ext(freq, 0, 0, &op_class, channel); +} + + +/** + * ieee80211_freq_to_channel_ext - Convert frequency into channel info + * 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 + * @op_class: Buffer for returning operating class + * @channel: Buffer for returning channel number + * Returns: hw_mode on success, NUM_HOSTAPD_MODES on failure + */ +enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq, + int sec_channel, int vht, + u8 *op_class, u8 *channel) +{ + /* TODO: more operating classes */ + + if (sec_channel > 1 || sec_channel < -1) + return NUM_HOSTAPD_MODES; if (freq >= 2412 && freq <= 2472) { - mode = HOSTAPD_MODE_IEEE80211G; + if ((freq - 2407) % 5) + return NUM_HOSTAPD_MODES; + + if (vht) + return NUM_HOSTAPD_MODES; + + /* 2.407 GHz, channels 1..13 */ + if (sec_channel == 1) + *op_class = 83; + else if (sec_channel == -1) + *op_class = 84; + else + *op_class = 81; + *channel = (freq - 2407) / 5; - } else if (freq == 2484) { - mode = HOSTAPD_MODE_IEEE80211B; + + return HOSTAPD_MODE_IEEE80211G; + } + + if (freq == 2484) { + if (sec_channel || vht) + return NUM_HOSTAPD_MODES; + + *op_class = 82; /* channel 14 */ *channel = 14; - } else if (freq >= 4900 && freq < 5000) { - mode = HOSTAPD_MODE_IEEE80211A; + + return HOSTAPD_MODE_IEEE80211B; + } + + if (freq >= 4900 && freq < 5000) { + if ((freq - 4000) % 5) + return NUM_HOSTAPD_MODES; *channel = (freq - 4000) / 5; - } else if (freq >= 5000 && freq < 5900) { - mode = HOSTAPD_MODE_IEEE80211A; + *op_class = 0; /* TODO */ + return HOSTAPD_MODE_IEEE80211A; + } + + /* 5 GHz, channels 36..48 */ + if (freq >= 5180 && freq <= 5240) { + if ((freq - 5000) % 5) + return NUM_HOSTAPD_MODES; + + 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; + + *channel = (freq - 5000) / 5; + + 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; - } else if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 4) { - mode = HOSTAPD_MODE_IEEE80211AD; + + 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; + + *channel = (freq - 5000) / 5; + + return HOSTAPD_MODE_IEEE80211A; + } + + if (freq >= 5000 && freq < 5900) { + if ((freq - 5000) % 5) + return NUM_HOSTAPD_MODES; + *channel = (freq - 5000) / 5; + *op_class = 0; /* TODO */ + return HOSTAPD_MODE_IEEE80211A; + } + + /* 56.16 GHz, channel 1..4 */ + if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 4) { + if (sec_channel || vht) + return NUM_HOSTAPD_MODES; + *channel = (freq - 56160) / 2160; + *op_class = 180; + + return HOSTAPD_MODE_IEEE80211AD; } - return mode; + return NUM_HOSTAPD_MODES; } -static const char *us_op_class_cc[] = { +static const char *const us_op_class_cc[] = { "US", "CA", NULL }; -static const char *eu_op_class_cc[] = { +static const char *const eu_op_class_cc[] = { "AL", "AM", "AT", "AZ", "BA", "BE", "BG", "BY", "CH", "CY", "CZ", "DE", "DK", "EE", "EL", "ES", "FI", "FR", "GE", "HR", "HU", "IE", "IS", "IT", "LI", "LT", "LU", "LV", "MD", "ME", "MK", "MT", "NL", "NO", "PL", "PT", "RO", "RS", "RU", "SE", "SI", "SK", "TR", "UA", "UK", NULL }; -static const char *jp_op_class_cc[] = { +static const char *const jp_op_class_cc[] = { "JP", NULL }; -static const char *cn_op_class_cc[] = { - "CN", "CA", NULL +static const char *const cn_op_class_cc[] = { + "CN", NULL }; -static int country_match(const char *cc[], const char *country) +static int country_match(const char *const cc[], const char *const country) { int i; @@ -612,6 +770,10 @@ static int ieee80211_chan_to_freq_us(u8 op_class, u8 chan) if (chan < 149 || chan > 161) return -1; return 5000 + 5 * chan; + case 5: /* channels 149,153,157,161,165 */ + if (chan < 149 || chan > 165) + return -1; + return 5000 + 5 * chan; case 34: /* 60 GHz band, channels 1..3 */ if (chan < 1 || chan > 3) return -1; @@ -764,12 +926,15 @@ static int ieee80211_chan_to_freq_global(u8 op_class, u8 chan) return -1; return 5000 + 5 * chan; case 124: /* channels 149,153,157,161 */ - case 125: /* channels 149,153,157,161,165,169 */ case 126: /* channels 149,157; 40 MHz */ case 127: /* channels 153,161; 40 MHz */ if (chan < 149 || chan > 161) return -1; return 5000 + 5 * chan; + case 125: /* channels 149,153,157,161,165,169 */ + if (chan < 149 || chan > 169) + return -1; + return 5000 + 5 * chan; case 128: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */ case 130: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */ if (chan < 36 || chan > 161) @@ -921,3 +1086,62 @@ const char * fc2str(u16 fc) return "WLAN_FC_TYPE_UNKNOWN"; #undef C2S } + + +int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf, + size_t ies_len) +{ + os_memset(info, 0, sizeof(*info)); + + while (ies_buf && ies_len >= 2 && + info->nof_ies < MAX_NOF_MB_IES_SUPPORTED) { + size_t len = 2 + ies_buf[1]; + + if (len > ies_len) { + wpa_hexdump(MSG_DEBUG, "Truncated IEs", + ies_buf, ies_len); + return -1; + } + + if (ies_buf[0] == WLAN_EID_MULTI_BAND) { + wpa_printf(MSG_DEBUG, "MB IE of %zu bytes found", len); + info->ies[info->nof_ies].ie = ies_buf + 2; + info->ies[info->nof_ies].ie_len = ies_buf[1]; + info->nof_ies++; + } + + ies_len -= len; + ies_buf += len; + } + + return 0; +} + + +struct wpabuf * mb_ies_by_info(struct mb_ies_info *info) +{ + struct wpabuf *mb_ies = NULL; + + WPA_ASSERT(info != NULL); + + if (info->nof_ies) { + u8 i; + size_t mb_ies_size = 0; + + for (i = 0; i < info->nof_ies; i++) + mb_ies_size += 2 + info->ies[i].ie_len; + + mb_ies = wpabuf_alloc(mb_ies_size); + if (mb_ies) { + for (i = 0; i < info->nof_ies; i++) { + wpabuf_put_u8(mb_ies, WLAN_EID_MULTI_BAND); + wpabuf_put_u8(mb_ies, info->ies[i].ie_len); + wpabuf_put_data(mb_ies, + info->ies[i].ie, + info->ies[i].ie_len); + } + } + } + + return mb_ies; +} diff --git a/contrib/wpa/src/common/ieee802_11_common.h b/contrib/wpa/src/common/ieee802_11_common.h index 7f0b296..55ce022 100644 --- a/contrib/wpa/src/common/ieee802_11_common.h +++ b/contrib/wpa/src/common/ieee802_11_common.h @@ -9,6 +9,16 @@ #ifndef IEEE802_11_COMMON_H #define IEEE802_11_COMMON_H +#define MAX_NOF_MB_IES_SUPPORTED 5 + +struct mb_ies_info { + struct { + const u8 *ie; + u8 ie_len; + } ies[MAX_NOF_MB_IES_SUPPORTED]; + u8 nof_ies; +}; + /* Parsed Information Elements */ struct ieee802_11_elems { const u8 *ssid; @@ -48,12 +58,11 @@ struct ieee802_11_elems { const u8 *osen; const u8 *ampe; const u8 *mic; + const u8 *pref_freq_list; u8 ssid_len; u8 supp_rates_len; - u8 ds_params_len; u8 challenge_len; - u8 erp_info_len; u8 ext_supp_rates_len; u8 wpa_ie_len; u8 rsn_ie_len; @@ -63,14 +72,9 @@ struct ieee802_11_elems { u8 supp_channels_len; u8 mdie_len; u8 ftie_len; - u8 timeout_int_len; - u8 ht_capabilities_len; - u8 ht_operation_len; u8 mesh_config_len; u8 mesh_id_len; u8 peer_mgmt_len; - u8 vht_capabilities_len; - u8 vht_operation_len; u8 vendor_ht_cap_len; u8 vendor_vht_len; u8 p2p_len; @@ -83,6 +87,8 @@ struct ieee802_11_elems { u8 osen_len; u8 ampe_len; u8 mic_len; + u8 pref_freq_list_len; + struct mb_ies_info mb_ies; }; typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes; @@ -108,9 +114,15 @@ int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[], const char *name, const char *val); enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel); int ieee80211_chan_to_freq(const char *country, u8 op_class, u8 chan); +enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq, + int sec_channel, int vht, + u8 *op_class, u8 *channel); int ieee80211_is_dfs(int freq); int supp_rates_11b_only(struct ieee802_11_elems *elems); +int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf, + size_t ies_len); +struct wpabuf * mb_ies_by_info(struct mb_ies_info *info); const char * fc2str(u16 fc); #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 2e51935..44530ce 100644 --- a/contrib/wpa/src/common/ieee802_11_defs.h +++ b/contrib/wpa/src/common/ieee802_11_defs.h @@ -10,6 +10,8 @@ #ifndef IEEE802_11_DEFS_H #define IEEE802_11_DEFS_H +#include <utils/common.h> + /* IEEE 802.11 defines */ #define WLAN_FC_PVER 0x0003 @@ -163,7 +165,10 @@ #define WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ 76 #define WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED 77 #define WLAN_STATUS_TRANSMISSION_FAILURE 79 +#define WLAN_STATUS_REJECTED_WITH_SUGGESTED_BSS_TRANSITION 82 +#define WLAN_STATUS_PENDING_ADMITTING_FST_SESSION 86 #define WLAN_STATUS_QUERY_RESP_OUTSTANDING 95 +#define WLAN_STATUS_DENIED_WITH_SUGGESTED_BAND_AND_CHANNEL 99 #define WLAN_STATUS_ASSOC_DENIED_NO_VHT 104 /* Reason codes (IEEE 802.11-2007, 7.3.1.7, Table 7-22) */ @@ -269,6 +274,8 @@ #define WLAN_EID_AMPE 139 #define WLAN_EID_MIC 140 #define WLAN_EID_CCKM 156 +#define WLAN_EID_MULTI_BAND 158 +#define WLAN_EID_SESSION_TRANSITION 164 #define WLAN_EID_VHT_CAP 191 #define WLAN_EID_VHT_OPERATION 192 #define WLAN_EID_VHT_EXTENDED_BSS_LOAD 193 @@ -297,6 +304,7 @@ #define WLAN_ACTION_TDLS 12 #define WLAN_ACTION_SELF_PROTECTED 15 #define WLAN_ACTION_WMM 17 /* WMM Specification 1.1 */ +#define WLAN_ACTION_FST 18 #define WLAN_ACTION_VENDOR_SPECIFIC 127 /* Public action codes */ @@ -470,35 +478,35 @@ struct ieee80211_mgmt { le16 auth_transaction; le16 status_code; /* possibly followed by Challenge text */ - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED auth; struct { le16 reason_code; - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED deauth; struct { le16 capab_info; le16 listen_interval; /* followed by SSID and Supported rates */ - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED assoc_req; struct { le16 capab_info; le16 status_code; le16 aid; /* followed by Supported rates */ - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED assoc_resp, reassoc_resp; struct { le16 capab_info; le16 listen_interval; u8 current_ap[6]; /* followed by SSID and Supported rates */ - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED reassoc_req; struct { le16 reason_code; - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED disassoc; struct { u8 timestamp[8]; @@ -506,7 +514,7 @@ struct ieee80211_mgmt { le16 capab_info; /* followed by some of SSID, Supported rates, * FH Params, DS Params, CF Params, IBSS Params, TIM */ - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED beacon; struct { /* only variable items: SSID, Supported rates */ @@ -518,7 +526,7 @@ struct ieee80211_mgmt { le16 capab_info; /* followed by some of SSID, Supported rates, * FH Params, DS Params, CF Params, IBSS Params */ - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED probe_resp; struct { u8 category; @@ -527,7 +535,7 @@ struct ieee80211_mgmt { u8 action_code; u8 dialog_token; u8 status_code; - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED wmm_action; struct{ u8 action_code; @@ -541,14 +549,14 @@ struct ieee80211_mgmt { u8 action; u8 sta_addr[ETH_ALEN]; u8 target_ap_addr[ETH_ALEN]; - u8 variable[0]; /* FT Request */ + u8 variable[]; /* FT Request */ } STRUCT_PACKED ft_action_req; struct { u8 action; u8 sta_addr[ETH_ALEN]; u8 target_ap_addr[ETH_ALEN]; le16 status_code; - u8 variable[0]; /* FT Request */ + u8 variable[]; /* FT Request */ } STRUCT_PACKED ft_action_resp; struct { u8 action; @@ -561,23 +569,23 @@ struct ieee80211_mgmt { struct { u8 action; u8 dialogtoken; - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED wnm_sleep_req; struct { u8 action; u8 dialogtoken; le16 keydata_len; - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED wnm_sleep_resp; struct { u8 action; - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED public_action; struct { u8 action; /* 9 */ u8 oui[3]; /* Vendor-specific content */ - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED vs_public_action; struct { u8 action; /* 7 */ @@ -589,7 +597,7 @@ struct ieee80211_mgmt { * Session Information URL (optional), * BSS Transition Candidate List * Entries */ - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED bss_tm_req; struct { u8 action; /* 8 */ @@ -599,7 +607,7 @@ struct ieee80211_mgmt { /* Target BSSID (optional), * BSS Transition Candidate List * Entries (optional) */ - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED bss_tm_resp; struct { u8 action; /* 6 */ @@ -607,12 +615,16 @@ struct ieee80211_mgmt { u8 query_reason; /* BSS Transition Candidate List * Entries (optional) */ - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED bss_tm_query; struct { u8 action; /* 15 */ - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED slf_prot_action; + struct { + u8 action; + u8 variable[]; + } STRUCT_PACKED fst_action; } u; } STRUCT_PACKED action; } u; @@ -1065,6 +1077,15 @@ enum p2p_attr_id { #define P2P_GROUP_CAPAB_GROUP_FORMATION BIT(6) #define P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION BIT(7) +/* P2PS Coordination Protocol Transport Bitmap */ +#define P2PS_FEATURE_CAPAB_UDP_TRANSPORT BIT(0) +#define P2PS_FEATURE_CAPAB_MAC_TRANSPORT BIT(1) + +struct p2ps_feature_capab { + u8 cpt; + u8 reserved; +} STRUCT_PACKED; + /* Invitation Flags */ #define P2P_INVITATION_FLAGS_TYPE BIT(0) @@ -1354,4 +1375,62 @@ 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 */ + u8 len; + u8 mb_ctrl; + u8 band_id; + u8 op_class; + u8 chan; + u8 bssid[ETH_ALEN]; + le16 beacon_int; + u8 tsf_offs[8]; + u8 mb_connection_capability; + u8 fst_session_tmout; + /* Optional: + * STA MAC Address + * Pairwise Cipher Suite Count + * Pairwise Cipher Suite List + */ + u8 variable[0]; +} STRUCT_PACKED; + +enum mb_ctrl_sta_role { + MB_STA_ROLE_AP = 0, + MB_STA_ROLE_TDLS_STA = 1, + MB_STA_ROLE_IBSS_STA = 2, + MB_STA_ROLE_PCP = 3, + MB_STA_ROLE_NON_PCP_NON_AP = 4 +}; + +#define MB_CTRL_ROLE_MASK (BIT(0) | BIT(1) | BIT(2)) +#define MB_CTRL_ROLE(ctrl) ((u8) ((ctrl) & MB_CTRL_ROLE_MASK)) +#define MB_CTRL_STA_MAC_PRESENT ((u8) (BIT(3))) +#define MB_CTRL_PAIRWISE_CIPHER_SUITE_PRESENT ((u8) (BIT(4))) + +enum mb_band_id { + MB_BAND_ID_WIFI_2_4GHZ = 2, /* 2.4 GHz */ + MB_BAND_ID_WIFI_5GHZ = 4, /* 4.9 and 5 GHz */ + MB_BAND_ID_WIFI_60GHZ = 5, /* 60 GHz */ +}; + +#define MB_CONNECTION_CAPABILITY_AP ((u8) (BIT(0))) +#define MB_CONNECTION_CAPABILITY_PCP ((u8) (BIT(1))) +#define MB_CONNECTION_CAPABILITY_DLS ((u8) (BIT(2))) +#define MB_CONNECTION_CAPABILITY_TDLS ((u8) (BIT(3))) +#define MB_CONNECTION_CAPABILITY_IBSS ((u8) (BIT(4))) + +/* IEEE Std 802.11ad-2014 - FST Action field */ +enum fst_action { + FST_ACTION_SETUP_REQUEST = 0, + FST_ACTION_SETUP_RESPONSE = 1, + FST_ACTION_TEAR_DOWN = 2, + FST_ACTION_ACK_REQUEST = 3, + FST_ACTION_ACK_RESPONSE = 4, + FST_ACTION_ON_CHANNEL_TUNNEL = 5, +}; + #endif /* IEEE802_11_DEFS_H */ diff --git a/contrib/wpa/src/common/privsep_commands.h b/contrib/wpa/src/common/privsep_commands.h index 4dc34c4..8dff303 100644 --- a/contrib/wpa/src/common/privsep_commands.h +++ b/contrib/wpa/src/common/privsep_commands.h @@ -9,6 +9,8 @@ #ifndef PRIVSEP_COMMANDS_H #define PRIVSEP_COMMANDS_H +#include "common/ieee802_11_defs.h" + enum privsep_cmd { PRIVSEP_CMD_REGISTER, PRIVSEP_CMD_UNREGISTER, @@ -24,12 +26,31 @@ enum privsep_cmd { PRIVSEP_CMD_L2_NOTIFY_AUTH_START, PRIVSEP_CMD_L2_SEND, PRIVSEP_CMD_SET_COUNTRY, + PRIVSEP_CMD_AUTHENTICATE, +}; + +struct privsep_cmd_authenticate +{ + int freq; + u8 bssid[ETH_ALEN]; + u8 ssid[SSID_MAX_LEN]; + size_t ssid_len; + int auth_alg; + size_t ie_len; + u8 wep_key[4][16]; + size_t wep_key_len[4]; + int wep_tx_keyidx; + int local_state_change; + int p2p; + size_t sae_data_len; + /* followed by ie_len bytes of ie */ + /* followed by sae_data_len bytes of sae_data */ }; struct privsep_cmd_associate { u8 bssid[ETH_ALEN]; - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; size_t ssid_len; int hwmode; int freq; @@ -66,6 +87,18 @@ enum privsep_event { PRIVSEP_EVENT_STKSTART, PRIVSEP_EVENT_FT_RESPONSE, PRIVSEP_EVENT_RX_EAPOL, + PRIVSEP_EVENT_SCAN_STARTED, + PRIVSEP_EVENT_AUTH, +}; + +struct privsep_event_auth { + u8 peer[ETH_ALEN]; + u8 bssid[ETH_ALEN]; + u16 auth_type; + u16 auth_transaction; + u16 status_code; + size_t ies_len; + /* followed by ies_len bytes of ies */ }; #endif /* PRIVSEP_COMMANDS_H */ diff --git a/contrib/wpa/src/common/qca-vendor.h b/contrib/wpa/src/common/qca-vendor.h index 2117ee7..28985f5 100644 --- a/contrib/wpa/src/common/qca-vendor.h +++ b/contrib/wpa/src/common/qca-vendor.h @@ -132,7 +132,7 @@ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY = 50, QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH = 51, QCA_NL80211_VENDOR_SUBCMD_APFIND = 52, - /* 53 - reserved for QCA */ + /* 53 - reserved - was used by QCA, but not in use anymore */ QCA_NL80211_VENDOR_SUBCMD_DO_ACS = 54, QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES = 55, QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED = 56, @@ -142,6 +142,20 @@ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED = 60, /* 61-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, + QCA_NL80211_VENDOR_SUBCMD_OCB_START_TIMING_ADVERT = 94, + QCA_NL80211_VENDOR_SUBCMD_OCB_STOP_TIMING_ADVERT = 95, + QCA_NL80211_VENDOR_SUBCMD_OCB_GET_TSF_TIMER = 96, + QCA_NL80211_VENDOR_SUBCMD_DCC_GET_STATS = 97, + QCA_NL80211_VENDOR_SUBCMD_DCC_CLEAR_STATS = 98, + QCA_NL80211_VENDOR_SUBCMD_DCC_UPDATE_NDL = 99, + QCA_NL80211_VENDOR_SUBCMD_DCC_STATS_EVENT = 100, + QCA_NL80211_VENDOR_SUBCMD_LINK_PROPERTIES = 101, + QCA_NL80211_VENDOR_SUBCMD_GW_PARAM_CONFIG = 102, + QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST = 103, + QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL = 104, + QCA_NL80211_VENDOR_SUBCMD_SETBAND = 105, }; @@ -162,6 +176,15 @@ enum qca_wlan_vendor_attr { /* used by QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES */ QCA_WLAN_VENDOR_ATTR_FEATURE_FLAGS = 7, QCA_WLAN_VENDOR_ATTR_TEST = 8, + /* used by QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES */ + /* Unsigned 32-bit value. */ + QCA_WLAN_VENDOR_ATTR_CONCURRENCY_CAPA = 9, + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_2_4_BAND = 10, + /* Unsigned 32-bit value */ + 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, /* keep last */ QCA_WLAN_VENDOR_ATTR_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_MAX = QCA_WLAN_VENDOR_ATTR_AFTER_LAST - 1, @@ -195,6 +218,12 @@ enum qca_wlan_vendor_attr_acs_offload { QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE, QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED, QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED, + QCA_WLAN_VENDOR_ATTR_ACS_VHT_ENABLED, + QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH, + QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST, + QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL, + QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL, + QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST, /* keep last */ QCA_WLAN_VENDOR_ATTR_ACS_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_ACS_MAX = @@ -206,6 +235,7 @@ enum qca_wlan_vendor_acs_hw_mode { QCA_ACS_MODE_IEEE80211G, QCA_ACS_MODE_IEEE80211A, QCA_ACS_MODE_IEEE80211AD, + QCA_ACS_MODE_IEEE80211ANY, }; /** @@ -215,10 +245,13 @@ enum qca_wlan_vendor_acs_hw_mode { * management offload, a mechanism where the station's firmware * does the exchange with the AP to establish the temporal keys * 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. * @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, NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */ }; @@ -243,4 +276,82 @@ enum qca_wlan_vendor_attr_data_offload_ind { QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_MAX = QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_AFTER_LAST - 1 }; + +enum qca_vendor_attr_get_preferred_freq_list { + QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_INVALID, + /* A 32-unsigned value; the interface type/mode for which the preferred + * frequency list is requested (see enum qca_iface_type for possible + * values); used in GET_PREFERRED_FREQ_LIST command from user-space to + * kernel and in the kernel response back to user-space. + */ + QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE, + /* An array of 32-unsigned values; values are frequency (MHz); sent + * from kernel space to user space. + */ + QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_MAX = + QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_AFTER_LAST - 1 +}; + +enum qca_vendor_attr_probable_oper_channel { + QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_INVALID, + /* 32-bit unsigned value; indicates the connection/iface type likely to + * come on this channel (see enum qca_iface_type). + */ + QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_IFACE_TYPE, + /* 32-bit unsigned value; the frequency (MHz) of the probable channel */ + QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_FREQ, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_MAX = + QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_AFTER_LAST - 1 +}; + +enum qca_iface_type { + QCA_IFACE_TYPE_STA, + QCA_IFACE_TYPE_AP, + QCA_IFACE_TYPE_P2P_CLIENT, + QCA_IFACE_TYPE_P2P_GO, + QCA_IFACE_TYPE_IBSS, + QCA_IFACE_TYPE_TDLS, +}; + +enum qca_set_band { + QCA_SETBAND_AUTO, + QCA_SETBAND_5G, + QCA_SETBAND_2G, +}; + +/* IEEE 802.11 Vendor Specific elements */ + +/** + * enum qca_vendor_element_id - QCA Vendor Specific element types + * + * These values are used to identify QCA Vendor Specific elements. The + * payload of the element starts with the three octet OUI (OUI_QCA) and + * is followed by a single octet type which is defined by this enum. + * + * @QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST: P2P preferred channel list. + * This element can be used to specify preference order for supported + * channels. The channels in this list are in preference order (the first + * one has the highest preference) and are described as a pair of + * (global) Operating Class and Channel Number (each one octet) fields. + * + * This extends the standard P2P functionality by providing option to have + * more than one preferred operating channel. When this element is present, + * it replaces the preference indicated in the Operating Channel attribute. + * For supporting other implementations, the Operating Channel attribute is + * expected to be used with the highest preference channel. Similarly, all + * the channels included in this Preferred channel list element are + * expected to be included in the Channel List attribute. + * + * This vendor element may be included in GO Negotiation Request, P2P + * Invitation Request, and Provision Discovery Request frames. + */ +enum qca_vendor_element_id { + QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST = 0, +}; + #endif /* QCA_VENDOR_H */ diff --git a/contrib/wpa/src/common/sae.c b/contrib/wpa/src/common/sae.c index 5888958..503fa1d 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-2013, Jouni Malinen <j@w1.fi> + * Copyright (c) 2012-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -124,9 +124,7 @@ static struct crypto_bignum * sae_get_rand(struct sae_data *sae) return NULL; for (;;) { - if (iter++ > 100) - return NULL; - if (random_get_bytes(val, order_len) < 0) + if (iter++ > 100 || random_get_bytes(val, order_len) < 0) return NULL; if (order_len_bits % 8) buf_shift_right(val, order_len, 8 - order_len_bits % 8); @@ -171,17 +169,107 @@ static void sae_pwd_seed_key(const u8 *addr1, const u8 *addr2, u8 *key) } +static struct crypto_bignum * +get_rand_1_to_p_1(const u8 *prime, size_t prime_len, size_t prime_bits, + int *r_odd) +{ + for (;;) { + struct crypto_bignum *r; + u8 tmp[SAE_MAX_ECC_PRIME_LEN]; + + if (random_get_bytes(tmp, prime_len) < 0) + break; + if (prime_bits % 8) + buf_shift_right(tmp, prime_len, 8 - prime_bits % 8); + if (os_memcmp(tmp, prime, prime_len) >= 0) + continue; + r = crypto_bignum_init_set(tmp, prime_len); + if (!r) + break; + if (crypto_bignum_is_zero(r)) { + crypto_bignum_deinit(r, 0); + continue; + } + + *r_odd = tmp[prime_len - 1] & 0x01; + return r; + } + + return NULL; +} + + +static int is_quadratic_residue_blind(struct sae_data *sae, + const u8 *prime, size_t bits, + const struct crypto_bignum *qr, + const struct crypto_bignum *qnr, + const struct crypto_bignum *y_sqr) +{ + struct crypto_bignum *r, *num; + int r_odd, check, res = -1; + + /* + * Use the blinding technique to mask y_sqr while determining + * whether it is a quadratic residue modulo p to avoid leaking + * timing information while determining the Legendre symbol. + * + * v = y_sqr + * r = a random number between 1 and p-1, inclusive + * num = (v * r * r) modulo p + */ + r = get_rand_1_to_p_1(prime, sae->tmp->prime_len, bits, &r_odd); + if (!r) + return -1; + + num = crypto_bignum_init(); + if (!num || + crypto_bignum_mulmod(y_sqr, r, sae->tmp->prime, num) < 0 || + crypto_bignum_mulmod(num, r, sae->tmp->prime, num) < 0) + goto fail; + + if (r_odd) { + /* + * num = (num * qr) module p + * LGR(num, p) = 1 ==> quadratic residue + */ + if (crypto_bignum_mulmod(num, qr, sae->tmp->prime, num) < 0) + goto fail; + check = 1; + } else { + /* + * num = (num * qnr) module p + * LGR(num, p) = -1 ==> quadratic residue + */ + if (crypto_bignum_mulmod(num, qnr, sae->tmp->prime, num) < 0) + goto fail; + check = -1; + } + + res = crypto_bignum_legendre(num, sae->tmp->prime); + if (res == -2) { + res = -1; + goto fail; + } + res = res == check; +fail: + crypto_bignum_deinit(num, 1); + crypto_bignum_deinit(r, 1); + return res; +} + + static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed, - struct crypto_ec_point *pwe) + const u8 *prime, + const struct crypto_bignum *qr, + const struct crypto_bignum *qnr, + struct crypto_bignum **ret_x_cand) { - u8 pwd_value[SAE_MAX_ECC_PRIME_LEN], prime[SAE_MAX_ECC_PRIME_LEN]; - struct crypto_bignum *x; - int y_bit; + u8 pwd_value[SAE_MAX_ECC_PRIME_LEN]; + struct crypto_bignum *y_sqr, *x_cand; + int res; size_t bits; - if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime), - sae->tmp->prime_len) < 0) - return -1; + *ret_x_cand = NULL; wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN); @@ -197,20 +285,23 @@ static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed, if (os_memcmp(pwd_value, prime, sae->tmp->prime_len) >= 0) return 0; - y_bit = pwd_seed[SHA256_MAC_LEN - 1] & 0x01; - - x = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len); - if (x == NULL) + x_cand = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len); + if (!x_cand) + return -1; + y_sqr = crypto_ec_point_compute_y_sqr(sae->tmp->ec, x_cand); + if (!y_sqr) { + crypto_bignum_deinit(x_cand, 1); return -1; - if (crypto_ec_point_solve_y_coord(sae->tmp->ec, pwe, x, y_bit) < 0) { - crypto_bignum_deinit(x, 0); - wpa_printf(MSG_DEBUG, "SAE: No solution found"); - return 0; } - crypto_bignum_deinit(x, 0); - wpa_printf(MSG_DEBUG, "SAE: PWE found"); + res = is_quadratic_residue_blind(sae, prime, bits, qr, qnr, y_sqr); + crypto_bignum_deinit(y_sqr, 1); + if (res <= 0) { + crypto_bignum_deinit(x_cand, 1); + return res; + } + *ret_x_cand = x_cand; return 1; } @@ -288,24 +379,77 @@ static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed, } +static int get_random_qr_qnr(const u8 *prime, size_t prime_len, + const struct crypto_bignum *prime_bn, + size_t prime_bits, struct crypto_bignum **qr, + struct crypto_bignum **qnr) +{ + *qr = NULL; + *qnr = NULL; + + while (!(*qr) || !(*qnr)) { + u8 tmp[SAE_MAX_ECC_PRIME_LEN]; + struct crypto_bignum *q; + int res; + + if (random_get_bytes(tmp, prime_len) < 0) + break; + if (prime_bits % 8) + buf_shift_right(tmp, prime_len, 8 - prime_bits % 8); + if (os_memcmp(tmp, prime, prime_len) >= 0) + continue; + q = crypto_bignum_init_set(tmp, prime_len); + if (!q) + break; + res = crypto_bignum_legendre(q, prime_bn); + + if (res == 1 && !(*qr)) + *qr = q; + else if (res == -1 && !(*qnr)) + *qnr = q; + else + crypto_bignum_deinit(q, 0); + } + + return (*qr && *qnr) ? 0 : -1; +} + + static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, const u8 *addr2, const u8 *password, size_t password_len) { - u8 counter, k = 4; + u8 counter, k = 40; u8 addrs[2 * ETH_ALEN]; const u8 *addr[2]; size_t len[2]; - int found = 0; - struct crypto_ec_point *pwe_tmp; + u8 dummy_password[32]; + size_t dummy_password_len; + int pwd_seed_odd = 0; + u8 prime[SAE_MAX_ECC_PRIME_LEN]; + size_t prime_len; + struct crypto_bignum *x = NULL, *qr, *qnr; + size_t bits; + int res; - if (sae->tmp->pwe_ecc == NULL) { - sae->tmp->pwe_ecc = crypto_ec_point_init(sae->tmp->ec); - if (sae->tmp->pwe_ecc == NULL) - return -1; - } - pwe_tmp = crypto_ec_point_init(sae->tmp->ec); - if (pwe_tmp == NULL) + dummy_password_len = password_len; + if (dummy_password_len > sizeof(dummy_password)) + dummy_password_len = sizeof(dummy_password); + if (random_get_bytes(dummy_password, dummy_password_len) < 0) + return -1; + + prime_len = sae->tmp->prime_len; + if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime), + prime_len) < 0) + return -1; + bits = crypto_ec_prime_len_bits(sae->tmp->ec); + + /* + * Create a random quadratic residue (qr) and quadratic non-residue + * (qnr) modulo p for blinding purposes during the loop. + */ + if (get_random_qr_qnr(prime, prime_len, sae->tmp->prime, bits, + &qr, &qnr) < 0) return -1; wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", @@ -313,8 +457,9 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, /* * H(salt, ikm) = HMAC-SHA256(salt, ikm) + * base = password * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC), - * password || counter) + * base || counter) */ sae_pwd_seed_key(addr1, addr2, addrs); @@ -328,9 +473,9 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, * attacks that attempt to determine the number of iterations required * in the loop. */ - for (counter = 1; counter < k || !found; counter++) { + for (counter = 1; counter <= k || !x; counter++) { u8 pwd_seed[SHA256_MAC_LEN]; - int res; + struct crypto_bignum *x_cand; if (counter > 200) { /* This should not happen in practice */ @@ -342,25 +487,58 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, if (hmac_sha256_vector(addrs, sizeof(addrs), 2, addr, len, pwd_seed) < 0) break; + res = sae_test_pwd_seed_ecc(sae, pwd_seed, - found ? pwe_tmp : - sae->tmp->pwe_ecc); + prime, qr, qnr, &x_cand); if (res < 0) - break; - if (res == 0) - continue; - if (found) { - wpa_printf(MSG_DEBUG, "SAE: Ignore this PWE (one was " - "already selected)"); - } else { - wpa_printf(MSG_DEBUG, "SAE: Use this PWE"); - found = 1; + goto fail; + if (res > 0 && !x) { + wpa_printf(MSG_DEBUG, + "SAE: Selected pwd-seed with counter %u", + counter); + x = x_cand; + pwd_seed_odd = pwd_seed[SHA256_MAC_LEN - 1] & 0x01; + os_memset(pwd_seed, 0, sizeof(pwd_seed)); + + /* + * Use a dummy password for the following rounds, if + * any. + */ + addr[0] = dummy_password; + len[0] = dummy_password_len; + } else if (res > 0) { + crypto_bignum_deinit(x_cand, 1); } } - crypto_ec_point_deinit(pwe_tmp, 1); + if (!x) { + wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE"); + res = -1; + goto fail; + } - return found ? 0 : -1; + if (!sae->tmp->pwe_ecc) + sae->tmp->pwe_ecc = crypto_ec_point_init(sae->tmp->ec); + if (!sae->tmp->pwe_ecc) + res = -1; + else + res = crypto_ec_point_solve_y_coord(sae->tmp->ec, + sae->tmp->pwe_ecc, x, + pwd_seed_odd); + crypto_bignum_deinit(x, 1); + if (res < 0) { + /* + * This should not happen since we already checked that there + * is a result. + */ + wpa_printf(MSG_DEBUG, "SAE: Could not solve y"); + } + +fail: + crypto_bignum_deinit(qr, 0); + crypto_bignum_deinit(qnr, 0); + + return res; } @@ -472,27 +650,41 @@ static int sae_derive_commit(struct sae_data *sae) { struct crypto_bignum *mask; int ret = -1; + unsigned int counter = 0; + + do { + counter++; + if (counter > 100) { + /* + * This cannot really happen in practice if the random + * number generator is working. Anyway, to avoid even a + * theoretical infinite loop, break out after 100 + * attemps. + */ + return -1; + } - mask = sae_get_rand_and_mask(sae); - if (mask == NULL) { - wpa_printf(MSG_DEBUG, "SAE: Could not get rand/mask"); - return -1; - } - - /* commit-scalar = (rand + mask) modulo r */ - if (!sae->tmp->own_commit_scalar) { - sae->tmp->own_commit_scalar = crypto_bignum_init(); - if (!sae->tmp->own_commit_scalar) - goto fail; - } - crypto_bignum_add(sae->tmp->sae_rand, mask, - sae->tmp->own_commit_scalar); - crypto_bignum_mod(sae->tmp->own_commit_scalar, sae->tmp->order, - sae->tmp->own_commit_scalar); + mask = sae_get_rand_and_mask(sae); + if (mask == NULL) { + wpa_printf(MSG_DEBUG, "SAE: Could not get rand/mask"); + return -1; + } - if (sae->tmp->ec && sae_derive_commit_element_ecc(sae, mask) < 0) - goto fail; - if (sae->tmp->dh && sae_derive_commit_element_ffc(sae, mask) < 0) + /* commit-scalar = (rand + mask) modulo r */ + if (!sae->tmp->own_commit_scalar) { + sae->tmp->own_commit_scalar = crypto_bignum_init(); + if (!sae->tmp->own_commit_scalar) + goto fail; + } + crypto_bignum_add(sae->tmp->sae_rand, mask, + sae->tmp->own_commit_scalar); + crypto_bignum_mod(sae->tmp->own_commit_scalar, sae->tmp->order, + sae->tmp->own_commit_scalar); + } while (crypto_bignum_is_zero(sae->tmp->own_commit_scalar) || + crypto_bignum_is_one(sae->tmp->own_commit_scalar)); + + if ((sae->tmp->ec && sae_derive_commit_element_ecc(sae, mask) < 0) || + (sae->tmp->dh && sae_derive_commit_element_ffc(sae, mask) < 0)) goto fail; ret = 0; @@ -506,15 +698,12 @@ int sae_prepare_commit(const u8 *addr1, const u8 *addr2, const u8 *password, size_t password_len, struct sae_data *sae) { - if (sae->tmp == NULL) - return -1; - if (sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password, - password_len) < 0) - return -1; - if (sae->tmp->dh && sae_derive_pwe_ffc(sae, addr1, addr2, password, - password_len) < 0) - return -1; - if (sae_derive_commit(sae) < 0) + if (sae->tmp == NULL || + (sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password, + password_len) < 0) || + (sae->tmp->dh && sae_derive_pwe_ffc(sae, addr1, addr2, password, + password_len) < 0) || + sae_derive_commit(sae) < 0) return -1; return 0; } @@ -780,8 +969,9 @@ static u16 sae_parse_commit_scalar(struct sae_data *sae, const u8 **pos, return WLAN_STATUS_UNSPECIFIED_FAILURE; } - /* 0 < scalar < r */ + /* 1 < scalar < r */ if (crypto_bignum_is_zero(peer_scalar) || + crypto_bignum_is_one(peer_scalar) || crypto_bignum_cmp(peer_scalar, sae->tmp->order) >= 0) { wpa_printf(MSG_DEBUG, "SAE: Invalid peer scalar"); crypto_bignum_deinit(peer_scalar, 0); @@ -847,7 +1037,8 @@ static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 *pos, static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 *pos, const u8 *end) { - struct crypto_bignum *res; + struct crypto_bignum *res, *one; + const u8 one_bin[1] = { 0x01 }; if (pos + sae->tmp->prime_len > end) { wpa_printf(MSG_DEBUG, "SAE: Not enough data for " @@ -862,18 +1053,23 @@ static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 *pos, crypto_bignum_init_set(pos, sae->tmp->prime_len); if (sae->tmp->peer_commit_element_ffc == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; - if (crypto_bignum_is_zero(sae->tmp->peer_commit_element_ffc) || + /* 1 < element < p - 1 */ + res = crypto_bignum_init(); + one = crypto_bignum_init_set(one_bin, sizeof(one_bin)); + if (!res || !one || + crypto_bignum_sub(sae->tmp->prime, one, res) || + crypto_bignum_is_zero(sae->tmp->peer_commit_element_ffc) || crypto_bignum_is_one(sae->tmp->peer_commit_element_ffc) || - crypto_bignum_cmp(sae->tmp->peer_commit_element_ffc, - sae->tmp->prime) >= 0) { + crypto_bignum_cmp(sae->tmp->peer_commit_element_ffc, res) >= 0) { + crypto_bignum_deinit(res, 0); + crypto_bignum_deinit(one, 0); wpa_printf(MSG_DEBUG, "SAE: Invalid peer element"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } + crypto_bignum_deinit(one, 0); /* scalar-op(r, ELEMENT) = 1 modulo p */ - res = crypto_bignum_init(); - if (res == NULL || - crypto_bignum_exptmod(sae->tmp->peer_commit_element_ffc, + if (crypto_bignum_exptmod(sae->tmp->peer_commit_element_ffc, sae->tmp->order, sae->tmp->prime, res) < 0 || !crypto_bignum_is_one(res)) { wpa_printf(MSG_DEBUG, "SAE: Invalid peer element (scalar-op)"); @@ -918,7 +1114,34 @@ u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, return res; /* commit-element */ - return sae_parse_commit_element(sae, pos, end); + res = sae_parse_commit_element(sae, pos, end); + if (res != WLAN_STATUS_SUCCESS) + return res; + + /* + * Check whether peer-commit-scalar and PEER-COMMIT-ELEMENT are same as + * the values we sent which would be evidence of a reflection attack. + */ + if (!sae->tmp->own_commit_scalar || + crypto_bignum_cmp(sae->tmp->own_commit_scalar, + sae->peer_commit_scalar) != 0 || + (sae->tmp->dh && + (!sae->tmp->own_commit_element_ffc || + crypto_bignum_cmp(sae->tmp->own_commit_element_ffc, + sae->tmp->peer_commit_element_ffc) != 0)) || + (sae->tmp->ec && + (!sae->tmp->own_commit_element_ecc || + crypto_ec_point_cmp(sae->tmp->ec, + sae->tmp->own_commit_element_ecc, + sae->tmp->peer_commit_element_ecc) != 0))) + return WLAN_STATUS_SUCCESS; /* scalars/elements are different */ + + /* + * This is a reflection attack - return special value to trigger caller + * to silently discard the frame instead of replying with a specific + * status code. + */ + return SAE_SILENTLY_DISCARD; } diff --git a/contrib/wpa/src/common/sae.h b/contrib/wpa/src/common/sae.h index 3ebf40c..c07026c 100644 --- a/contrib/wpa/src/common/sae.h +++ b/contrib/wpa/src/common/sae.h @@ -18,6 +18,9 @@ #define SAE_COMMIT_MAX_LEN (2 + 3 * SAE_MAX_PRIME_LEN) #define SAE_CONFIRM_MAX_LEN (2 + SAE_MAX_PRIME_LEN) +/* Special value returned by sae_parse_commit() */ +#define SAE_SILENTLY_DISCARD 65535 + struct sae_temporary_data { u8 kck[SAE_KCK_LEN]; struct crypto_bignum *own_commit_scalar; diff --git a/contrib/wpa/src/common/version.h b/contrib/wpa/src/common/version.h index e39a8db..a5cc5b7 100644 --- a/contrib/wpa/src/common/version.h +++ b/contrib/wpa/src/common/version.h @@ -5,6 +5,6 @@ #define VERSION_STR_POSTFIX "" #endif /* VERSION_STR_POSTFIX */ -#define VERSION_STR "2.4" VERSION_STR_POSTFIX +#define VERSION_STR "2.5" 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 de81d53..e9d4248 100644 --- a/contrib/wpa/src/common/wpa_common.c +++ b/contrib/wpa/src/common/wpa_common.c @@ -170,6 +170,12 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, ptk->tk_len = wpa_cipher_key_len(cipher); ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len; +#ifdef CONFIG_SUITEB192 + if (wpa_key_mgmt_sha384(akmp)) + sha384_prf(pmk, pmk_len, label, data, sizeof(data), + tmp, ptk_len); + else +#endif /* CONFIG_SUITEB192 */ #ifdef CONFIG_IEEE80211W if (wpa_key_mgmt_sha256(akmp)) sha256_prf(pmk, pmk_len, label, data, sizeof(data), @@ -207,8 +213,10 @@ int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr, const u8 *rsnie, size_t rsnie_len, const u8 *ric, size_t ric_len, u8 *mic) { - u8 *buf, *pos; - size_t buf_len; + const u8 *addr[9]; + size_t len[9]; + size_t i, num_elem = 0; + u8 zero_mic[16]; if (kck_len != 16) { wpa_printf(MSG_WARNING, "FT: Unsupported KCK length %u", @@ -216,48 +224,58 @@ int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr, return -1; } - buf_len = 2 * ETH_ALEN + 1 + mdie_len + ftie_len + rsnie_len + ric_len; - buf = os_malloc(buf_len); - if (buf == NULL) - return -1; + addr[num_elem] = sta_addr; + len[num_elem] = ETH_ALEN; + num_elem++; + + addr[num_elem] = ap_addr; + len[num_elem] = ETH_ALEN; + num_elem++; + + addr[num_elem] = &transaction_seqnum; + len[num_elem] = 1; + num_elem++; - pos = buf; - os_memcpy(pos, sta_addr, ETH_ALEN); - pos += ETH_ALEN; - os_memcpy(pos, ap_addr, ETH_ALEN); - pos += ETH_ALEN; - *pos++ = transaction_seqnum; if (rsnie) { - os_memcpy(pos, rsnie, rsnie_len); - pos += rsnie_len; + addr[num_elem] = rsnie; + len[num_elem] = rsnie_len; + num_elem++; } if (mdie) { - os_memcpy(pos, mdie, mdie_len); - pos += mdie_len; + addr[num_elem] = mdie; + len[num_elem] = mdie_len; + num_elem++; } if (ftie) { - struct rsn_ftie *_ftie; - os_memcpy(pos, ftie, ftie_len); - if (ftie_len < 2 + sizeof(*_ftie)) { - os_free(buf); + if (ftie_len < 2 + sizeof(struct rsn_ftie)) return -1; - } - _ftie = (struct rsn_ftie *) (pos + 2); - os_memset(_ftie->mic, 0, sizeof(_ftie->mic)); - pos += ftie_len; + + /* IE hdr and mic_control */ + addr[num_elem] = ftie; + len[num_elem] = 2 + 2; + num_elem++; + + /* MIC field with all zeros */ + os_memset(zero_mic, 0, sizeof(zero_mic)); + addr[num_elem] = zero_mic; + len[num_elem] = sizeof(zero_mic); + num_elem++; + + /* Rest of FTIE */ + addr[num_elem] = ftie + 2 + 2 + 16; + len[num_elem] = ftie_len - (2 + 2 + 16); + num_elem++; } if (ric) { - os_memcpy(pos, ric, ric_len); - pos += ric_len; + addr[num_elem] = ric; + len[num_elem] = ric_len; + num_elem++; } - wpa_hexdump(MSG_MSGDUMP, "FT: MIC data", buf, pos - buf); - if (omac1_aes_128(kck, buf, pos - buf, mic)) { - os_free(buf); + for (i = 0; i < num_elem; i++) + wpa_hexdump(MSG_MSGDUMP, "FT: MIC data", addr[i], len[i]); + if (omac1_aes_128_vector(kck, num_elem, addr, len, mic)) return -1; - } - - os_free(buf); return 0; } @@ -344,6 +362,8 @@ 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)) + return -1; parse->mdie = pos + 2; parse->mdie_len = pos[1]; break; @@ -356,6 +376,8 @@ int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, return -1; break; case WLAN_EID_TIMEOUT_INTERVAL: + if (pos[1] != 5) + break; parse->tie = pos + 2; parse->tie_len = pos[1]; break; @@ -416,14 +438,10 @@ static int rsn_selector_to_bitfield(const u8 *s) { if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_NONE) return WPA_CIPHER_NONE; - if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_WEP40) - return WPA_CIPHER_WEP40; if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_TKIP) return WPA_CIPHER_TKIP; if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_CCMP) return WPA_CIPHER_CCMP; - if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_WEP104) - return WPA_CIPHER_WEP104; #ifdef CONFIG_IEEE80211W if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_AES_128_CMAC) return WPA_CIPHER_AES_128_CMAC; @@ -474,15 +492,15 @@ static int rsn_key_mgmt_to_bitfield(const u8 *s) return WPA_KEY_MGMT_IEEE8021X_SUITE_B; if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192) return WPA_KEY_MGMT_IEEE8021X_SUITE_B_192; + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_OSEN) + return WPA_KEY_MGMT_OSEN; return 0; } -static int wpa_cipher_valid_group(int cipher) +int wpa_cipher_valid_group(int cipher) { return wpa_cipher_valid_pairwise(cipher) || - cipher == WPA_CIPHER_WEP104 || - cipher == WPA_CIPHER_WEP40 || cipher == WPA_CIPHER_GTK_NOT_USED; } @@ -508,7 +526,6 @@ int wpa_cipher_valid_mgmt_group(int cipher) int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, struct wpa_ie_data *data) { - const struct rsn_ie_hdr *hdr; const u8 *pos; int left; int i, count; @@ -538,18 +555,29 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, return -1; } - hdr = (const struct rsn_ie_hdr *) rsn_ie; + if (rsn_ie_len >= 6 && rsn_ie[1] >= 4 && + rsn_ie[1] == rsn_ie_len - 2 && + WPA_GET_BE32(&rsn_ie[2]) == OSEN_IE_VENDOR_TYPE) { + pos = rsn_ie + 6; + left = rsn_ie_len - 6; - if (hdr->elem_id != WLAN_EID_RSN || - hdr->len != rsn_ie_len - 2 || - WPA_GET_LE16(hdr->version) != RSN_VERSION) { - wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version", - __func__); - return -2; - } + data->proto = WPA_PROTO_OSEN; + } else { + const struct rsn_ie_hdr *hdr; - pos = (const u8 *) (hdr + 1); - left = rsn_ie_len - sizeof(*hdr); + hdr = (const struct rsn_ie_hdr *) rsn_ie; + + if (hdr->elem_id != WLAN_EID_RSN || + hdr->len != rsn_ie_len - 2 || + WPA_GET_LE16(hdr->version) != RSN_VERSION) { + wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version", + __func__); + return -2; + } + + pos = (const u8 *) (hdr + 1); + left = rsn_ie_len - sizeof(*hdr); + } if (left >= RSN_SELECTOR_LEN) { data->group_cipher = rsn_selector_to_bitfield(pos); @@ -667,14 +695,10 @@ static int wpa_selector_to_bitfield(const u8 *s) { if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_NONE) return WPA_CIPHER_NONE; - if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP40) - return WPA_CIPHER_WEP40; if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_TKIP) return WPA_CIPHER_TKIP; if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_CCMP) return WPA_CIPHER_CCMP; - if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP104) - return WPA_CIPHER_WEP104; return 0; } @@ -709,11 +733,6 @@ int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, data->num_pmkid = 0; data->mgmt_group_cipher = 0; - if (wpa_ie_len == 0) { - /* No WPA IE - fail silently */ - return -1; - } - if (wpa_ie_len < sizeof(struct wpa_ie_hdr)) { wpa_printf(MSG_DEBUG, "%s: ie len too short %lu", __func__, (unsigned long) wpa_ie_len); @@ -814,7 +833,7 @@ void wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len, const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len, const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name) { - u8 buf[1 + WPA_MAX_SSID_LEN + MOBILITY_DOMAIN_ID_LEN + 1 + + u8 buf[1 + SSID_MAX_LEN + MOBILITY_DOMAIN_ID_LEN + 1 + FT_R0KH_ID_MAX_LEN + ETH_ALEN]; u8 *pos, r0_key_data[48], hash[32]; const u8 *addr[2]; @@ -828,7 +847,7 @@ void wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len, * PMK-R0 = L(R0-Key-Data, 0, 256) * PMK-R0Name-Salt = L(R0-Key-Data, 256, 128) */ - if (ssid_len > WPA_MAX_SSID_LEN || r0kh_id_len > FT_R0KH_ID_MAX_LEN) + if (ssid_len > SSID_MAX_LEN || r0kh_id_len > FT_R0KH_ID_MAX_LEN) return; pos = buf; *pos++ = ssid_len; @@ -1279,6 +1298,9 @@ int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid) os_memmove(rpos + 2, rpos, end - rpos); *rpos++ = 0; *rpos++ = 0; + added += 2; + start[1] += 2; + rend = rpos; } else { /* Skip RSN Capabilities */ rpos += 2; @@ -1291,7 +1313,7 @@ int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid) if (rpos == rend) { /* No PMKID-Count field included; add it */ - os_memmove(rpos + 2 + PMKID_LEN, rpos, end - rpos); + os_memmove(rpos + 2 + PMKID_LEN, rpos, end + added - rpos); WPA_PUT_LE16(rpos, 1); rpos += 2; os_memcpy(rpos, pmkid, PMKID_LEN); @@ -1306,7 +1328,7 @@ int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid) } WPA_PUT_LE16(rpos, 1); rpos += 2; - os_memmove(rpos + PMKID_LEN, rpos, end - rpos); + os_memmove(rpos + PMKID_LEN, rpos, end + added - rpos); os_memcpy(rpos, pmkid, PMKID_LEN); added += PMKID_LEN; start[1] += PMKID_LEN; @@ -1335,10 +1357,6 @@ int wpa_cipher_key_len(int cipher) return 16; case WPA_CIPHER_TKIP: return 32; - case WPA_CIPHER_WEP104: - return 13; - case WPA_CIPHER_WEP40: - return 5; } return 0; @@ -1354,9 +1372,6 @@ int wpa_cipher_rsc_len(int cipher) case WPA_CIPHER_GCMP: case WPA_CIPHER_TKIP: return 6; - case WPA_CIPHER_WEP104: - case WPA_CIPHER_WEP40: - return 0; } return 0; @@ -1376,9 +1391,6 @@ int wpa_cipher_to_alg(int cipher) return WPA_ALG_GCMP; case WPA_CIPHER_TKIP: return WPA_ALG_TKIP; - case WPA_CIPHER_WEP104: - case WPA_CIPHER_WEP40: - return WPA_ALG_WEP; case WPA_CIPHER_AES_128_CMAC: return WPA_ALG_IGTK; case WPA_CIPHER_BIP_GMAC_128: @@ -1416,12 +1428,6 @@ u32 wpa_cipher_to_suite(int proto, int cipher) if (cipher & WPA_CIPHER_TKIP) return (proto == WPA_PROTO_RSN ? RSN_CIPHER_SUITE_TKIP : WPA_CIPHER_SUITE_TKIP); - if (cipher & WPA_CIPHER_WEP104) - return (proto == WPA_PROTO_RSN ? - RSN_CIPHER_SUITE_WEP104 : WPA_CIPHER_SUITE_WEP104); - if (cipher & WPA_CIPHER_WEP40) - return (proto == WPA_PROTO_RSN ? - RSN_CIPHER_SUITE_WEP40 : WPA_CIPHER_SUITE_WEP40); if (cipher & WPA_CIPHER_NONE) return (proto == WPA_PROTO_RSN ? RSN_CIPHER_SUITE_NONE : WPA_CIPHER_SUITE_NONE); @@ -1525,10 +1531,6 @@ int wpa_pick_group_cipher(int ciphers) return WPA_CIPHER_GTK_NOT_USED; if (ciphers & WPA_CIPHER_TKIP) return WPA_CIPHER_TKIP; - if (ciphers & WPA_CIPHER_WEP104) - return WPA_CIPHER_WEP104; - if (ciphers & WPA_CIPHER_WEP40) - return WPA_CIPHER_WEP40; return -1; } @@ -1626,20 +1628,6 @@ int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim) return -1; pos += ret; } - if (ciphers & WPA_CIPHER_WEP104) { - ret = os_snprintf(pos, end - pos, "%sWEP104", - pos == start ? "" : delim); - if (os_snprintf_error(end - pos, ret)) - return -1; - pos += ret; - } - if (ciphers & WPA_CIPHER_WEP40) { - ret = os_snprintf(pos, end - pos, "%sWEP40", - pos == start ? "" : delim); - if (os_snprintf_error(end - pos, ret)) - return -1; - pos += ret; - } if (ciphers & WPA_CIPHER_NONE) { ret = os_snprintf(pos, end - pos, "%sNONE", pos == start ? "" : delim); diff --git a/contrib/wpa/src/common/wpa_common.h b/contrib/wpa/src/common/wpa_common.h index 091e317..c08f651 100644 --- a/contrib/wpa/src/common/wpa_common.h +++ b/contrib/wpa/src/common/wpa_common.h @@ -9,8 +9,6 @@ #ifndef WPA_COMMON_H #define WPA_COMMON_H -#define WPA_MAX_SSID_LEN 32 - /* IEEE 802.11i */ #define PMKID_LEN 16 #define PMK_LEN 32 @@ -24,8 +22,8 @@ (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | WPA_CIPHER_NONE | \ WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256) #define WPA_ALLOWED_GROUP_CIPHERS \ -(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | WPA_CIPHER_WEP104 | \ -WPA_CIPHER_WEP40 | WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256 | \ +(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | \ +WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256 | \ WPA_CIPHER_GTK_NOT_USED) #define WPA_SELECTOR_LEN 4 @@ -42,13 +40,8 @@ WPA_CIPHER_GTK_NOT_USED) #define WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X RSN_SELECTOR(0x00, 0x50, 0xf2, 2) #define WPA_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0) #define WPA_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x50, 0xf2, 0) -#define WPA_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x50, 0xf2, 1) #define WPA_CIPHER_SUITE_TKIP RSN_SELECTOR(0x00, 0x50, 0xf2, 2) -#if 0 -#define WPA_CIPHER_SUITE_WRAP RSN_SELECTOR(0x00, 0x50, 0xf2, 3) -#endif #define WPA_CIPHER_SUITE_CCMP RSN_SELECTOR(0x00, 0x50, 0xf2, 4) -#define WPA_CIPHER_SUITE_WEP104 RSN_SELECTOR(0x00, 0x50, 0xf2, 5) #define RSN_AUTH_KEY_MGMT_UNSPEC_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 1) @@ -70,13 +63,11 @@ RSN_SELECTOR(0x00, 0x0f, 0xac, 13) #define RSN_AUTH_KEY_MGMT_OSEN RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x01) #define RSN_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x0f, 0xac, 0) -#define RSN_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x0f, 0xac, 1) #define RSN_CIPHER_SUITE_TKIP RSN_SELECTOR(0x00, 0x0f, 0xac, 2) #if 0 #define RSN_CIPHER_SUITE_WRAP RSN_SELECTOR(0x00, 0x0f, 0xac, 3) #endif #define RSN_CIPHER_SUITE_CCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 4) -#define RSN_CIPHER_SUITE_WEP104 RSN_SELECTOR(0x00, 0x0f, 0xac, 5) #define RSN_CIPHER_SUITE_AES_128_CMAC RSN_SELECTOR(0x00, 0x0f, 0xac, 6) #define RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED RSN_SELECTOR(0x00, 0x0f, 0xac, 7) #define RSN_CIPHER_SUITE_GCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 8) @@ -308,7 +299,6 @@ struct wpa_igtk_kde { } STRUCT_PACKED; #endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_IEEE80211R struct rsn_mdie { u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; u8 ft_capab; @@ -336,7 +326,6 @@ struct rsn_rdie { le16 status_code; } STRUCT_PACKED; -#endif /* CONFIG_IEEE80211R */ #ifdef _MSC_VER #pragma pack(pop) @@ -446,6 +435,7 @@ int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse); int wpa_cipher_key_len(int cipher); int wpa_cipher_rsc_len(int cipher); int wpa_cipher_to_alg(int cipher); +int wpa_cipher_valid_group(int cipher); int wpa_cipher_valid_pairwise(int cipher); int wpa_cipher_valid_mgmt_group(int cipher); u32 wpa_cipher_to_suite(int proto, int cipher); diff --git a/contrib/wpa/src/common/wpa_ctrl.c b/contrib/wpa/src/common/wpa_ctrl.c index ccaaf1b..5733aa6 100644 --- a/contrib/wpa/src/common/wpa_ctrl.c +++ b/contrib/wpa/src/common/wpa_ctrl.c @@ -21,6 +21,7 @@ #ifdef ANDROID #include <dirent.h> +#include <sys/stat.h> #include <cutils/sockets.h> #include "private/android_filesystem_config.h" #endif /* ANDROID */ @@ -84,6 +85,13 @@ struct wpa_ctrl { struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) { + return wpa_ctrl_open2(ctrl_path, NULL); +} + + +struct wpa_ctrl * wpa_ctrl_open2(const char *ctrl_path, + const char *cli_path) +{ struct wpa_ctrl *ctrl; static int counter = 0; int ret; @@ -107,10 +115,18 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) ctrl->local.sun_family = AF_UNIX; counter++; try_again: - ret = os_snprintf(ctrl->local.sun_path, sizeof(ctrl->local.sun_path), - CONFIG_CTRL_IFACE_CLIENT_DIR "/" - CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d", - (int) getpid(), counter); + if (cli_path && cli_path[0] == '/') { + ret = os_snprintf(ctrl->local.sun_path, + sizeof(ctrl->local.sun_path), + "%s/" CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d", + cli_path, (int) getpid(), counter); + } else { + ret = os_snprintf(ctrl->local.sun_path, + sizeof(ctrl->local.sun_path), + CONFIG_CTRL_IFACE_CLIENT_DIR "/" + CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d", + (int) getpid(), counter); + } if (os_snprintf_error(sizeof(ctrl->local.sun_path), ret)) { close(ctrl->s); os_free(ctrl); @@ -136,6 +152,8 @@ try_again: #ifdef ANDROID chmod(ctrl->local.sun_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + /* Set group even if we do not have privileges to change owner */ + chown(ctrl->local.sun_path, -1, AID_WIFI); chown(ctrl->local.sun_path, AID_SYSTEM, AID_WIFI); if (os_strncmp(ctrl_path, "@android:", 9) == 0) { diff --git a/contrib/wpa/src/common/wpa_ctrl.h b/contrib/wpa/src/common/wpa_ctrl.h index 1d19fc5..3de4682 100644 --- a/contrib/wpa/src/common/wpa_ctrl.h +++ b/contrib/wpa/src/common/wpa_ctrl.h @@ -28,6 +28,8 @@ extern "C" { #define WPA_EVENT_DISCONNECTED "CTRL-EVENT-DISCONNECTED " /** Association rejected during connection attempt */ #define WPA_EVENT_ASSOC_REJECT "CTRL-EVENT-ASSOC-REJECT " +/** Authentication rejected during connection attempt */ +#define WPA_EVENT_AUTH_REJECT "CTRL-EVENT-AUTH-REJECT " /** wpa_supplicant is exiting */ #define WPA_EVENT_TERMINATING "CTRL-EVENT-TERMINATING " /** Password change was completed successfully */ @@ -68,6 +70,8 @@ extern "C" { #define WPA_EVENT_BSS_ADDED "CTRL-EVENT-BSS-ADDED " /** A BSS entry was removed (followed by BSS entry id and BSSID) */ #define WPA_EVENT_BSS_REMOVED "CTRL-EVENT-BSS-REMOVED " +/** No suitable network was found */ +#define WPA_EVENT_NETWORK_NOT_FOUND "CTRL-EVENT-NETWORK-NOT-FOUND " /** Change in the signal level was reported by the driver */ #define WPA_EVENT_SIGNAL_CHANGE "CTRL-EVENT-SIGNAL-CHANGE " /** Regulatory domain channel */ @@ -227,6 +231,7 @@ extern "C" { #define WPS_EVENT_AP_PIN_DISABLED "WPS-AP-PIN-DISABLED " #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_REJECTED_MAX_STA "AP-REJECTED-MAX-STA " #define AP_REJECTED_BLOCKED_STA "AP-REJECTED-BLOCKED-STA " @@ -276,6 +281,7 @@ extern "C" { #define WPA_BSS_MASK_MESH_SCAN BIT(18) #define WPA_BSS_MASK_SNR BIT(19) #define WPA_BSS_MASK_EST_THROUGHPUT BIT(20) +#define WPA_BSS_MASK_FST BIT(21) /* VENDOR_ELEM_* frame id values */ @@ -312,6 +318,20 @@ enum wpa_vendor_elem_frame { */ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path); +/** + * wpa_ctrl_open2 - Open a control interface to wpa_supplicant/hostapd + * @ctrl_path: Path for UNIX domain sockets; ignored if UDP sockets are used. + * @cli_path: Path for client UNIX domain sockets; ignored if UDP socket + * is used. + * Returns: Pointer to abstract control interface data or %NULL on failure + * + * This function is used to open a control interface to wpa_supplicant/hostapd + * when the socket path for client need to be specified explicitly. Default + * ctrl_path is usually /var/run/wpa_supplicant or /var/run/hostapd and client + * socket path is /tmp. + */ +struct wpa_ctrl * wpa_ctrl_open2(const char *ctrl_path, const char *cli_path); + /** * wpa_ctrl_close - Close a control interface to wpa_supplicant/hostapd diff --git a/contrib/wpa/src/crypto/crypto.h b/contrib/wpa/src/crypto/crypto.h index f2d5662..534c4bd 100644 --- a/contrib/wpa/src/crypto/crypto.h +++ b/contrib/wpa/src/crypto/crypto.h @@ -614,6 +614,15 @@ int crypto_bignum_is_zero(const struct crypto_bignum *a); int crypto_bignum_is_one(const struct crypto_bignum *a); /** + * crypto_bignum_legendre - Compute the Legendre symbol (a/p) + * @a: Bignum + * @p: Bignum + * Returns: Legendre symbol -1,0,1 on success; -2 on calculation failure + */ +int crypto_bignum_legendre(const struct crypto_bignum *a, + const struct crypto_bignum *p); + +/** * struct crypto_ec - Elliptic curve context * * Internal data structure for EC implementation. The contents is specific @@ -758,6 +767,16 @@ int crypto_ec_point_solve_y_coord(struct crypto_ec *e, const struct crypto_bignum *x, int y_bit); /** + * crypto_ec_point_compute_y_sqr - Compute y^2 = x^3 + ax + b + * @e: EC context from crypto_ec_init() + * @x: x coordinate + * Returns: y^2 on success, %NULL failure + */ +struct crypto_bignum * +crypto_ec_point_compute_y_sqr(struct crypto_ec *e, + const struct crypto_bignum *x); + +/** * crypto_ec_point_is_at_infinity - Check whether EC point is neutral element * @e: EC context from crypto_ec_init() * @p: EC point @@ -776,4 +795,15 @@ int crypto_ec_point_is_at_infinity(struct crypto_ec *e, int crypto_ec_point_is_on_curve(struct crypto_ec *e, const struct crypto_ec_point *p); +/** + * crypto_ec_point_cmp - Compare two EC points + * @e: EC context from crypto_ec_init() + * @a: EC point + * @b: EC point + * Returns: 0 on equal, non-zero otherwise + */ +int crypto_ec_point_cmp(const struct crypto_ec *e, + const struct crypto_ec_point *a, + const struct crypto_ec_point *b); + #endif /* CRYPTO_H */ diff --git a/contrib/wpa/src/crypto/crypto_cryptoapi.c b/contrib/wpa/src/crypto/crypto_cryptoapi.c deleted file mode 100644 index 55a069b..0000000 --- a/contrib/wpa/src/crypto/crypto_cryptoapi.c +++ /dev/null @@ -1,783 +0,0 @@ -/* - * Crypto wrapper for Microsoft CryptoAPI - * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi> - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -#include "includes.h" -#include <windows.h> -#include <wincrypt.h> - -#include "common.h" -#include "crypto.h" - -#ifndef MS_ENH_RSA_AES_PROV -#ifdef UNICODE -#define MS_ENH_RSA_AES_PROV \ -L"Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)" -#else -#define MS_ENH_RSA_AES_PROV \ -"Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)" -#endif -#endif /* MS_ENH_RSA_AES_PROV */ - -#ifndef CALG_HMAC -#define CALG_HMAC (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_HMAC) -#endif - -#ifdef __MINGW32_VERSION -/* - * MinGW does not yet include all the needed definitions for CryptoAPI, so - * define here whatever extra is needed. - */ - -static BOOL WINAPI -(*CryptImportPublicKeyInfo)(HCRYPTPROV hCryptProv, DWORD dwCertEncodingType, - PCERT_PUBLIC_KEY_INFO pInfo, HCRYPTKEY *phKey) -= NULL; /* to be loaded from crypt32.dll */ - - -static int mingw_load_crypto_func(void) -{ - HINSTANCE dll; - - /* MinGW does not yet have full CryptoAPI support, so load the needed - * function here. */ - - if (CryptImportPublicKeyInfo) - return 0; - - dll = LoadLibrary("crypt32"); - if (dll == NULL) { - wpa_printf(MSG_DEBUG, "CryptoAPI: Could not load crypt32 " - "library"); - return -1; - } - - CryptImportPublicKeyInfo = GetProcAddress( - dll, "CryptImportPublicKeyInfo"); - if (CryptImportPublicKeyInfo == NULL) { - wpa_printf(MSG_DEBUG, "CryptoAPI: Could not get " - "CryptImportPublicKeyInfo() address from " - "crypt32 library"); - return -1; - } - - return 0; -} - -#else /* __MINGW32_VERSION */ - -static int mingw_load_crypto_func(void) -{ - return 0; -} - -#endif /* __MINGW32_VERSION */ - - -static void cryptoapi_report_error(const char *msg) -{ - char *s, *pos; - DWORD err = GetLastError(); - - if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM, - NULL, err, 0, (LPTSTR) &s, 0, NULL) == 0) { - wpa_printf(MSG_DEBUG, "CryptoAPI: %s: %d", msg, (int) err); - } - - pos = s; - while (*pos) { - if (*pos == '\n' || *pos == '\r') { - *pos = '\0'; - break; - } - pos++; - } - - wpa_printf(MSG_DEBUG, "CryptoAPI: %s: %d: (%s)", msg, (int) err, s); - LocalFree(s); -} - - -int cryptoapi_hash_vector(ALG_ID alg, size_t hash_len, size_t num_elem, - const u8 *addr[], const size_t *len, u8 *mac) -{ - HCRYPTPROV prov; - HCRYPTHASH hash; - size_t i; - DWORD hlen; - int ret = 0; - - if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, 0)) { - cryptoapi_report_error("CryptAcquireContext"); - return -1; - } - - if (!CryptCreateHash(prov, alg, 0, 0, &hash)) { - cryptoapi_report_error("CryptCreateHash"); - CryptReleaseContext(prov, 0); - return -1; - } - - for (i = 0; i < num_elem; i++) { - if (!CryptHashData(hash, (BYTE *) addr[i], len[i], 0)) { - cryptoapi_report_error("CryptHashData"); - CryptDestroyHash(hash); - CryptReleaseContext(prov, 0); - } - } - - hlen = hash_len; - if (!CryptGetHashParam(hash, HP_HASHVAL, mac, &hlen, 0)) { - cryptoapi_report_error("CryptGetHashParam"); - ret = -1; - } - - CryptDestroyHash(hash); - CryptReleaseContext(prov, 0); - - return ret; -} - - -int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) -{ - return cryptoapi_hash_vector(CALG_MD4, 16, num_elem, addr, len, mac); -} - - -void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) -{ - u8 next, tmp; - int i; - HCRYPTPROV prov; - HCRYPTKEY ckey; - DWORD dlen; - struct { - BLOBHEADER hdr; - DWORD len; - BYTE key[8]; - } key_blob; - DWORD mode = CRYPT_MODE_ECB; - - key_blob.hdr.bType = PLAINTEXTKEYBLOB; - key_blob.hdr.bVersion = CUR_BLOB_VERSION; - key_blob.hdr.reserved = 0; - key_blob.hdr.aiKeyAlg = CALG_DES; - key_blob.len = 8; - - /* Add parity bits to the key */ - next = 0; - for (i = 0; i < 7; i++) { - tmp = key[i]; - key_blob.key[i] = (tmp >> i) | next | 1; - next = tmp << (7 - i); - } - key_blob.key[i] = next | 1; - - if (!CryptAcquireContext(&prov, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, - CRYPT_VERIFYCONTEXT)) { - wpa_printf(MSG_DEBUG, "CryptoAPI: CryptAcquireContext failed: " - "%d", (int) GetLastError()); - return; - } - - if (!CryptImportKey(prov, (BYTE *) &key_blob, sizeof(key_blob), 0, 0, - &ckey)) { - wpa_printf(MSG_DEBUG, "CryptoAPI: CryptImportKey failed: %d", - (int) GetLastError()); - CryptReleaseContext(prov, 0); - return; - } - - if (!CryptSetKeyParam(ckey, KP_MODE, (BYTE *) &mode, 0)) { - wpa_printf(MSG_DEBUG, "CryptoAPI: CryptSetKeyParam(KP_MODE) " - "failed: %d", (int) GetLastError()); - CryptDestroyKey(ckey); - CryptReleaseContext(prov, 0); - return; - } - - os_memcpy(cypher, clear, 8); - dlen = 8; - if (!CryptEncrypt(ckey, 0, FALSE, 0, cypher, &dlen, 8)) { - wpa_printf(MSG_DEBUG, "CryptoAPI: CryptEncrypt failed: %d", - (int) GetLastError()); - os_memset(cypher, 0, 8); - } - - CryptDestroyKey(ckey); - CryptReleaseContext(prov, 0); -} - - -int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) -{ - return cryptoapi_hash_vector(CALG_MD5, 16, num_elem, addr, len, mac); -} - - -int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) -{ - return cryptoapi_hash_vector(CALG_SHA, 20, num_elem, addr, len, mac); -} - - -struct aes_context { - HCRYPTPROV prov; - HCRYPTKEY ckey; -}; - - -void * aes_encrypt_init(const u8 *key, size_t len) -{ - struct aes_context *akey; - struct { - BLOBHEADER hdr; - DWORD len; - BYTE key[16]; - } key_blob; - DWORD mode = CRYPT_MODE_ECB; - - if (len != 16) - return NULL; - - key_blob.hdr.bType = PLAINTEXTKEYBLOB; - key_blob.hdr.bVersion = CUR_BLOB_VERSION; - key_blob.hdr.reserved = 0; - key_blob.hdr.aiKeyAlg = CALG_AES_128; - key_blob.len = len; - os_memcpy(key_blob.key, key, len); - - akey = os_zalloc(sizeof(*akey)); - if (akey == NULL) - return NULL; - - if (!CryptAcquireContext(&akey->prov, NULL, - MS_ENH_RSA_AES_PROV, PROV_RSA_AES, - CRYPT_VERIFYCONTEXT)) { - wpa_printf(MSG_DEBUG, "CryptoAPI: CryptAcquireContext failed: " - "%d", (int) GetLastError()); - os_free(akey); - return NULL; - } - - if (!CryptImportKey(akey->prov, (BYTE *) &key_blob, sizeof(key_blob), - 0, 0, &akey->ckey)) { - wpa_printf(MSG_DEBUG, "CryptoAPI: CryptImportKey failed: %d", - (int) GetLastError()); - CryptReleaseContext(akey->prov, 0); - os_free(akey); - return NULL; - } - - if (!CryptSetKeyParam(akey->ckey, KP_MODE, (BYTE *) &mode, 0)) { - wpa_printf(MSG_DEBUG, "CryptoAPI: CryptSetKeyParam(KP_MODE) " - "failed: %d", (int) GetLastError()); - CryptDestroyKey(akey->ckey); - CryptReleaseContext(akey->prov, 0); - os_free(akey); - return NULL; - } - - return akey; -} - - -void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) -{ - struct aes_context *akey = ctx; - DWORD dlen; - - os_memcpy(crypt, plain, 16); - dlen = 16; - if (!CryptEncrypt(akey->ckey, 0, FALSE, 0, crypt, &dlen, 16)) { - wpa_printf(MSG_DEBUG, "CryptoAPI: CryptEncrypt failed: %d", - (int) GetLastError()); - os_memset(crypt, 0, 16); - } -} - - -void aes_encrypt_deinit(void *ctx) -{ - struct aes_context *akey = ctx; - if (akey) { - CryptDestroyKey(akey->ckey); - CryptReleaseContext(akey->prov, 0); - os_free(akey); - } -} - - -void * aes_decrypt_init(const u8 *key, size_t len) -{ - return aes_encrypt_init(key, len); -} - - -void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) -{ - struct aes_context *akey = ctx; - DWORD dlen; - - os_memcpy(plain, crypt, 16); - dlen = 16; - - if (!CryptDecrypt(akey->ckey, 0, FALSE, 0, plain, &dlen)) { - wpa_printf(MSG_DEBUG, "CryptoAPI: CryptDecrypt failed: %d", - (int) GetLastError()); - } -} - - -void aes_decrypt_deinit(void *ctx) -{ - aes_encrypt_deinit(ctx); -} - - -struct crypto_hash { - enum crypto_hash_alg alg; - int error; - HCRYPTPROV prov; - HCRYPTHASH hash; - HCRYPTKEY key; -}; - -struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, - size_t key_len) -{ - struct crypto_hash *ctx; - ALG_ID calg; - struct { - BLOBHEADER hdr; - DWORD len; - BYTE key[32]; - } key_blob; - - os_memset(&key_blob, 0, sizeof(key_blob)); - switch (alg) { - case CRYPTO_HASH_ALG_MD5: - calg = CALG_MD5; - break; - case CRYPTO_HASH_ALG_SHA1: - calg = CALG_SHA; - break; - case CRYPTO_HASH_ALG_HMAC_MD5: - case CRYPTO_HASH_ALG_HMAC_SHA1: - calg = CALG_HMAC; - key_blob.hdr.bType = PLAINTEXTKEYBLOB; - key_blob.hdr.bVersion = CUR_BLOB_VERSION; - key_blob.hdr.reserved = 0; - /* - * Note: RC2 is not really used, but that can be used to - * import HMAC keys of up to 16 byte long. - * CRYPT_IPSEC_HMAC_KEY flag for CryptImportKey() is needed to - * be able to import longer keys (HMAC-SHA1 uses 20-byte key). - */ - key_blob.hdr.aiKeyAlg = CALG_RC2; - key_blob.len = key_len; - if (key_len > sizeof(key_blob.key)) - return NULL; - os_memcpy(key_blob.key, key, key_len); - break; - default: - return NULL; - } - - ctx = os_zalloc(sizeof(*ctx)); - if (ctx == NULL) - return NULL; - - ctx->alg = alg; - - if (!CryptAcquireContext(&ctx->prov, NULL, NULL, PROV_RSA_FULL, 0)) { - cryptoapi_report_error("CryptAcquireContext"); - os_free(ctx); - return NULL; - } - - if (calg == CALG_HMAC) { -#ifndef CRYPT_IPSEC_HMAC_KEY -#define CRYPT_IPSEC_HMAC_KEY 0x00000100 -#endif - if (!CryptImportKey(ctx->prov, (BYTE *) &key_blob, - sizeof(key_blob), 0, CRYPT_IPSEC_HMAC_KEY, - &ctx->key)) { - cryptoapi_report_error("CryptImportKey"); - CryptReleaseContext(ctx->prov, 0); - os_free(ctx); - return NULL; - } - } - - if (!CryptCreateHash(ctx->prov, calg, ctx->key, 0, &ctx->hash)) { - cryptoapi_report_error("CryptCreateHash"); - CryptReleaseContext(ctx->prov, 0); - os_free(ctx); - return NULL; - } - - if (calg == CALG_HMAC) { - HMAC_INFO info; - os_memset(&info, 0, sizeof(info)); - switch (alg) { - case CRYPTO_HASH_ALG_HMAC_MD5: - info.HashAlgid = CALG_MD5; - break; - case CRYPTO_HASH_ALG_HMAC_SHA1: - info.HashAlgid = CALG_SHA; - break; - default: - /* unreachable */ - break; - } - - if (!CryptSetHashParam(ctx->hash, HP_HMAC_INFO, (BYTE *) &info, - 0)) { - cryptoapi_report_error("CryptSetHashParam"); - CryptDestroyHash(ctx->hash); - CryptReleaseContext(ctx->prov, 0); - os_free(ctx); - return NULL; - } - } - - return ctx; -} - - -void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len) -{ - if (ctx == NULL || ctx->error) - return; - - if (!CryptHashData(ctx->hash, (BYTE *) data, len, 0)) { - cryptoapi_report_error("CryptHashData"); - ctx->error = 1; - } -} - - -int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) -{ - int ret = 0; - DWORD hlen; - - if (ctx == NULL) - return -2; - - if (mac == NULL || len == NULL) - goto done; - - if (ctx->error) { - ret = -2; - goto done; - } - - hlen = *len; - if (!CryptGetHashParam(ctx->hash, HP_HASHVAL, mac, &hlen, 0)) { - cryptoapi_report_error("CryptGetHashParam"); - ret = -2; - } - *len = hlen; - -done: - if (ctx->alg == CRYPTO_HASH_ALG_HMAC_SHA1 || - ctx->alg == CRYPTO_HASH_ALG_HMAC_MD5) - CryptDestroyKey(ctx->key); - - os_free(ctx); - - return ret; -} - - -struct crypto_cipher { - HCRYPTPROV prov; - HCRYPTKEY key; -}; - - -struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, - const u8 *iv, const u8 *key, - size_t key_len) -{ - struct crypto_cipher *ctx; - struct { - BLOBHEADER hdr; - DWORD len; - BYTE key[32]; - } key_blob; - DWORD mode = CRYPT_MODE_CBC; - - key_blob.hdr.bType = PLAINTEXTKEYBLOB; - key_blob.hdr.bVersion = CUR_BLOB_VERSION; - key_blob.hdr.reserved = 0; - key_blob.len = key_len; - if (key_len > sizeof(key_blob.key)) - return NULL; - os_memcpy(key_blob.key, key, key_len); - - switch (alg) { - case CRYPTO_CIPHER_ALG_AES: - if (key_len == 32) - key_blob.hdr.aiKeyAlg = CALG_AES_256; - else if (key_len == 24) - key_blob.hdr.aiKeyAlg = CALG_AES_192; - else - key_blob.hdr.aiKeyAlg = CALG_AES_128; - break; - case CRYPTO_CIPHER_ALG_3DES: - key_blob.hdr.aiKeyAlg = CALG_3DES; - break; - case CRYPTO_CIPHER_ALG_DES: - key_blob.hdr.aiKeyAlg = CALG_DES; - break; - case CRYPTO_CIPHER_ALG_RC2: - key_blob.hdr.aiKeyAlg = CALG_RC2; - break; - case CRYPTO_CIPHER_ALG_RC4: - key_blob.hdr.aiKeyAlg = CALG_RC4; - break; - default: - return NULL; - } - - ctx = os_zalloc(sizeof(*ctx)); - if (ctx == NULL) - return NULL; - - if (!CryptAcquireContext(&ctx->prov, NULL, MS_ENH_RSA_AES_PROV, - PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) { - cryptoapi_report_error("CryptAcquireContext"); - goto fail1; - } - - if (!CryptImportKey(ctx->prov, (BYTE *) &key_blob, - sizeof(key_blob), 0, 0, &ctx->key)) { - cryptoapi_report_error("CryptImportKey"); - goto fail2; - } - - if (!CryptSetKeyParam(ctx->key, KP_MODE, (BYTE *) &mode, 0)) { - cryptoapi_report_error("CryptSetKeyParam(KP_MODE)"); - goto fail3; - } - - if (iv && !CryptSetKeyParam(ctx->key, KP_IV, (BYTE *) iv, 0)) { - cryptoapi_report_error("CryptSetKeyParam(KP_IV)"); - goto fail3; - } - - return ctx; - -fail3: - CryptDestroyKey(ctx->key); -fail2: - CryptReleaseContext(ctx->prov, 0); -fail1: - os_free(ctx); - return NULL; -} - - -int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, - u8 *crypt, size_t len) -{ - DWORD dlen; - - os_memcpy(crypt, plain, len); - dlen = len; - if (!CryptEncrypt(ctx->key, 0, FALSE, 0, crypt, &dlen, len)) { - cryptoapi_report_error("CryptEncrypt"); - os_memset(crypt, 0, len); - return -1; - } - - return 0; -} - - -int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, - u8 *plain, size_t len) -{ - DWORD dlen; - - os_memcpy(plain, crypt, len); - dlen = len; - if (!CryptDecrypt(ctx->key, 0, FALSE, 0, plain, &dlen)) { - cryptoapi_report_error("CryptDecrypt"); - return -1; - } - - return 0; -} - - -void crypto_cipher_deinit(struct crypto_cipher *ctx) -{ - CryptDestroyKey(ctx->key); - CryptReleaseContext(ctx->prov, 0); - os_free(ctx); -} - - -struct crypto_public_key { - HCRYPTPROV prov; - HCRYPTKEY rsa; -}; - -struct crypto_private_key { - HCRYPTPROV prov; - HCRYPTKEY rsa; -}; - - -struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len) -{ - /* Use crypto_public_key_from_cert() instead. */ - return NULL; -} - - -struct crypto_private_key * crypto_private_key_import(const u8 *key, - size_t len, - const char *passwd) -{ - /* TODO */ - return NULL; -} - - -struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf, - size_t len) -{ - struct crypto_public_key *pk; - PCCERT_CONTEXT cc; - - pk = os_zalloc(sizeof(*pk)); - if (pk == NULL) - return NULL; - - cc = CertCreateCertificateContext(X509_ASN_ENCODING | - PKCS_7_ASN_ENCODING, buf, len); - if (!cc) { - cryptoapi_report_error("CryptCreateCertificateContext"); - os_free(pk); - return NULL; - } - - if (!CryptAcquireContext(&pk->prov, NULL, MS_DEF_PROV, PROV_RSA_FULL, - 0)) { - cryptoapi_report_error("CryptAcquireContext"); - os_free(pk); - CertFreeCertificateContext(cc); - return NULL; - } - - if (!CryptImportPublicKeyInfo(pk->prov, X509_ASN_ENCODING | - PKCS_7_ASN_ENCODING, - &cc->pCertInfo->SubjectPublicKeyInfo, - &pk->rsa)) { - cryptoapi_report_error("CryptImportPublicKeyInfo"); - CryptReleaseContext(pk->prov, 0); - os_free(pk); - CertFreeCertificateContext(cc); - return NULL; - } - - CertFreeCertificateContext(cc); - - return pk; -} - - -int crypto_public_key_encrypt_pkcs1_v15(struct crypto_public_key *key, - const u8 *in, size_t inlen, - u8 *out, size_t *outlen) -{ - DWORD clen; - u8 *tmp; - size_t i; - - if (*outlen < inlen) - return -1; - tmp = malloc(*outlen); - if (tmp == NULL) - return -1; - - os_memcpy(tmp, in, inlen); - clen = inlen; - if (!CryptEncrypt(key->rsa, 0, TRUE, 0, tmp, &clen, *outlen)) { - wpa_printf(MSG_DEBUG, "CryptoAPI: Failed to encrypt using " - "public key: %d", (int) GetLastError()); - os_free(tmp); - return -1; - } - - *outlen = clen; - - /* Reverse the output */ - for (i = 0; i < *outlen; i++) - out[i] = tmp[*outlen - 1 - i]; - - os_free(tmp); - - return 0; -} - - -int crypto_private_key_sign_pkcs1(struct crypto_private_key *key, - const u8 *in, size_t inlen, - u8 *out, size_t *outlen) -{ - /* TODO */ - return -1; -} - - -void crypto_public_key_free(struct crypto_public_key *key) -{ - if (key) { - CryptDestroyKey(key->rsa); - CryptReleaseContext(key->prov, 0); - os_free(key); - } -} - - -void crypto_private_key_free(struct crypto_private_key *key) -{ - if (key) { - CryptDestroyKey(key->rsa); - CryptReleaseContext(key->prov, 0); - os_free(key); - } -} - - -int crypto_global_init(void) -{ - return mingw_load_crypto_func(); -} - - -void crypto_global_deinit(void) -{ -} - - -int crypto_mod_exp(const u8 *base, size_t base_len, - const u8 *power, size_t power_len, - const u8 *modulus, size_t modulus_len, - u8 *result, size_t *result_len) -{ - /* TODO */ - return -1; -} diff --git a/contrib/wpa/src/crypto/crypto_module_tests.c b/contrib/wpa/src/crypto/crypto_module_tests.c index 7137c27..581005d 100644 --- a/contrib/wpa/src/crypto/crypto_module_tests.c +++ b/contrib/wpa/src/crypto/crypto_module_tests.c @@ -161,7 +161,7 @@ struct omac1_test_vector { u8 tag[16]; }; -static struct omac1_test_vector omac1_test_vectors[] = +static const struct omac1_test_vector omac1_test_vectors[] = { { { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, @@ -210,7 +210,8 @@ static struct omac1_test_vector omac1_test_vectors[] = }; -static int test_omac1_vector(struct omac1_test_vector *tv, unsigned int i) +static int test_omac1_vector(const struct omac1_test_vector *tv, + unsigned int i) { u8 key[] = { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, @@ -515,6 +516,7 @@ static int test_key_wrap(void) 0xAE, 0xF3, 0x4B, 0xD8, 0xFB, 0x5A, 0x7B, 0x82, 0x9D, 0x3E, 0x86, 0x23, 0x71, 0xD2, 0xCF, 0xE5 }; +#ifndef CONFIG_BORINGSSL /* RFC 3394 - Test vector 4.2 */ u8 kek42[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, @@ -530,6 +532,7 @@ static int test_key_wrap(void) 0xF9, 0x2B, 0x5B, 0x97, 0xC0, 0x50, 0xAE, 0xD2, 0x46, 0x8A, 0xB8, 0xA1, 0x7A, 0xD8, 0x4E, 0x5D }; +#endif /* CONFIG_BORINGSSL */ /* RFC 3394 - Test vector 4.3 */ u8 kek43[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, @@ -546,6 +549,7 @@ static int test_key_wrap(void) 0x63, 0xE9, 0x77, 0x79, 0x05, 0x81, 0x8A, 0x2A, 0x93, 0xC8, 0x19, 0x1E, 0x7D, 0x6E, 0x8A, 0xE7, }; +#ifndef CONFIG_BORINGSSL /* RFC 3394 - Test vector 4.4 */ u8 kek44[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, @@ -563,6 +567,7 @@ static int test_key_wrap(void) 0xE1, 0xC6, 0xC7, 0xDD, 0xEE, 0x72, 0x5A, 0x93, 0x6B, 0xA8, 0x14, 0x91, 0x5C, 0x67, 0x62, 0xD2 }; +#endif /* CONFIG_BORINGSSL */ /* RFC 3394 - Test vector 4.5 */ u8 kek45[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, @@ -623,6 +628,7 @@ static int test_key_wrap(void) ret++; } +#ifndef CONFIG_BORINGSSL wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.2"); if (aes_wrap(kek42, sizeof(kek42), sizeof(plain42) / 8, plain42, result)) { @@ -642,6 +648,7 @@ static int test_key_wrap(void) wpa_printf(MSG_ERROR, "AES-UNWRAP-192 failed"); ret++; } +#endif /* CONFIG_BORINGSSL */ wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.3"); if (aes_wrap(kek43, sizeof(kek43), sizeof(plain43) / 8, plain43, @@ -663,6 +670,7 @@ static int test_key_wrap(void) ret++; } +#ifndef CONFIG_BORINGSSL wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.4"); if (aes_wrap(kek44, sizeof(kek44), sizeof(plain44) / 8, plain44, result)) { @@ -682,6 +690,7 @@ static int test_key_wrap(void) wpa_printf(MSG_ERROR, "AES-UNWRAP-192 failed"); ret++; } +#endif /* CONFIG_BORINGSSL */ wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.5"); if (aes_wrap(kek45, sizeof(kek45), sizeof(plain45) / 8, plain45, @@ -732,6 +741,7 @@ static int test_key_wrap(void) static int test_md5(void) { +#ifndef CONFIG_FIPS struct { char *data; char *hash; @@ -810,6 +820,10 @@ static int test_md5(void) wpa_printf(MSG_INFO, "MD5 test cases passed"); return errors; +#else /* CONFIG_FIPS */ + wpa_printf(MSG_INFO, "MD5 test cases skipped due to CONFIG_FIPS"); + return 0; +#endif /* CONFIG_FIPS */ } @@ -841,6 +855,7 @@ static int test_eap_fast(void) 0x38, 0x4B, 0x7A, 0x85, 0xBE, 0x16, 0x4D, 0x27, 0x33, 0xD5, 0x24, 0x79, 0x87, 0xB1, 0xC5, 0xA2 }; +#ifndef CONFIG_FIPS const u8 key_block[] = { 0x59, 0x59, 0xBE, 0x8E, 0x41, 0x3A, 0x77, 0x74, 0x8B, 0xB2, 0xE5, 0xD3, 0x60, 0xAC, 0x4D, 0x35, @@ -857,6 +872,7 @@ static int test_eap_fast(void) 0x64, 0xC1, 0xC8, 0x0C, 0x96, 0x44, 0x09, 0x98, 0xFF, 0x92, 0xA8, 0xB4, 0xC6, 0x42, 0x28, 0x71 }; +#endif /* CONFIG_FIPS */ const u8 sks[] = { 0xD6, 0x4B, 0x7D, 0x72, 0x17, 0x59, 0x28, 0x05, 0xAF, 0xF9, 0xB7, 0xFF, 0x66, 0x6D, 0xA1, 0x96, @@ -931,6 +947,7 @@ static int test_eap_fast(void) errors++; } +#ifndef CONFIG_FIPS wpa_printf(MSG_INFO, "- PRF (TLS, SHA1/MD5) test case / key_block"); if (tls_prf_sha1_md5(master_secret, sizeof(master_secret), "key expansion", seed, sizeof(seed), @@ -939,6 +956,7 @@ static int test_eap_fast(void) wpa_printf(MSG_INFO, "PRF test - FAILED!"); errors++; } +#endif /* CONFIG_FIPS */ wpa_printf(MSG_INFO, "- T-PRF (SHA1) test case / IMCK"); if (sha1_t_prf(sks, sizeof(sks), "Inner Methods Compound Keys", @@ -983,14 +1001,14 @@ static int test_eap_fast(void) } -static u8 key0[] = +static const u8 key0[] = { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b }; -static u8 data0[] = "Hi There"; -static u8 prf0[] = +static const u8 data0[] = "Hi There"; +static const u8 prf0[] = { 0xbc, 0xd4, 0xc6, 0x50, 0xb3, 0x0b, 0x96, 0x84, 0x95, 0x18, 0x29, 0xe0, 0xd7, 0x5f, 0x9d, 0x54, @@ -1002,9 +1020,9 @@ static u8 prf0[] = 0xdb, 0x83, 0x73, 0x69, 0x83, 0x56, 0xcf, 0x5a }; -static u8 key1[] = "Jefe"; -static u8 data1[] = "what do ya want for nothing?"; -static u8 prf1[] = +static const u8 key1[] = "Jefe"; +static const u8 data1[] = "what do ya want for nothing?"; +static const u8 prf1[] = { 0x51, 0xf4, 0xde, 0x5b, 0x33, 0xf2, 0x49, 0xad, 0xf8, 0x1a, 0xeb, 0x71, 0x3a, 0x3c, 0x20, 0xf4, @@ -1017,13 +1035,13 @@ static u8 prf1[] = }; -static u8 key2[] = +static const u8 key2[] = { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa }; -static u8 data2[] = +static const u8 data2[] = { 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, @@ -1033,7 +1051,7 @@ static u8 data2[] = 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd }; -static u8 prf2[] = +static const u8 prf2[] = { 0xe1, 0xac, 0x54, 0x6e, 0xc4, 0xcb, 0x63, 0x6f, 0x99, 0x76, 0x48, 0x7b, 0xe5, 0xc8, 0x6b, 0xe1, @@ -1052,7 +1070,7 @@ struct passphrase_test { char psk[32]; }; -static struct passphrase_test passphrase_tests[] = +static const struct passphrase_test passphrase_tests[] = { { "password", @@ -1097,7 +1115,7 @@ struct rfc6070_test { size_t dk_len; }; -static struct rfc6070_test rfc6070_tests[] = +static const struct rfc6070_test rfc6070_tests[] = { { "password", @@ -1214,7 +1232,7 @@ static int test_sha1(void) wpa_printf(MSG_INFO, "PBKDF2-SHA1 Passphrase test cases:"); for (i = 0; i < NUM_PASSPHRASE_TESTS; i++) { u8 psk[32]; - struct passphrase_test *test = &passphrase_tests[i]; + const struct passphrase_test *test = &passphrase_tests[i]; if (pbkdf2_sha1(test->passphrase, (const u8 *) test->ssid, strlen(test->ssid), @@ -1230,7 +1248,7 @@ static int test_sha1(void) wpa_printf(MSG_INFO, "PBKDF2-SHA1 test cases (RFC 6070):"); for (i = 0; i < NUM_RFC6070_TESTS; i++) { u8 dk[25]; - struct rfc6070_test *test = &rfc6070_tests[i]; + const struct rfc6070_test *test = &rfc6070_tests[i]; if (pbkdf2_sha1(test->p, (const u8 *) test->s, strlen(test->s), test->c, dk, test->dk_len) == 0 && @@ -1248,7 +1266,7 @@ static int test_sha1(void) } -struct { +const struct { char *data; u8 hash[32]; } tests[] = { @@ -1272,7 +1290,7 @@ struct { } }; -struct hmac_test { +const struct hmac_test { u8 key[80]; size_t key_len; u8 data[128]; @@ -1513,7 +1531,7 @@ static int test_sha256(void) } for (i = 0; i < ARRAY_SIZE(hmac_tests); i++) { - struct hmac_test *t = &hmac_tests[i]; + const struct hmac_test *t = &hmac_tests[i]; wpa_printf(MSG_INFO, "HMAC-SHA256 test case %d:", i + 1); @@ -1563,6 +1581,7 @@ static int test_sha256(void) static int test_ms_funcs(void) { +#ifndef CONFIG_FIPS /* Test vector from RFC2759 example */ char *username = "User"; char *password = "clientPass"; @@ -1655,6 +1674,10 @@ static int test_ms_funcs(void) wpa_printf(MSG_INFO, "ms_funcs test cases passed"); return errors; +#else /* CONFIG_FIPS */ + wpa_printf(MSG_INFO, "ms_funcs test cases skipped due to CONFIG_FIPS"); + return 0; +#endif /* CONFIG_FIPS */ } diff --git a/contrib/wpa/src/crypto/crypto_openssl.c b/contrib/wpa/src/crypto/crypto_openssl.c index f158ef4..6cff75c 100644 --- a/contrib/wpa/src/crypto/crypto_openssl.c +++ b/contrib/wpa/src/crypto/crypto_openssl.c @@ -93,10 +93,12 @@ static int openssl_digest_vector(const EVP_MD *type, size_t num_elem, } +#ifndef CONFIG_FIPS int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { return openssl_digest_vector(EVP_md4(), num_elem, addr, len, mac); } +#endif /* CONFIG_FIPS */ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) @@ -120,6 +122,7 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) } +#ifndef CONFIG_NO_RC4 int rc4_skip(const u8 *key, size_t keylen, size_t skip, u8 *data, size_t data_len) { @@ -155,12 +158,15 @@ out: return res; #endif /* OPENSSL_NO_RC4 */ } +#endif /* CONFIG_NO_RC4 */ +#ifndef CONFIG_FIPS int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { return openssl_digest_vector(EVP_md5(), num_elem, addr, len, mac); } +#endif /* CONFIG_FIPS */ int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) @@ -297,6 +303,9 @@ void aes_decrypt_deinit(void *ctx) } +#ifndef CONFIG_FIPS +#ifndef CONFIG_OPENSSL_INTERNAL_AES_WRAP + int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher) { AES_KEY actx; @@ -323,6 +332,59 @@ int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher, return res <= 0 ? -1 : 0; } +#endif /* CONFIG_OPENSSL_INTERNAL_AES_WRAP */ +#endif /* CONFIG_FIPS */ + + +int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) +{ + EVP_CIPHER_CTX ctx; + int clen, len; + u8 buf[16]; + + EVP_CIPHER_CTX_init(&ctx); + if (EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv) != 1) + return -1; + EVP_CIPHER_CTX_set_padding(&ctx, 0); + + 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); + + return 0; +} + + +int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) +{ + EVP_CIPHER_CTX ctx; + int plen, len; + u8 buf[16]; + + EVP_CIPHER_CTX_init(&ctx); + if (EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv) != 1) + return -1; + EVP_CIPHER_CTX_set_padding(&ctx, 0); + + 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); + + return 0; +} + int crypto_mod_exp(const u8 *base, size_t base_len, const u8 *power, size_t power_len, @@ -380,11 +442,13 @@ struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, return NULL; switch (alg) { +#ifndef CONFIG_NO_RC4 #ifndef OPENSSL_NO_RC4 case CRYPTO_CIPHER_ALG_RC4: cipher = EVP_rc4(); break; #endif /* OPENSSL_NO_RC4 */ +#endif /* CONFIG_NO_RC4 */ #ifndef OPENSSL_NO_AES case CRYPTO_CIPHER_ALG_AES: switch (key_len) { @@ -1057,6 +1121,42 @@ int crypto_bignum_is_one(const struct crypto_bignum *a) } +int crypto_bignum_legendre(const struct crypto_bignum *a, + const struct crypto_bignum *p) +{ + BN_CTX *bnctx; + BIGNUM *exp = NULL, *tmp = NULL; + int res = -2; + + bnctx = BN_CTX_new(); + if (bnctx == NULL) + return -2; + + exp = BN_new(); + tmp = BN_new(); + if (!exp || !tmp || + /* exp = (p-1) / 2 */ + !BN_sub(exp, (const BIGNUM *) p, BN_value_one()) || + !BN_rshift1(exp, exp) || + !BN_mod_exp(tmp, (const BIGNUM *) a, exp, (const BIGNUM *) p, + bnctx)) + goto fail; + + if (BN_is_word(tmp, 1)) + res = 1; + else if (BN_is_zero(tmp)) + res = 0; + else + res = -1; + +fail: + BN_clear_free(tmp); + BN_clear_free(exp); + BN_CTX_free(bnctx); + return res; +} + + #ifdef CONFIG_ECC struct crypto_ec { @@ -1064,6 +1164,8 @@ struct crypto_ec { BN_CTX *bnctx; BIGNUM *prime; BIGNUM *order; + BIGNUM *a; + BIGNUM *b; }; struct crypto_ec * crypto_ec_init(int group) @@ -1088,6 +1190,26 @@ struct crypto_ec * crypto_ec_init(int group) 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: return NULL; } @@ -1100,9 +1222,11 @@ struct crypto_ec * crypto_ec_init(int group) e->group = EC_GROUP_new_by_curve_name(nid); e->prime = BN_new(); e->order = BN_new(); + e->a = BN_new(); + e->b = BN_new(); if (e->group == NULL || e->bnctx == NULL || e->prime == NULL || - e->order == NULL || - !EC_GROUP_get_curve_GFp(e->group, e->prime, NULL, NULL, e->bnctx) || + e->order == NULL || e->a == NULL || e->b == NULL || + !EC_GROUP_get_curve_GFp(e->group, e->prime, e->a, e->b, e->bnctx) || !EC_GROUP_get_order(e->group, e->order, e->bnctx)) { crypto_ec_deinit(e); e = NULL; @@ -1116,6 +1240,8 @@ void crypto_ec_deinit(struct crypto_ec *e) { if (e == NULL) return; + BN_clear_free(e->b); + BN_clear_free(e->a); BN_clear_free(e->order); BN_clear_free(e->prime); EC_GROUP_free(e->group); @@ -1263,6 +1389,33 @@ int crypto_ec_point_solve_y_coord(struct crypto_ec *e, } +struct crypto_bignum * +crypto_ec_point_compute_y_sqr(struct crypto_ec *e, + const struct crypto_bignum *x) +{ + BIGNUM *tmp, *tmp2, *y_sqr = NULL; + + tmp = BN_new(); + tmp2 = BN_new(); + + /* y^2 = x^3 + ax + b */ + if (tmp && tmp2 && + BN_mod_sqr(tmp, (const BIGNUM *) x, e->prime, e->bnctx) && + BN_mod_mul(tmp, tmp, (const BIGNUM *) x, e->prime, e->bnctx) && + BN_mod_mul(tmp2, e->a, (const BIGNUM *) x, e->prime, e->bnctx) && + BN_mod_add_quick(tmp2, tmp2, tmp, e->prime) && + BN_mod_add_quick(tmp2, tmp2, e->b, e->prime)) { + y_sqr = tmp2; + tmp2 = NULL; + } + + BN_clear_free(tmp); + BN_clear_free(tmp2); + + return (struct crypto_bignum *) y_sqr; +} + + int crypto_ec_point_is_at_infinity(struct crypto_ec *e, const struct crypto_ec_point *p) { @@ -1273,7 +1426,17 @@ int crypto_ec_point_is_at_infinity(struct crypto_ec *e, int crypto_ec_point_is_on_curve(struct crypto_ec *e, const struct crypto_ec_point *p) { - return EC_POINT_is_on_curve(e->group, (const EC_POINT *) p, e->bnctx); + return EC_POINT_is_on_curve(e->group, (const EC_POINT *) p, + e->bnctx) == 1; +} + + +int crypto_ec_point_cmp(const struct crypto_ec *e, + const struct crypto_ec_point *a, + const struct crypto_ec_point *b) +{ + return EC_POINT_cmp(e->group, (const EC_POINT *) a, + (const EC_POINT *) b, e->bnctx); } #endif /* CONFIG_ECC */ diff --git a/contrib/wpa/src/crypto/dh_groups.c b/contrib/wpa/src/crypto/dh_groups.c index d3b2631..3aeb2bb 100644 --- a/contrib/wpa/src/crypto/dh_groups.c +++ b/contrib/wpa/src/crypto/dh_groups.c @@ -1153,7 +1153,7 @@ dh_group ## id ## _prime, sizeof(dh_group ## id ## _prime), \ dh_group ## id ## _order, sizeof(dh_group ## id ## _order), safe } -static struct dh_group dh_groups[] = { +static const struct dh_group dh_groups[] = { DH_GROUP(5, 1), #ifdef ALL_DH_GROUPS DH_GROUP(1, 1), diff --git a/contrib/wpa/src/crypto/fips_prf_openssl.c b/contrib/wpa/src/crypto/fips_prf_openssl.c index d69ecea..fb03efc 100644 --- a/contrib/wpa/src/crypto/fips_prf_openssl.c +++ b/contrib/wpa/src/crypto/fips_prf_openssl.c @@ -13,13 +13,21 @@ #include "crypto.h" -static void sha1_transform(u8 *state, const u8 data[64]) +static void sha1_transform(u32 *state, const u8 data[64]) { SHA_CTX context; os_memset(&context, 0, sizeof(context)); - os_memcpy(&context.h0, state, 5 * 4); + context.h0 = state[0]; + context.h1 = state[1]; + context.h2 = state[2]; + context.h3 = state[3]; + context.h4 = state[4]; SHA1_Transform(&context, data); - os_memcpy(state, &context.h0, 5 * 4); + state[0] = context.h0; + state[1] = context.h1; + state[2] = context.h2; + state[3] = context.h3; + state[4] = context.h4; } @@ -53,7 +61,7 @@ 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((u8 *) _t, xkey); + 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]); diff --git a/contrib/wpa/src/crypto/ms_funcs.c b/contrib/wpa/src/crypto/ms_funcs.c index 49a5c1c..053d203 100644 --- a/contrib/wpa/src/crypto/ms_funcs.c +++ b/contrib/wpa/src/crypto/ms_funcs.c @@ -78,9 +78,8 @@ static int utf8_to_ucs2(const u8 *utf8_string, size_t utf8_string_len, * @challenge: 8-octet Challenge (OUT) * Returns: 0 on success, -1 on failure */ -static int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge, - const u8 *username, size_t username_len, - u8 *challenge) +int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge, + const u8 *username, size_t username_len, u8 *challenge) { u8 hash[SHA1_MAC_LEN]; const unsigned char *addr[3]; @@ -175,9 +174,8 @@ int generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge, u8 password_hash[16]; if (challenge_hash(peer_challenge, auth_challenge, username, - username_len, challenge)) - return -1; - if (nt_password_hash(password, password_len, password_hash)) + username_len, challenge) || + nt_password_hash(password, password_len, password_hash)) return -1; challenge_response(challenge, password_hash, response); return 0; @@ -257,12 +255,9 @@ int generate_authenticator_response_pwhash( addr2[1] = challenge; addr2[2] = magic2; - if (hash_nt_password_hash(password_hash, password_hash_hash)) - return -1; - if (sha1_vector(3, addr1, len1, response)) - return -1; - - if (challenge_hash(peer_challenge, auth_challenge, username, + if (hash_nt_password_hash(password_hash, password_hash_hash) || + sha1_vector(3, addr1, len1, response) || + challenge_hash(peer_challenge, auth_challenge, username, username_len, challenge)) return -1; return sha1_vector(3, addr2, len2, response); @@ -417,6 +412,8 @@ int get_asymetric_start_key(const u8 *master_key, u8 *session_key, } +#ifndef CONFIG_NO_RC4 + #define PWBLOCK_LEN 516 /** @@ -436,10 +433,8 @@ int encrypt_pw_block_with_password_hash( os_memset(pw_block, 0, PWBLOCK_LEN); - if (utf8_to_ucs2(password, password_len, pw_block, 512, &ucs2_len) < 0) - return -1; - - if (ucs2_len > 256) + if (utf8_to_ucs2(password, password_len, pw_block, 512, &ucs2_len) < 0 + || ucs2_len > 256) return -1; offset = (256 - ucs2_len) * 2; @@ -484,6 +479,8 @@ int new_password_encrypted_with_old_nt_password_hash( return 0; } +#endif /* CONFIG_NO_RC4 */ + /** * nt_password_hash_encrypted_with_block - NtPasswordHashEncryptedWithBlock() - RFC 2759, Sect 8.13 diff --git a/contrib/wpa/src/crypto/ms_funcs.h b/contrib/wpa/src/crypto/ms_funcs.h index bd9bfee..b5b5918 100644 --- a/contrib/wpa/src/crypto/ms_funcs.h +++ b/contrib/wpa/src/crypto/ms_funcs.h @@ -33,6 +33,8 @@ int nt_challenge_response(const u8 *challenge, const u8 *password, void challenge_response(const u8 *challenge, const u8 *password_hash, u8 *response); +int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge, + const u8 *username, size_t username_len, u8 *challenge); int nt_password_hash(const u8 *password, size_t password_len, u8 *password_hash); int hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash); diff --git a/contrib/wpa/src/crypto/random.c b/contrib/wpa/src/crypto/random.c index bc758aa..3a86a93 100644 --- a/contrib/wpa/src/crypto/random.c +++ b/contrib/wpa/src/crypto/random.c @@ -181,6 +181,7 @@ int random_get_bytes(void *buf, size_t len) #ifdef CONFIG_FIPS /* Mix in additional entropy from the crypto module */ + bytes = buf; left = len; while (left) { size_t siz, i; diff --git a/contrib/wpa/src/crypto/sha1-tlsprf.c b/contrib/wpa/src/crypto/sha1-tlsprf.c index 0effd9b..f9bc0eb 100644 --- a/contrib/wpa/src/crypto/sha1-tlsprf.c +++ b/contrib/wpa/src/crypto/sha1-tlsprf.c @@ -95,5 +95,10 @@ int tls_prf_sha1_md5(const u8 *secret, size_t secret_len, const char *label, SHA1_pos++; } + os_memset(A_MD5, 0, MD5_MAC_LEN); + os_memset(P_MD5, 0, MD5_MAC_LEN); + os_memset(A_SHA1, 0, SHA1_MAC_LEN); + os_memset(P_SHA1, 0, SHA1_MAC_LEN); + return 0; } diff --git a/contrib/wpa/src/crypto/sha1-tprf.c b/contrib/wpa/src/crypto/sha1-tprf.c index a529494..562510f 100644 --- a/contrib/wpa/src/crypto/sha1-tprf.c +++ b/contrib/wpa/src/crypto/sha1-tprf.c @@ -66,5 +66,7 @@ int sha1_t_prf(const u8 *key, size_t key_len, const char *label, len[0] = SHA1_MAC_LEN; } + os_memset(hash, 0, SHA1_MAC_LEN); + return 0; } diff --git a/contrib/wpa/src/crypto/sha256-kdf.c b/contrib/wpa/src/crypto/sha256-kdf.c index d8a1beb..e7509ce 100644 --- a/contrib/wpa/src/crypto/sha256-kdf.c +++ b/contrib/wpa/src/crypto/sha256-kdf.c @@ -61,6 +61,7 @@ int hmac_sha256_kdf(const u8 *secret, size_t secret_len, if (iter == 255) { os_memset(out, 0, outlen); + os_memset(T, 0, SHA256_MAC_LEN); return -1; } iter++; @@ -68,9 +69,11 @@ int hmac_sha256_kdf(const u8 *secret, size_t secret_len, if (hmac_sha256_vector(secret, secret_len, 4, addr, len, T) < 0) { os_memset(out, 0, outlen); + os_memset(T, 0, SHA256_MAC_LEN); return -1; } } + os_memset(T, 0, SHA256_MAC_LEN); return 0; } diff --git a/contrib/wpa/src/crypto/sha384-prf.c b/contrib/wpa/src/crypto/sha384-prf.c new file mode 100644 index 0000000..653920b --- /dev/null +++ b/contrib/wpa/src/crypto/sha384-prf.c @@ -0,0 +1,100 @@ +/* + * SHA384-based KDF (IEEE 802.11ac) + * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha384.h" +#include "crypto.h" + + +/** + * sha384_prf - SHA384-based Key derivation function (IEEE 802.11ac, 11.6.1.7.2) + * @key: Key for KDF + * @key_len: Length of the key in bytes + * @label: A unique label for each purpose of the PRF + * @data: Extra data to bind into the key + * @data_len: Length of the data + * @buf: Buffer for the generated pseudo-random key + * @buf_len: Number of bytes of key to generate + * + * This function is used to derive new, cryptographically separate keys from a + * given key. + */ +void sha384_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len) +{ + sha384_prf_bits(key, key_len, label, data, data_len, buf, buf_len * 8); +} + + +/** + * sha384_prf_bits - IEEE Std 802.11ac-2013, 11.6.1.7.2 Key derivation function + * @key: Key for KDF + * @key_len: Length of the key in bytes + * @label: A unique label for each purpose of the PRF + * @data: Extra data to bind into the key + * @data_len: Length of the data + * @buf: Buffer for the generated pseudo-random key + * @buf_len: Number of bits of key to generate + * + * 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 sha384_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; + u8 hash[SHA384_MAC_LEN]; + const u8 *addr[4]; + size_t len[4]; + u8 counter_le[2], length_le[2]; + size_t buf_len = (buf_len_bits + 7) / 8; + + addr[0] = counter_le; + len[0] = 2; + addr[1] = (u8 *) label; + len[1] = os_strlen(label); + addr[2] = data; + len[2] = data_len; + addr[3] = length_le; + len[3] = sizeof(length_le); + + WPA_PUT_LE16(length_le, buf_len_bits); + pos = 0; + while (pos < buf_len) { + plen = buf_len - pos; + WPA_PUT_LE16(counter_le, counter); + if (plen >= SHA384_MAC_LEN) { + hmac_sha384_vector(key, key_len, 4, addr, len, + &buf[pos]); + pos += SHA384_MAC_LEN; + } else { + hmac_sha384_vector(key, key_len, 4, addr, len, hash); + os_memcpy(&buf[pos], hash, plen); + pos += plen; + break; + } + counter++; + } + + /* + * Mask out unused bits in the last octet if it does not use all the + * bits. + */ + if (buf_len_bits % 8) { + u8 mask = 0xff << (8 - buf_len_bits % 8); + buf[pos - 1] &= mask; + } + + os_memset(hash, 0, sizeof(hash)); +} diff --git a/contrib/wpa/src/crypto/sha384.h b/contrib/wpa/src/crypto/sha384.h index e6a1fe4..3deafa5 100644 --- a/contrib/wpa/src/crypto/sha384.h +++ b/contrib/wpa/src/crypto/sha384.h @@ -15,5 +15,10 @@ int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); int hmac_sha384(const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *mac); +void sha384_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 sha384_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); #endif /* SHA384_H */ diff --git a/contrib/wpa/src/crypto/tls.h b/contrib/wpa/src/crypto/tls.h index 9ae95a6..2e56233 100644 --- a/contrib/wpa/src/crypto/tls.h +++ b/contrib/wpa/src/crypto/tls.h @@ -11,9 +11,7 @@ struct tls_connection; -struct tls_keys { - const u8 *master_key; /* TLS master secret */ - size_t master_key_len; +struct tls_random { const u8 *client_random; size_t client_random_len; const u8 *server_random; @@ -81,6 +79,7 @@ struct tls_config { int fips_mode; int cert_in_cb; const char *openssl_ciphers; + unsigned int tls_session_lifetime; void (*event_cb)(void *ctx, enum tls_event ev, union tls_event_data *data); @@ -95,6 +94,7 @@ struct tls_config { #define TLS_CONN_DISABLE_TLSv1_1 BIT(5) #define TLS_CONN_DISABLE_TLSv1_2 BIT(6) #define TLS_CONN_EAP_FAST BIT(7) +#define TLS_CONN_DISABLE_TLSv1_0 BIT(8) /** * struct tls_connection_params - Parameters for TLS connection @@ -255,6 +255,7 @@ int tls_connection_established(void *tls_ctx, struct tls_connection *conn); int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn); enum { + TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN = -4, TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED = -3, TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED = -2 }; @@ -265,10 +266,12 @@ enum { * @conn: Connection context data from tls_connection_init() * @params: Connection parameters * Returns: 0 on success, -1 on failure, - * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on possible PIN error causing - * PKCS#11 engine failure, or + * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on error causing PKCS#11 engine + * failure, or * TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED (-3) on failure to verify the - * PKCS#11 engine private key. + * PKCS#11 engine private key, or + * TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN (-4) on PIN error causing PKCS#11 engine + * failure. */ int __must_check tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, @@ -279,10 +282,12 @@ tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, * @tls_ctx: TLS context data from tls_init() * @params: Global TLS parameters * Returns: 0 on success, -1 on failure, - * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on possible PIN error causing - * PKCS#11 engine failure, or + * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on error causing PKCS#11 engine + * failure, or * TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED (-3) on failure to verify the - * PKCS#11 engine private key. + * PKCS#11 engine private key, or + * TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN (-4) on PIN error causing PKCS#11 engine + * failure. */ int __must_check tls_global_set_params( void *tls_ctx, const struct tls_connection_params *params); @@ -301,22 +306,28 @@ int __must_check tls_global_set_verify(void *tls_ctx, int check_crl); * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @verify_peer: 1 = verify peer certificate + * @flags: Connection flags (TLS_CONN_*) + * @session_ctx: Session caching context or %NULL to use default + * @session_ctx_len: Length of @session_ctx in bytes. * Returns: 0 on success, -1 on failure */ int __must_check tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, - int verify_peer); + int verify_peer, + unsigned int flags, + const u8 *session_ctx, + size_t session_ctx_len); /** - * tls_connection_get_keys - Get master key and random data from TLS connection + * tls_connection_get_random - Get random data from TLS connection * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() - * @keys: Structure of key/random data (filled on success) + * @data: Structure of client/server random data (filled on success) * Returns: 0 on success, -1 on failure */ -int __must_check tls_connection_get_keys(void *tls_ctx, +int __must_check tls_connection_get_random(void *tls_ctx, struct tls_connection *conn, - struct tls_keys *keys); + struct tls_random *data); /** * tls_connection_prf - Use TLS-PRF to derive keying material @@ -325,23 +336,22 @@ int __must_check tls_connection_get_keys(void *tls_ctx, * @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 * - * This function is optional to implement if tls_connection_get_keys() provides - * access to master secret and server/client random values. If these values are - * not exported from the TLS library, tls_connection_prf() is required so that - * further keying material can be derived from the master secret. If not - * implemented, the function will still need to be defined, but it can just - * return -1. 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). + * 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. */ 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); /** @@ -461,6 +471,19 @@ int __must_check tls_connection_set_cipher_list(void *tls_ctx, u8 *ciphers); /** + * tls_get_version - Get the current TLS version number + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @buf: Buffer for returning the TLS version number + * @buflen: buf size + * Returns: 0 on success, -1 on failure + * + * Get the currently used TLS version number. + */ +int __must_check tls_get_version(void *tls_ctx, struct tls_connection *conn, + char *buf, size_t buflen); + +/** * tls_get_cipher - Get current cipher name * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() @@ -527,23 +550,6 @@ int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn); int tls_connection_get_write_alerts(void *tls_ctx, struct tls_connection *conn); -/** - * tls_connection_get_keyblock_size - Get TLS key_block size - * @tls_ctx: TLS context data from tls_init() - * @conn: Connection context data from tls_connection_init() - * Returns: Size of the key_block for the negotiated cipher suite or -1 on - * failure - */ -int tls_connection_get_keyblock_size(void *tls_ctx, - struct tls_connection *conn); - -/** - * tls_capabilities - Get supported TLS capabilities - * @tls_ctx: TLS context data from tls_init() - * Returns: Bit field of supported TLS capabilities (TLS_CAPABILITY_*) - */ -unsigned int tls_capabilities(void *tls_ctx); - typedef int (*tls_session_ticket_cb) (void *ctx, const u8 *ticket, size_t len, const u8 *client_random, const u8 *server_random, u8 *master_secret); @@ -569,4 +575,14 @@ void tls_connection_set_test_flags(struct tls_connection *conn, u32 flags); int tls_get_library_version(char *buf, size_t buf_len); +void tls_connection_set_success_data(struct tls_connection *conn, + struct wpabuf *data); + +void tls_connection_set_success_data_resumed(struct tls_connection *conn); + +const struct wpabuf * +tls_connection_get_success_data(struct tls_connection *conn); + +void tls_connection_remove_session(struct tls_connection *conn); + #endif /* TLS_H */ diff --git a/contrib/wpa/src/crypto/tls_gnutls.c b/contrib/wpa/src/crypto/tls_gnutls.c index 65db6fc..f994379 100644 --- a/contrib/wpa/src/crypto/tls_gnutls.c +++ b/contrib/wpa/src/crypto/tls_gnutls.c @@ -708,7 +708,8 @@ int tls_global_set_verify(void *ssl_ctx, int check_crl) int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, - int verify_peer) + int verify_peer, unsigned int flags, + const u8 *session_ctx, size_t session_ctx_len) { if (conn == NULL || conn->session == NULL) return -1; @@ -722,8 +723,8 @@ int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, } -int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, - struct tls_keys *keys) +int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn, + struct tls_random *keys) { #if GNUTLS_VERSION_NUMBER >= 0x030012 gnutls_datum_t client, server; @@ -747,9 +748,9 @@ int tls_connection_get_keys(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, - u8 *out, size_t out_len) + int skip_keyblock, u8 *out, size_t out_len) { - if (conn == NULL || conn->session == NULL) + if (conn == NULL || conn->session == NULL || skip_keyblock) return -1; return gnutls_prf(conn->session, os_strlen(label), label, @@ -1426,6 +1427,14 @@ 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 */ + return -1; +} + + int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn, char *buf, size_t buflen) { @@ -1476,30 +1485,39 @@ int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn) } -int tls_connection_get_keyblock_size(void *tls_ctx, - struct tls_connection *conn) +int tls_connection_set_session_ticket_cb(void *tls_ctx, + struct tls_connection *conn, + tls_session_ticket_cb cb, void *ctx) { - /* TODO */ return -1; } -unsigned int tls_capabilities(void *tls_ctx) +int tls_get_library_version(char *buf, size_t buf_len) { - return 0; + return os_snprintf(buf, buf_len, "GnuTLS build=%s run=%s", + GNUTLS_VERSION, gnutls_check_version(NULL)); } -int tls_connection_set_session_ticket_cb(void *tls_ctx, - struct tls_connection *conn, - tls_session_ticket_cb cb, void *ctx) +void tls_connection_set_success_data(struct tls_connection *conn, + struct wpabuf *data) { - return -1; } -int tls_get_library_version(char *buf, size_t buf_len) +void tls_connection_set_success_data_resumed(struct tls_connection *conn) +{ +} + + +const struct wpabuf * +tls_connection_get_success_data(struct tls_connection *conn) +{ + return NULL; +} + + +void tls_connection_remove_session(struct tls_connection *conn) { - return os_snprintf(buf, buf_len, "GnuTLS build=%s run=%s", - GNUTLS_VERSION, gnutls_check_version(NULL)); } diff --git a/contrib/wpa/src/crypto/tls_internal.c b/contrib/wpa/src/crypto/tls_internal.c index 0c955da..704751d 100644 --- a/contrib/wpa/src/crypto/tls_internal.c +++ b/contrib/wpa/src/crypto/tls_internal.c @@ -192,26 +192,31 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, if (params->subject_match) { wpa_printf(MSG_INFO, "TLS: subject_match not supported"); + tlsv1_cred_free(cred); return -1; } if (params->altsubject_match) { wpa_printf(MSG_INFO, "TLS: altsubject_match not supported"); + tlsv1_cred_free(cred); return -1; } if (params->suffix_match) { wpa_printf(MSG_INFO, "TLS: suffix_match not supported"); + tlsv1_cred_free(cred); return -1; } if (params->domain_match) { wpa_printf(MSG_INFO, "TLS: domain_match not supported"); + tlsv1_cred_free(cred); return -1; } if (params->openssl_ciphers) { - wpa_printf(MSG_INFO, "GnuTLS: openssl_ciphers not supported"); + wpa_printf(MSG_INFO, "TLS: openssl_ciphers not supported"); + tlsv1_cred_free(cred); return -1; } @@ -323,7 +328,8 @@ int tls_global_set_verify(void *tls_ctx, int check_crl) int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, - int verify_peer) + int verify_peer, unsigned int flags, + const u8 *session_ctx, size_t session_ctx_len) { #ifdef CONFIG_TLS_INTERNAL_SERVER if (conn->server) @@ -333,16 +339,30 @@ int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, } -int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn, - struct tls_keys *keys) +int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn, + struct tls_random *data) +{ +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (conn->client) + return tlsv1_client_get_random(conn->client, data); +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_get_random(conn->server, data); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + return -1; +} + + +static int tls_get_keyblock_size(struct tls_connection *conn) { #ifdef CONFIG_TLS_INTERNAL_CLIENT if (conn->client) - return tlsv1_client_get_keys(conn->client, keys); + return tlsv1_client_get_keyblock_size(conn->client); #endif /* CONFIG_TLS_INTERNAL_CLIENT */ #ifdef CONFIG_TLS_INTERNAL_SERVER if (conn->server) - return tlsv1_server_get_keys(conn->server, keys); + return tlsv1_server_get_keyblock_size(conn->server); #endif /* CONFIG_TLS_INTERNAL_SERVER */ return -1; } @@ -350,23 +370,41 @@ int tls_connection_get_keys(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, - u8 *out, size_t out_len) -{ + int skip_keyblock, u8 *out, size_t out_len) +{ + int ret = -1, skip = 0; + u8 *tmp_out = NULL; + u8 *_out = out; + + if (skip_keyblock) { + skip = tls_get_keyblock_size(conn); + if (skip < 0) + return -1; + tmp_out = os_malloc(skip + out_len); + if (!tmp_out) + return -1; + _out = tmp_out; + } + #ifdef CONFIG_TLS_INTERNAL_CLIENT if (conn->client) { - return tlsv1_client_prf(conn->client, label, - server_random_first, - out, out_len); + ret = tlsv1_client_prf(conn->client, label, + server_random_first, + _out, out_len); } #endif /* CONFIG_TLS_INTERNAL_CLIENT */ #ifdef CONFIG_TLS_INTERNAL_SERVER if (conn->server) { - return tlsv1_server_prf(conn->server, label, - server_random_first, - out, out_len); + ret = tlsv1_server_prf(conn->server, label, + server_random_first, + _out, out_len); } #endif /* CONFIG_TLS_INTERNAL_SERVER */ - return -1; + if (ret == 0 && skip_keyblock) + os_memcpy(out, _out + skip, out_len); + bin_clear_free(tmp_out, skip); + + return ret; } @@ -580,6 +618,14 @@ 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 */ + return -1; +} + + int tls_get_cipher(void *tls_ctx, struct tls_connection *conn, char *buf, size_t buflen) { @@ -637,27 +683,6 @@ int tls_connection_get_write_alerts(void *tls_ctx, } -int tls_connection_get_keyblock_size(void *tls_ctx, - struct tls_connection *conn) -{ -#ifdef CONFIG_TLS_INTERNAL_CLIENT - if (conn->client) - return tlsv1_client_get_keyblock_size(conn->client); -#endif /* CONFIG_TLS_INTERNAL_CLIENT */ -#ifdef CONFIG_TLS_INTERNAL_SERVER - if (conn->server) - return tlsv1_server_get_keyblock_size(conn->server); -#endif /* CONFIG_TLS_INTERNAL_SERVER */ - return -1; -} - - -unsigned int tls_capabilities(void *tls_ctx) -{ - return 0; -} - - int tls_connection_set_session_ticket_cb(void *tls_ctx, struct tls_connection *conn, tls_session_ticket_cb cb, @@ -683,3 +708,26 @@ int tls_get_library_version(char *buf, size_t buf_len) { return os_snprintf(buf, buf_len, "internal"); } + + +void tls_connection_set_success_data(struct tls_connection *conn, + struct wpabuf *data) +{ +} + + +void tls_connection_set_success_data_resumed(struct tls_connection *conn) +{ +} + + +const struct wpabuf * +tls_connection_get_success_data(struct tls_connection *conn) +{ + return NULL; +} + + +void tls_connection_remove_session(struct tls_connection *conn) +{ +} diff --git a/contrib/wpa/src/crypto/tls_none.c b/contrib/wpa/src/crypto/tls_none.c index a6d210a..ae392ad 100644 --- a/contrib/wpa/src/crypto/tls_none.c +++ b/contrib/wpa/src/crypto/tls_none.c @@ -72,14 +72,15 @@ int tls_global_set_verify(void *tls_ctx, int check_crl) int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, - int verify_peer) + int verify_peer, unsigned int flags, + const u8 *session_ctx, size_t session_ctx_len) { return -1; } -int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn, - struct tls_keys *keys) +int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn, + struct tls_random *data) { return -1; } @@ -87,7 +88,7 @@ int tls_connection_get_keys(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, - u8 *out, size_t out_len) + int skip_keyblock, u8 *out, size_t out_len) { return -1; } @@ -140,6 +141,13 @@ 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) +{ + return -1; +} + + int tls_get_cipher(void *tls_ctx, struct tls_connection *conn, char *buf, size_t buflen) { @@ -181,20 +189,30 @@ int tls_connection_get_write_alerts(void *tls_ctx, } -int tls_connection_get_keyblock_size(void *tls_ctx, - struct tls_connection *conn) +int tls_get_library_version(char *buf, size_t buf_len) { - return -1; + return os_snprintf(buf, buf_len, "none"); } -unsigned int tls_capabilities(void *tls_ctx) +void tls_connection_set_success_data(struct tls_connection *conn, + struct wpabuf *data) { - return 0; } -int tls_get_library_version(char *buf, size_t buf_len) +void tls_connection_set_success_data_resumed(struct tls_connection *conn) +{ +} + + +const struct wpabuf * +tls_connection_get_success_data(struct tls_connection *conn) +{ + return NULL; +} + + +void tls_connection_remove_session(struct tls_connection *conn) { - return os_snprintf(buf, buf_len, "none"); } diff --git a/contrib/wpa/src/crypto/tls_openssl.c b/contrib/wpa/src/crypto/tls_openssl.c index 52db8fc..8b7b47b 100644 --- a/contrib/wpa/src/crypto/tls_openssl.c +++ b/contrib/wpa/src/crypto/tls_openssl.c @@ -1,6 +1,6 @@ /* * SSL/TLS interface functions for OpenSSL - * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -23,15 +23,19 @@ #ifndef OPENSSL_NO_ENGINE #include <openssl/engine.h> #endif /* OPENSSL_NO_ENGINE */ +#ifndef OPENSSL_NO_DSA +#include <openssl/dsa.h> +#endif +#ifndef OPENSSL_NO_DH +#include <openssl/dh.h> +#endif #include "common.h" #include "crypto.h" +#include "sha1.h" +#include "sha256.h" #include "tls.h" -#if defined(SSL_CTX_get_app_data) && defined(SSL_CTX_set_app_data) -#define OPENSSL_SUPPORTS_CTX_APP_DATA -#endif - #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 @@ -70,6 +74,7 @@ static BIO * BIO_from_keystore(const char *key) #endif /* ANDROID */ static int tls_openssl_ref_count = 0; +static int tls_ex_idx_session = -1; struct tls_context { void (*event_cb)(void *ctx, enum tls_event ev, @@ -82,6 +87,11 @@ struct tls_context { static struct tls_context *tls_global = NULL; +struct tls_data { + SSL_CTX *ssl; + unsigned int tls_session_lifetime; +}; + struct tls_connection { struct tls_context *context; SSL_CTX *ssl_ctx; @@ -105,6 +115,7 @@ struct tls_connection { unsigned int cert_probe:1; unsigned int server_cert_only:1; unsigned int invalid_hb_used:1; + unsigned int success_data:1; u8 srv_cert_hash[32]; @@ -113,6 +124,11 @@ struct tls_connection { X509 *peer_cert; 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 }; @@ -735,8 +751,27 @@ static int tls_engine_load_dynamic_opensc(const char *opensc_so_path) #endif /* OPENSSL_NO_ENGINE */ +static void remove_session_cb(SSL_CTX *ctx, SSL_SESSION *sess) +{ + struct wpabuf *buf; + + if (tls_ex_idx_session < 0) + return; + buf = SSL_SESSION_get_ex_data(sess, tls_ex_idx_session); + if (!buf) + return; + wpa_printf(MSG_DEBUG, + "OpenSSL: Free application session data %p (sess %p)", + buf, sess); + wpabuf_free(buf); + + SSL_SESSION_set_ex_data(sess, tls_ex_idx_session, NULL); +} + + void * tls_init(const struct tls_config *conf) { + struct tls_data *data; SSL_CTX *ssl; struct tls_context *context; const char *ciphers; @@ -748,7 +783,9 @@ void * tls_init(const struct tls_config *conf) #ifdef CONFIG_FIPS #ifdef OPENSSL_FIPS if (conf && conf->fips_mode) { - if (!FIPS_mode_set(1)) { + static int fips_enabled = 0; + + if (!fips_enabled && !FIPS_mode_set(1)) { wpa_printf(MSG_ERROR, "Failed to enable FIPS " "mode"); ERR_load_crypto_strings(); @@ -756,8 +793,10 @@ void * tls_init(const struct tls_config *conf) os_free(tls_global); tls_global = NULL; return NULL; - } else + } else { wpa_printf(MSG_INFO, "Running in FIPS mode"); + fips_enabled = 1; + } } #else /* OPENSSL_FIPS */ if (conf && conf->fips_mode) { @@ -791,38 +830,58 @@ void * tls_init(const struct tls_config *conf) PKCS12_PBE_add(); #endif /* PKCS12_FUNCS */ } else { -#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA - /* Newer OpenSSL can store app-data per-SSL */ context = tls_context_new(conf); if (context == NULL) return NULL; -#else /* OPENSSL_SUPPORTS_CTX_APP_DATA */ - context = tls_global; -#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ } tls_openssl_ref_count++; - ssl = SSL_CTX_new(SSLv23_method()); + data = os_zalloc(sizeof(*data)); + if (data) + ssl = SSL_CTX_new(SSLv23_method()); + else + ssl = NULL; if (ssl == NULL) { tls_openssl_ref_count--; -#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA if (context != tls_global) os_free(context); -#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ if (tls_openssl_ref_count == 0) { os_free(tls_global); tls_global = NULL; } return NULL; } + data->ssl = ssl; + if (conf) + data->tls_session_lifetime = conf->tls_session_lifetime; SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv2); SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv3); SSL_CTX_set_info_callback(ssl, ssl_info_cb); -#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA SSL_CTX_set_app_data(ssl, context); -#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ + if (data->tls_session_lifetime > 0) { + SSL_CTX_set_quiet_shutdown(ssl, 1); + /* + * Set default context here. In practice, this will be replaced + * by the per-EAP method context in tls_connection_set_verify(). + */ + SSL_CTX_set_session_id_context(ssl, (u8 *) "hostapd", 7); + SSL_CTX_set_session_cache_mode(ssl, SSL_SESS_CACHE_SERVER); + SSL_CTX_set_timeout(ssl, data->tls_session_lifetime); + SSL_CTX_sess_set_remove_cb(ssl, remove_session_cb); + } else { + SSL_CTX_set_session_cache_mode(ssl, SSL_SESS_CACHE_OFF); + } + + if (tls_ex_idx_session < 0) { + tls_ex_idx_session = SSL_SESSION_get_ex_new_index( + 0, NULL, NULL, NULL, NULL); + if (tls_ex_idx_session < 0) { + tls_deinit(data); + return NULL; + } + } #ifndef OPENSSL_NO_ENGINE wpa_printf(MSG_DEBUG, "ENGINE: Loading dynamic engine"); @@ -835,7 +894,7 @@ void * tls_init(const struct tls_config *conf) if (tls_engine_load_dynamic_opensc(conf->opensc_engine_path) || tls_engine_load_dynamic_pkcs11(conf->pkcs11_engine_path, conf->pkcs11_module_path)) { - tls_deinit(ssl); + tls_deinit(data); return NULL; } } @@ -849,22 +908,23 @@ void * tls_init(const struct tls_config *conf) wpa_printf(MSG_ERROR, "OpenSSL: Failed to set cipher string '%s'", ciphers); - tls_deinit(ssl); + tls_deinit(data); return NULL; } - return ssl; + return data; } void tls_deinit(void *ssl_ctx) { - SSL_CTX *ssl = ssl_ctx; -#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA + struct tls_data *data = ssl_ctx; + SSL_CTX *ssl = data->ssl; struct tls_context *context = SSL_CTX_get_app_data(ssl); if (context != tls_global) os_free(context); -#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ + if (data->tls_session_lifetime > 0) + SSL_CTX_flush_sessions(ssl, 0); SSL_CTX_free(ssl); tls_openssl_ref_count--; @@ -881,8 +941,31 @@ void tls_deinit(void *ssl_ctx) os_free(tls_global); tls_global = NULL; } + + os_free(data); +} + + +#ifndef OPENSSL_NO_ENGINE + +/* Cryptoki return values */ +#define CKR_PIN_INCORRECT 0x000000a0 +#define CKR_PIN_INVALID 0x000000a1 +#define CKR_PIN_LEN_RANGE 0x000000a2 + +/* libp11 */ +#define ERR_LIB_PKCS11 ERR_LIB_USER + +static int tls_is_pin_error(unsigned int err) +{ + return ERR_GET_LIB(err) == ERR_LIB_PKCS11 && + (ERR_GET_REASON(err) == CKR_PIN_INCORRECT || + ERR_GET_REASON(err) == CKR_PIN_INVALID || + ERR_GET_REASON(err) == CKR_PIN_LEN_RANGE); } +#endif /* OPENSSL_NO_ENGINE */ + static int tls_engine_init(struct tls_connection *conn, const char *engine_id, const char *pin, const char *key_id, @@ -935,11 +1018,16 @@ static int tls_engine_init(struct tls_connection *conn, const char *engine_id, key_id, NULL, &key_cb); if (!conn->private_key) { + unsigned long err = ERR_get_error(); + wpa_printf(MSG_ERROR, "ENGINE: cannot load private key with id '%s' [%s]", key_id, - ERR_error_string(ERR_get_error(), NULL)); - ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; + ERR_error_string(err, NULL)); + if (tls_is_pin_error(err)) + ret = TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN; + else + ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; goto err; } } @@ -1030,19 +1118,16 @@ static void tls_msg_cb(int write_p, int version, int content_type, struct tls_connection * tls_connection_init(void *ssl_ctx) { - SSL_CTX *ssl = ssl_ctx; + struct tls_data *data = ssl_ctx; + SSL_CTX *ssl = data->ssl; struct tls_connection *conn; long options; -#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA struct tls_context *context = SSL_CTX_get_app_data(ssl); -#else /* OPENSSL_SUPPORTS_CTX_APP_DATA */ - struct tls_context *context = tls_global; -#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ conn = os_zalloc(sizeof(*conn)); if (conn == NULL) return NULL; - conn->ssl_ctx = ssl_ctx; + conn->ssl_ctx = ssl; conn->ssl = SSL_new(ssl); if (conn->ssl == NULL) { tls_show_errors(MSG_INFO, __func__, @@ -1091,6 +1176,14 @@ void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) { if (conn == NULL) return; + if (conn->success_data) { + /* + * Make sure ssl_clear_bad_session() does not remove this + * session. + */ + SSL_set_quiet_shutdown(conn->ssl, 1); + SSL_shutdown(conn->ssl); + } SSL_free(conn->ssl); tls_engine_deinit(conn); os_free(conn->subject_match); @@ -1118,7 +1211,7 @@ int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) * and "close notify" shutdown alert would confuse AS. */ SSL_set_quiet_shutdown(conn->ssl, 1); SSL_shutdown(conn->ssl); - return 0; + return SSL_clear(conn->ssl) == 1 ? 0 : -1; } @@ -1617,9 +1710,9 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) #ifndef OPENSSL_NO_STDIO -static int tls_load_ca_der(void *_ssl_ctx, const char *ca_cert) +static int tls_load_ca_der(struct tls_data *data, const char *ca_cert) { - SSL_CTX *ssl_ctx = _ssl_ctx; + SSL_CTX *ssl_ctx = data->ssl; X509_LOOKUP *lookup; int ret = 0; @@ -1649,11 +1742,12 @@ static int tls_load_ca_der(void *_ssl_ctx, const char *ca_cert) #endif /* OPENSSL_NO_STDIO */ -static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, +static int tls_connection_ca_cert(struct tls_data *data, + struct tls_connection *conn, const char *ca_cert, const u8 *ca_cert_blob, size_t ca_cert_blob_len, const char *ca_path) { - SSL_CTX *ssl_ctx = _ssl_ctx; + SSL_CTX *ssl_ctx = data->ssl; X509_STORE *store; /* @@ -1788,7 +1882,7 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, tls_show_errors(MSG_WARNING, __func__, "Failed to load root certificates"); if (ca_cert && - tls_load_ca_der(ssl_ctx, ca_cert) == 0) { + tls_load_ca_der(data, ca_cert) == 0) { wpa_printf(MSG_DEBUG, "OpenSSL: %s - loaded " "DER format CA certificate", __func__); @@ -1797,7 +1891,7 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, } else { wpa_printf(MSG_DEBUG, "TLS: Trusted root " "certificate(s) loaded"); - tls_get_errors(ssl_ctx); + tls_get_errors(data); } #else /* OPENSSL_NO_STDIO */ wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", @@ -1814,8 +1908,10 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, } -static int tls_global_ca_cert(SSL_CTX *ssl_ctx, const char *ca_cert) +static int tls_global_ca_cert(struct tls_data *data, const char *ca_cert) { + SSL_CTX *ssl_ctx = data->ssl; + if (ca_cert) { if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, NULL) != 1) { @@ -1843,7 +1939,8 @@ int tls_global_set_verify(void *ssl_ctx, int check_crl) int flags; if (check_crl) { - X509_STORE *cs = SSL_CTX_get_cert_store(ssl_ctx); + struct tls_data *data = ssl_ctx; + X509_STORE *cs = SSL_CTX_get_cert_store(data->ssl); if (cs == NULL) { tls_show_errors(MSG_INFO, __func__, "Failed to get " "certificate store when enabling " @@ -1901,10 +1998,44 @@ static int tls_connection_set_subject_match(struct tls_connection *conn, } +static void tls_set_conn_flags(SSL *ssl, unsigned int flags) +{ +#ifdef SSL_OP_NO_TICKET + if (flags & TLS_CONN_DISABLE_SESSION_TICKET) + SSL_set_options(ssl, SSL_OP_NO_TICKET); +#ifdef SSL_clear_options + else + SSL_clear_options(ssl, SSL_OP_NO_TICKET); +#endif /* SSL_clear_options */ +#endif /* SSL_OP_NO_TICKET */ + +#ifdef SSL_OP_NO_TLSv1 + if (flags & TLS_CONN_DISABLE_TLSv1_0) + SSL_set_options(ssl, SSL_OP_NO_TLSv1); + else + SSL_clear_options(ssl, SSL_OP_NO_TLSv1); +#endif /* SSL_OP_NO_TLSv1 */ +#ifdef SSL_OP_NO_TLSv1_1 + if (flags & TLS_CONN_DISABLE_TLSv1_1) + SSL_set_options(ssl, SSL_OP_NO_TLSv1_1); + else + SSL_clear_options(ssl, SSL_OP_NO_TLSv1_1); +#endif /* SSL_OP_NO_TLSv1_1 */ +#ifdef SSL_OP_NO_TLSv1_2 + if (flags & TLS_CONN_DISABLE_TLSv1_2) + SSL_set_options(ssl, SSL_OP_NO_TLSv1_2); + else + SSL_clear_options(ssl, SSL_OP_NO_TLSv1_2); +#endif /* SSL_OP_NO_TLSv1_2 */ +} + + int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, - int verify_peer) + int verify_peer, unsigned int flags, + const u8 *session_ctx, size_t session_ctx_len) { static int counter = 0; + struct tls_data *data = ssl_ctx; if (conn == NULL) return -1; @@ -1919,20 +2050,25 @@ int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL); } + tls_set_conn_flags(conn->ssl, flags); + conn->flags = flags; + SSL_set_accept_state(conn->ssl); - /* - * Set session id context in order to avoid fatal errors when client - * tries to resume a session. However, set the context to a unique - * value in order to effectively disable session resumption for now - * since not all areas of the server code are ready for it (e.g., - * EAP-TTLS needs special handling for Phase 2 after abbreviated TLS - * handshake). - */ - counter++; - SSL_set_session_id_context(conn->ssl, - (const unsigned char *) &counter, - sizeof(counter)); + if (data->tls_session_lifetime == 0) { + /* + * Set session id context to a unique value to make sure + * session resumption cannot be used either through session + * caching or TLS ticket extension. + */ + counter++; + SSL_set_session_id_context(conn->ssl, + (const unsigned char *) &counter, + sizeof(counter)); + } else if (session_ctx) { + SSL_set_session_id_context(conn->ssl, session_ctx, + session_ctx_len); + } return 0; } @@ -2004,9 +2140,12 @@ static int tls_connection_client_cert(struct tls_connection *conn, } -static int tls_global_client_cert(SSL_CTX *ssl_ctx, const char *client_cert) +static int tls_global_client_cert(struct tls_data *data, + const char *client_cert) { #ifndef OPENSSL_NO_STDIO + SSL_CTX *ssl_ctx = data->ssl; + if (client_cert == NULL) return 0; @@ -2040,7 +2179,7 @@ static int tls_passwd_cb(char *buf, int size, int rwflag, void *password) #ifdef PKCS12_FUNCS -static int tls_parse_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, PKCS12 *p12, +static int tls_parse_pkcs12(struct tls_data *data, SSL *ssl, PKCS12 *p12, const char *passwd) { EVP_PKEY *pkey; @@ -2052,6 +2191,8 @@ static int tls_parse_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, PKCS12 *p12, pkey = NULL; cert = NULL; certs = NULL; + if (!passwd) + passwd = ""; if (!PKCS12_parse(p12, passwd, &pkey, &cert, &certs)) { tls_show_errors(MSG_DEBUG, __func__, "Failed to parse PKCS12 file"); @@ -2069,7 +2210,7 @@ static int tls_parse_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, PKCS12 *p12, if (SSL_use_certificate(ssl, cert) != 1) res = -1; } else { - if (SSL_CTX_use_certificate(ssl_ctx, cert) != 1) + if (SSL_CTX_use_certificate(data->ssl, cert) != 1) res = -1; } X509_free(cert); @@ -2081,13 +2222,52 @@ static int tls_parse_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, PKCS12 *p12, if (SSL_use_PrivateKey(ssl, pkey) != 1) res = -1; } else { - if (SSL_CTX_use_PrivateKey(ssl_ctx, pkey) != 1) + if (SSL_CTX_use_PrivateKey(data->ssl, pkey) != 1) res = -1; } EVP_PKEY_free(pkey); } if (certs) { +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + SSL_clear_chain_certs(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) { + tls_show_errors(MSG_DEBUG, __func__, + "Failed to add additional certificate"); + res = -1; + break; + } + } + if (!res) { + /* Try to continue anyway */ + } + sk_X509_free(certs); +#ifndef OPENSSL_IS_BORINGSSL + res = SSL_build_cert_chain(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"); + } else if (res == 2) { + wpa_printf(MSG_DEBUG, + "TLS: Ignore certificate chain verification error when building chain with PKCS#12 extra certificates"); + } +#endif /* OPENSSL_IS_BORINGSSL */ + /* + * Try to continue regardless of result since it is possible for + * the extra certificates not to be required. + */ + 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)); @@ -2097,26 +2277,28 @@ static int tls_parse_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, PKCS12 *p12, * There is no SSL equivalent for the chain cert - so * always add it to the context... */ - if (SSL_CTX_add_extra_chain_cert(ssl_ctx, cert) != 1) { + if (SSL_CTX_add_extra_chain_cert(data->ssl, cert) != 1) + { res = -1; break; } } sk_X509_free(certs); +#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */ } PKCS12_free(p12); if (res < 0) - tls_get_errors(ssl_ctx); + tls_get_errors(data); return res; } #endif /* PKCS12_FUNCS */ -static int tls_read_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, const char *private_key, - const char *passwd) +static int tls_read_pkcs12(struct tls_data *data, SSL *ssl, + const char *private_key, const char *passwd) { #ifdef PKCS12_FUNCS FILE *f; @@ -2135,7 +2317,7 @@ static int tls_read_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, const char *private_key, return -1; } - return tls_parse_pkcs12(ssl_ctx, ssl, p12, passwd); + return tls_parse_pkcs12(data, ssl, p12, passwd); #else /* PKCS12_FUNCS */ wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot read " @@ -2145,7 +2327,7 @@ static int tls_read_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, const char *private_key, } -static int tls_read_pkcs12_blob(SSL_CTX *ssl_ctx, SSL *ssl, +static int tls_read_pkcs12_blob(struct tls_data *data, SSL *ssl, const u8 *blob, size_t len, const char *passwd) { #ifdef PKCS12_FUNCS @@ -2158,7 +2340,7 @@ static int tls_read_pkcs12_blob(SSL_CTX *ssl_ctx, SSL *ssl, return -1; } - return tls_parse_pkcs12(ssl_ctx, ssl, p12, passwd); + return tls_parse_pkcs12(data, ssl, p12, passwd); #else /* PKCS12_FUNCS */ wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot parse " @@ -2183,9 +2365,13 @@ static int tls_engine_get_cert(struct tls_connection *conn, if (!ENGINE_ctrl_cmd(conn->engine, "LOAD_CERT_CTRL", 0, ¶ms, NULL, 1)) { + unsigned long err = ERR_get_error(); + wpa_printf(MSG_ERROR, "ENGINE: cannot load client cert with id" " '%s' [%s]", cert_id, - ERR_error_string(ERR_get_error(), NULL)); + ERR_error_string(err, NULL)); + if (tls_is_pin_error(err)) + return TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN; return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; } if (!params.cert) { @@ -2225,13 +2411,13 @@ static int tls_connection_engine_client_cert(struct tls_connection *conn, } -static int tls_connection_engine_ca_cert(void *_ssl_ctx, +static int tls_connection_engine_ca_cert(struct tls_data *data, struct tls_connection *conn, const char *ca_cert_id) { #ifndef OPENSSL_NO_ENGINE X509 *cert; - SSL_CTX *ssl_ctx = _ssl_ctx; + SSL_CTX *ssl_ctx = data->ssl; X509_STORE *store; if (tls_engine_get_cert(conn, ca_cert_id, &cert)) @@ -2297,14 +2483,14 @@ static int tls_connection_engine_private_key(struct tls_connection *conn) } -static int tls_connection_private_key(void *_ssl_ctx, +static int tls_connection_private_key(struct tls_data *data, struct tls_connection *conn, const char *private_key, const char *private_key_passwd, const u8 *private_key_blob, size_t private_key_blob_len) { - SSL_CTX *ssl_ctx = _ssl_ctx; + SSL_CTX *ssl_ctx = data->ssl; char *passwd; int ok; @@ -2350,7 +2536,7 @@ static int tls_connection_private_key(void *_ssl_ctx, break; } - if (tls_read_pkcs12_blob(ssl_ctx, conn->ssl, private_key_blob, + if (tls_read_pkcs12_blob(data, conn->ssl, private_key_blob, private_key_blob_len, passwd) == 0) { wpa_printf(MSG_DEBUG, "OpenSSL: PKCS#12 as blob --> " "OK"); @@ -2383,7 +2569,7 @@ static int tls_connection_private_key(void *_ssl_ctx, __func__); #endif /* OPENSSL_NO_STDIO */ - if (tls_read_pkcs12(ssl_ctx, conn->ssl, private_key, passwd) + if (tls_read_pkcs12(data, conn->ssl, private_key, passwd) == 0) { wpa_printf(MSG_DEBUG, "OpenSSL: Reading PKCS#12 file " "--> OK"); @@ -2422,9 +2608,11 @@ static int tls_connection_private_key(void *_ssl_ctx, } -static int tls_global_private_key(SSL_CTX *ssl_ctx, const char *private_key, +static int tls_global_private_key(struct tls_data *data, + const char *private_key, const char *private_key_passwd) { + SSL_CTX *ssl_ctx = data->ssl; char *passwd; if (private_key == NULL) @@ -2446,7 +2634,7 @@ static int tls_global_private_key(SSL_CTX *ssl_ctx, const char *private_key, SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key, SSL_FILETYPE_PEM) != 1 && #endif /* OPENSSL_NO_STDIO */ - tls_read_pkcs12(ssl_ctx, NULL, private_key, passwd)) { + tls_read_pkcs12(data, NULL, private_key, passwd)) { tls_show_errors(MSG_INFO, __func__, "Failed to load private key"); os_free(passwd); @@ -2541,7 +2729,7 @@ static int tls_connection_dh(struct tls_connection *conn, const char *dh_file) } -static int tls_global_dh(SSL_CTX *ssl_ctx, const char *dh_file) +static int tls_global_dh(struct tls_data *data, const char *dh_file) { #ifdef OPENSSL_NO_DH if (dh_file == NULL) @@ -2550,6 +2738,7 @@ static int tls_global_dh(SSL_CTX *ssl_ctx, const char *dh_file) "dh_file specified"); return -1; #else /* OPENSSL_NO_DH */ + SSL_CTX *ssl_ctx = data->ssl; DH *dh; BIO *bio; @@ -2615,45 +2804,275 @@ static int tls_global_dh(SSL_CTX *ssl_ctx, const char *dh_file) } -int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, - struct tls_keys *keys) +int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn, + struct tls_random *keys) { -#ifdef CONFIG_FIPS - wpa_printf(MSG_ERROR, "OpenSSL: TLS keys cannot be exported in FIPS " - "mode"); - return -1; -#else /* CONFIG_FIPS */ SSL *ssl; 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->master_key = ssl->session->master_key; - keys->master_key_len = ssl->session->master_key_length; 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; + + os_memset(keys, 0, sizeof(*keys)); + keys->client_random = conn->client_random; + keys->client_random_len = SSL_get_client_random( + ssl, conn->client_random, sizeof(conn->client_random)); + 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 +static int openssl_get_keyblock_size(SSL *ssl) +{ +#if OPENSSL_VERSION_NUMBER < 0x10100000L + const EVP_CIPHER *c; + const EVP_MD *h; + int md_size; + + if (ssl->enc_read_ctx == NULL || ssl->enc_read_ctx->cipher == NULL || + ssl->read_hash == NULL) + 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; + + wpa_printf(MSG_DEBUG, "OpenSSL: keyblock size: key_len=%d MD_size=%d " + "IV_len=%d", EVP_CIPHER_key_length(c), md_size, + EVP_CIPHER_iv_length(c)); + return 2 * (EVP_CIPHER_key_length(c) + + md_size + + EVP_CIPHER_iv_length(c)); +#else + const SSL_CIPHER *ssl_cipher; + int cipher, digest; + const EVP_CIPHER *c; + const EVP_MD *h; + + ssl_cipher = SSL_get_current_cipher(ssl); + if (!ssl_cipher) + return -1; + cipher = SSL_CIPHER_get_cipher_nid(ssl_cipher); + digest = SSL_CIPHER_get_digest_nid(ssl_cipher); + wpa_printf(MSG_DEBUG, "OpenSSL: cipher nid %d digest nid %d", + cipher, digest); + if (cipher < 0 || digest < 0) + return -1; + c = EVP_get_cipherbynid(cipher); + h = EVP_get_digestbynid(digest); + if (!c || !h) + return -1; + + wpa_printf(MSG_DEBUG, + "OpenSSL: keyblock size: key_len=%d MD_size=%d IV_len=%d", + EVP_CIPHER_key_length(c), EVP_MD_size(h), + EVP_CIPHER_iv_length(c)); + return 2 * (EVP_CIPHER_key_length(c) + EVP_MD_size(h) + + EVP_CIPHER_iv_length(c)); +#endif +} +#endif /* CONFIG_FIPS */ + + +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) +{ +#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) + 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); + + 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 + SSL *ssl; + SSL_SESSION *sess; + u8 *rnd; + int ret = -1; + int skip = 0; + u8 *tmp_out = NULL; + u8 *_out = out; + unsigned char client_random[SSL3_RANDOM_SIZE]; + unsigned char server_random[SSL3_RANDOM_SIZE]; + unsigned char master_key[64]; + size_t master_key_len; + 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) + return -1; + ssl = conn->ssl; + if (ssl == NULL) + return -1; + ver = SSL_get_version(ssl); + sess = SSL_get_session(ssl); + 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; + } + + rnd = os_malloc(2 * SSL3_RANDOM_SIZE); + if (!rnd) { + os_free(tmp_out); + return -1; + } + + SSL_get_client_random(ssl, client_random, sizeof(client_random)); + SSL_get_server_random(ssl, server_random, sizeof(server_random)); + 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); + } + + if (os_strcmp(ver, "TLSv1.2") == 0) { + tls_prf_sha256(master_key, master_key_len, + label, 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, + _out, skip + out_len) == 0) { + ret = 0; + } + os_memset(master_key, 0, sizeof(master_key)); + os_free(rnd); + if (ret == 0 && skip_keyblock) + 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, - u8 *out, size_t out_len) + 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) - 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) { @@ -2661,7 +3080,8 @@ int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, return 0; } #endif - return -1; + return openssl_tls_prf(conn, label, server_random_first, + skip_keyblock, out, out_len); } @@ -2676,7 +3096,7 @@ openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data, * Give TLS handshake data from the server (if available) to OpenSSL * for processing. */ - if (in_data && + if (in_data && wpabuf_len(in_data) > 0 && BIO_write(conn->ssl_in, wpabuf_head(in_data), wpabuf_len(in_data)) < 0) { tls_show_errors(MSG_INFO, __func__, @@ -2788,8 +3208,14 @@ openssl_connection_handshake(struct tls_connection *conn, return NULL; } - if (SSL_is_init_finished(conn->ssl) && appl_data && in_data) - *appl_data = openssl_get_appl_data(conn, wpabuf_len(in_data)); + if (SSL_is_init_finished(conn->ssl)) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Handshake finished - resumed=%d", + tls_connection_resumed(conn->ssl_ctx, conn)); + if (appl_data && in_data) + *appl_data = openssl_get_appl_data(conn, + wpabuf_len(in_data)); + } if (conn->invalid_hb_used) { wpa_printf(MSG_INFO, "TLS: Heartbeat attack detected - do not send response"); @@ -2968,6 +3394,21 @@ 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 defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) + if (os_strstr(buf, ":ADH-")) { + /* + * Need to drop to security level 0 to allow anonymous + * cipher suites for EAP-FAST. + */ + SSL_set_security_level(conn->ssl, 0); + } else if (SSL_get_security_level(conn->ssl) == 0) { + /* Force at least security level 1 */ + SSL_set_security_level(conn->ssl, 1); + } +#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ +#endif + if (SSL_set_cipher_list(conn->ssl, buf + 1) != 1) { tls_show_errors(MSG_INFO, __func__, "Cipher suite configuration failed"); @@ -2978,6 +3419,22 @@ 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) +{ + const char *name; + if (conn == NULL || conn->ssl == NULL) + return -1; + + name = SSL_get_version(conn->ssl); + if (name == NULL) + return -1; + + os_strlcpy(buf, name, buflen); + return 0; +} + + int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn, char *buf, size_t buflen) { @@ -3300,6 +3757,7 @@ static int ocsp_status_cb(SSL *s, void *arg) int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, const struct tls_connection_params *params) { + struct tls_data *data = tls_ctx; int ret; unsigned long err; int can_pkcs11 = 0; @@ -3341,6 +3799,8 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, if (can_pkcs11 == 2 && !engine_id) engine_id = "pkcs11"; +#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) +#if OPENSSL_VERSION_NUMBER < 0x10100000L if (params->flags & TLS_CONN_EAP_FAST) { wpa_printf(MSG_DEBUG, "OpenSSL: Use TLSv1_method() for EAP-FAST"); @@ -3350,6 +3810,8 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; } } +#endif +#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ while ((err = ERR_get_error())) { wpa_printf(MSG_INFO, "%s: Clearing pending SSL error: %s", @@ -3371,10 +3833,9 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; if (engine_id && ca_cert_id) { - if (tls_connection_engine_ca_cert(tls_ctx, conn, - ca_cert_id)) + if (tls_connection_engine_ca_cert(data, conn, ca_cert_id)) return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED; - } else if (tls_connection_ca_cert(tls_ctx, conn, params->ca_cert, + } else if (tls_connection_ca_cert(data, conn, params->ca_cert, params->ca_cert_blob, params->ca_cert_blob_len, params->ca_path)) @@ -3392,7 +3853,7 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, wpa_printf(MSG_DEBUG, "TLS: Using private key from engine"); if (tls_connection_engine_private_key(conn)) return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED; - } else if (tls_connection_private_key(tls_ctx, conn, + } else if (tls_connection_private_key(data, conn, params->private_key, params->private_key_passwd, params->private_key_blob, @@ -3416,40 +3877,30 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; } -#ifdef SSL_OP_NO_TICKET - if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET) - SSL_set_options(conn->ssl, SSL_OP_NO_TICKET); -#ifdef SSL_clear_options - else - SSL_clear_options(conn->ssl, SSL_OP_NO_TICKET); -#endif /* SSL_clear_options */ -#endif /* SSL_OP_NO_TICKET */ - -#ifdef SSL_OP_NO_TLSv1_1 - if (params->flags & TLS_CONN_DISABLE_TLSv1_1) - SSL_set_options(conn->ssl, SSL_OP_NO_TLSv1_1); - else - SSL_clear_options(conn->ssl, SSL_OP_NO_TLSv1_1); -#endif /* SSL_OP_NO_TLSv1_1 */ -#ifdef SSL_OP_NO_TLSv1_2 - if (params->flags & TLS_CONN_DISABLE_TLSv1_2) - SSL_set_options(conn->ssl, SSL_OP_NO_TLSv1_2); - else - SSL_clear_options(conn->ssl, SSL_OP_NO_TLSv1_2); -#endif /* SSL_OP_NO_TLSv1_2 */ + tls_set_conn_flags(conn->ssl, params->flags); #ifdef HAVE_OCSP if (params->flags & TLS_CONN_REQUEST_OCSP) { - SSL_CTX *ssl_ctx = tls_ctx; + SSL_CTX *ssl_ctx = data->ssl; SSL_set_tlsext_status_type(conn->ssl, TLSEXT_STATUSTYPE_ocsp); SSL_CTX_set_tlsext_status_cb(ssl_ctx, ocsp_resp_cb); SSL_CTX_set_tlsext_status_arg(ssl_ctx, conn); } +#else /* HAVE_OCSP */ + if (params->flags & TLS_CONN_REQUIRE_OCSP) { + wpa_printf(MSG_INFO, + "OpenSSL: No OCSP support included - reject configuration"); + return -1; + } + if (params->flags & TLS_CONN_REQUEST_OCSP) { + wpa_printf(MSG_DEBUG, + "OpenSSL: No OCSP support included - allow optional OCSP case to continue"); + } #endif /* HAVE_OCSP */ conn->flags = params->flags; - tls_get_errors(tls_ctx); + tls_get_errors(data); return 0; } @@ -3458,7 +3909,8 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, int tls_global_set_params(void *tls_ctx, const struct tls_connection_params *params) { - SSL_CTX *ssl_ctx = tls_ctx; + struct tls_data *data = tls_ctx; + SSL_CTX *ssl_ctx = data->ssl; unsigned long err; while ((err = ERR_get_error())) { @@ -3466,19 +3918,12 @@ int tls_global_set_params(void *tls_ctx, __func__, ERR_error_string(err, NULL)); } - if (tls_global_ca_cert(ssl_ctx, params->ca_cert)) - return -1; - - if (tls_global_client_cert(ssl_ctx, params->client_cert)) - return -1; - - if (tls_global_private_key(ssl_ctx, params->private_key, - params->private_key_passwd)) - return -1; - - if (tls_global_dh(ssl_ctx, params->dh_file)) { - wpa_printf(MSG_INFO, "TLS: Failed to load DH file '%s'", - params->dh_file); + if (tls_global_ca_cert(data, params->ca_cert) || + tls_global_client_cert(data, params->client_cert) || + tls_global_private_key(data, params->private_key, + params->private_key_passwd) || + tls_global_dh(data, params->dh_file)) { + wpa_printf(MSG_INFO, "TLS: Failed to set global parameters"); return -1; } @@ -3514,49 +3959,6 @@ int tls_global_set_params(void *tls_ctx, } -int tls_connection_get_keyblock_size(void *tls_ctx, - struct tls_connection *conn) -{ - const EVP_CIPHER *c; - const EVP_MD *h; - int md_size; - - if (conn == NULL || conn->ssl == NULL || - conn->ssl->enc_read_ctx == NULL || - conn->ssl->enc_read_ctx->cipher == NULL || - conn->ssl->read_hash == NULL) - return -1; - - c = conn->ssl->enc_read_ctx->cipher; -#if OPENSSL_VERSION_NUMBER >= 0x00909000L - h = EVP_MD_CTX_md(conn->ssl->read_hash); -#else - h = conn->ssl->read_hash; -#endif - if (h) - md_size = EVP_MD_size(h); -#if OPENSSL_VERSION_NUMBER >= 0x10000000L - else if (conn->ssl->s3) - md_size = conn->ssl->s3->tmp.new_mac_secret_size; -#endif - else - return -1; - - wpa_printf(MSG_DEBUG, "OpenSSL: keyblock size: key_len=%d MD_size=%d " - "IV_len=%d", EVP_CIPHER_key_length(c), md_size, - EVP_CIPHER_iv_length(c)); - return 2 * (EVP_CIPHER_key_length(c) + - md_size + - EVP_CIPHER_iv_length(c)); -} - - -unsigned int tls_capabilities(void *tls_ctx) -{ - return 0; -} - - #if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) /* Pre-shared secred requires a patch to openssl, so this function is * commented out unless explicitly needed for EAP-FAST in order to be able to @@ -3575,6 +3977,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 (conn == NULL || conn->session_ticket_cb == NULL) return 0; @@ -3583,6 +3986,23 @@ static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len, conn->session_ticket_len, s->s3->client_random, s->s3->server_random, secret); +#else + unsigned char client_random[SSL3_RANDOM_SIZE]; + unsigned char server_random[SSL3_RANDOM_SIZE]; + + if (conn == NULL || conn->session_ticket_cb == NULL) + return 0; + + SSL_get_client_random(s, client_random, sizeof(client_random)); + SSL_get_server_random(s, server_random, sizeof(server_random)); + + ret = conn->session_ticket_cb(conn->session_ticket_cb_ctx, + conn->session_ticket, + conn->session_ticket_len, + client_random, + server_random, secret); +#endif + os_free(conn->session_ticket); conn->session_ticket = NULL; @@ -3656,3 +4076,70 @@ int tls_get_library_version(char *buf, size_t buf_len) OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION)); } + + +void tls_connection_set_success_data(struct tls_connection *conn, + struct wpabuf *data) +{ + SSL_SESSION *sess; + struct wpabuf *old; + + if (tls_ex_idx_session < 0) + goto fail; + sess = SSL_get_session(conn->ssl); + if (!sess) + goto fail; + old = SSL_SESSION_get_ex_data(sess, tls_ex_idx_session); + if (old) { + wpa_printf(MSG_DEBUG, "OpenSSL: Replacing old success data %p", + old); + wpabuf_free(old); + } + if (SSL_SESSION_set_ex_data(sess, tls_ex_idx_session, data) != 1) + goto fail; + + wpa_printf(MSG_DEBUG, "OpenSSL: Stored success data %p", data); + conn->success_data = 1; + return; + +fail: + wpa_printf(MSG_INFO, "OpenSSL: Failed to store success data"); + wpabuf_free(data); +} + + +void tls_connection_set_success_data_resumed(struct tls_connection *conn) +{ + wpa_printf(MSG_DEBUG, + "OpenSSL: Success data accepted for resumed session"); + conn->success_data = 1; +} + + +const struct wpabuf * +tls_connection_get_success_data(struct tls_connection *conn) +{ + SSL_SESSION *sess; + + if (tls_ex_idx_session < 0 || + !(sess = SSL_get_session(conn->ssl))) + return NULL; + return SSL_SESSION_get_ex_data(sess, tls_ex_idx_session); +} + + +void tls_connection_remove_session(struct tls_connection *conn) +{ + SSL_SESSION *sess; + + sess = SSL_get_session(conn->ssl); + if (!sess) + return; + + if (SSL_CTX_remove_session(conn->ssl_ctx, sess) != 1) + wpa_printf(MSG_DEBUG, + "OpenSSL: Session was not cached"); + else + wpa_printf(MSG_DEBUG, + "OpenSSL: Removed cached session to disable session resumption"); +} diff --git a/contrib/wpa/src/crypto/tls_schannel.c b/contrib/wpa/src/crypto/tls_schannel.c deleted file mode 100644 index 31a2c94..0000000 --- a/contrib/wpa/src/crypto/tls_schannel.c +++ /dev/null @@ -1,763 +0,0 @@ -/* - * SSL/TLS interface functions for Microsoft Schannel - * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi> - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -/* - * FIX: Go through all SSPI functions and verify what needs to be freed - * FIX: session resumption - * TODO: add support for server cert chain validation - * TODO: add support for CA cert validation - * TODO: add support for EAP-TLS (client cert/key conf) - */ - -#include "includes.h" -#include <windows.h> -#include <wincrypt.h> -#include <schannel.h> -#define SECURITY_WIN32 -#include <security.h> -#include <sspi.h> - -#include "common.h" -#include "tls.h" - - -struct tls_global { - HMODULE hsecurity; - PSecurityFunctionTable sspi; - HCERTSTORE my_cert_store; -}; - -struct tls_connection { - int established, start; - int failed, read_alerts, write_alerts; - - SCHANNEL_CRED schannel_cred; - CredHandle creds; - CtxtHandle context; - - u8 eap_tls_prf[128]; - int eap_tls_prf_set; -}; - - -static int schannel_load_lib(struct tls_global *global) -{ - INIT_SECURITY_INTERFACE pInitSecurityInterface; - - global->hsecurity = LoadLibrary(TEXT("Secur32.dll")); - if (global->hsecurity == NULL) { - wpa_printf(MSG_ERROR, "%s: Could not load Secur32.dll - 0x%x", - __func__, (unsigned int) GetLastError()); - return -1; - } - - pInitSecurityInterface = (INIT_SECURITY_INTERFACE) GetProcAddress( - global->hsecurity, "InitSecurityInterfaceA"); - if (pInitSecurityInterface == NULL) { - wpa_printf(MSG_ERROR, "%s: Could not find " - "InitSecurityInterfaceA from Secur32.dll", - __func__); - FreeLibrary(global->hsecurity); - global->hsecurity = NULL; - return -1; - } - - global->sspi = pInitSecurityInterface(); - if (global->sspi == NULL) { - wpa_printf(MSG_ERROR, "%s: Could not read security " - "interface - 0x%x", - __func__, (unsigned int) GetLastError()); - FreeLibrary(global->hsecurity); - global->hsecurity = NULL; - return -1; - } - - return 0; -} - - -void * tls_init(const struct tls_config *conf) -{ - struct tls_global *global; - - global = os_zalloc(sizeof(*global)); - if (global == NULL) - return NULL; - if (schannel_load_lib(global)) { - os_free(global); - return NULL; - } - return global; -} - - -void tls_deinit(void *ssl_ctx) -{ - struct tls_global *global = ssl_ctx; - - if (global->my_cert_store) - CertCloseStore(global->my_cert_store, 0); - FreeLibrary(global->hsecurity); - os_free(global); -} - - -int tls_get_errors(void *ssl_ctx) -{ - return 0; -} - - -struct tls_connection * tls_connection_init(void *ssl_ctx) -{ - struct tls_connection *conn; - - conn = os_zalloc(sizeof(*conn)); - if (conn == NULL) - return NULL; - conn->start = 1; - - return conn; -} - - -void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) -{ - if (conn == NULL) - return; - - os_free(conn); -} - - -int tls_connection_established(void *ssl_ctx, struct tls_connection *conn) -{ - return conn ? conn->established : 0; -} - - -int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) -{ - struct tls_global *global = ssl_ctx; - if (conn == NULL) - return -1; - - conn->eap_tls_prf_set = 0; - conn->established = conn->failed = 0; - conn->read_alerts = conn->write_alerts = 0; - global->sspi->DeleteSecurityContext(&conn->context); - /* FIX: what else needs to be reseted? */ - - return 0; -} - - -int tls_global_set_params(void *tls_ctx, - const struct tls_connection_params *params) -{ - return -1; -} - - -int tls_global_set_verify(void *ssl_ctx, int check_crl) -{ - return -1; -} - - -int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, - int verify_peer) -{ - return -1; -} - - -int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, - struct tls_keys *keys) -{ - /* Schannel does not export master secret or client/server random. */ - return -1; -} - - -int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, - const char *label, int server_random_first, - u8 *out, size_t out_len) -{ - /* - * Cannot get master_key from Schannel, but EapKeyBlock can be used to - * generate session keys for EAP-TLS and EAP-PEAPv0. EAP-PEAPv2 and - * EAP-TTLS cannot use this, though, since they are using different - * labels. The only option could be to implement TLSv1 completely here - * and just use Schannel or CryptoAPI for low-level crypto - * functionality.. - */ - - if (conn == NULL || !conn->eap_tls_prf_set || server_random_first || - os_strcmp(label, "client EAP encryption") != 0 || - out_len > sizeof(conn->eap_tls_prf)) - return -1; - - os_memcpy(out, conn->eap_tls_prf, out_len); - - return 0; -} - - -static struct wpabuf * tls_conn_hs_clienthello(struct tls_global *global, - struct tls_connection *conn) -{ - DWORD sspi_flags, sspi_flags_out; - SecBufferDesc outbuf; - SecBuffer outbufs[1]; - SECURITY_STATUS status; - TimeStamp ts_expiry; - - sspi_flags = ISC_REQ_REPLAY_DETECT | - ISC_REQ_CONFIDENTIALITY | - ISC_RET_EXTENDED_ERROR | - ISC_REQ_ALLOCATE_MEMORY | - ISC_REQ_MANUAL_CRED_VALIDATION; - - wpa_printf(MSG_DEBUG, "%s: Generating ClientHello", __func__); - - outbufs[0].pvBuffer = NULL; - outbufs[0].BufferType = SECBUFFER_TOKEN; - outbufs[0].cbBuffer = 0; - - outbuf.cBuffers = 1; - outbuf.pBuffers = outbufs; - outbuf.ulVersion = SECBUFFER_VERSION; - -#ifdef UNICODE - status = global->sspi->InitializeSecurityContextW( - &conn->creds, NULL, NULL /* server name */, sspi_flags, 0, - SECURITY_NATIVE_DREP, NULL, 0, &conn->context, - &outbuf, &sspi_flags_out, &ts_expiry); -#else /* UNICODE */ - status = global->sspi->InitializeSecurityContextA( - &conn->creds, NULL, NULL /* server name */, sspi_flags, 0, - SECURITY_NATIVE_DREP, NULL, 0, &conn->context, - &outbuf, &sspi_flags_out, &ts_expiry); -#endif /* UNICODE */ - if (status != SEC_I_CONTINUE_NEEDED) { - wpa_printf(MSG_ERROR, "%s: InitializeSecurityContextA " - "failed - 0x%x", - __func__, (unsigned int) status); - return NULL; - } - - if (outbufs[0].cbBuffer != 0 && outbufs[0].pvBuffer) { - struct wpabuf *buf; - wpa_hexdump(MSG_MSGDUMP, "SChannel - ClientHello", - outbufs[0].pvBuffer, outbufs[0].cbBuffer); - conn->start = 0; - buf = wpabuf_alloc_copy(outbufs[0].pvBuffer, - outbufs[0].cbBuffer); - if (buf == NULL) - return NULL; - global->sspi->FreeContextBuffer(outbufs[0].pvBuffer); - return buf; - } - - wpa_printf(MSG_ERROR, "SChannel: Failed to generate ClientHello"); - - return NULL; -} - - -#ifndef SECPKG_ATTR_EAP_KEY_BLOCK -#define SECPKG_ATTR_EAP_KEY_BLOCK 0x5b - -typedef struct _SecPkgContext_EapKeyBlock { - BYTE rgbKeys[128]; - BYTE rgbIVs[64]; -} SecPkgContext_EapKeyBlock, *PSecPkgContext_EapKeyBlock; -#endif /* !SECPKG_ATTR_EAP_KEY_BLOCK */ - -static int tls_get_eap(struct tls_global *global, struct tls_connection *conn) -{ - SECURITY_STATUS status; - SecPkgContext_EapKeyBlock kb; - - /* Note: Windows NT and Windows Me/98/95 do not support getting - * EapKeyBlock */ - - status = global->sspi->QueryContextAttributes( - &conn->context, SECPKG_ATTR_EAP_KEY_BLOCK, &kb); - if (status != SEC_E_OK) { - wpa_printf(MSG_DEBUG, "%s: QueryContextAttributes(" - "SECPKG_ATTR_EAP_KEY_BLOCK) failed (%d)", - __func__, (int) status); - return -1; - } - - wpa_hexdump_key(MSG_MSGDUMP, "Schannel - EapKeyBlock - rgbKeys", - kb.rgbKeys, sizeof(kb.rgbKeys)); - wpa_hexdump_key(MSG_MSGDUMP, "Schannel - EapKeyBlock - rgbIVs", - kb.rgbIVs, sizeof(kb.rgbIVs)); - - os_memcpy(conn->eap_tls_prf, kb.rgbKeys, sizeof(kb.rgbKeys)); - conn->eap_tls_prf_set = 1; - return 0; -} - - -struct wpabuf * tls_connection_handshake(void *tls_ctx, - struct tls_connection *conn, - const struct wpabuf *in_data, - struct wpabuf **appl_data) -{ - struct tls_global *global = tls_ctx; - DWORD sspi_flags, sspi_flags_out; - SecBufferDesc inbuf, outbuf; - SecBuffer inbufs[2], outbufs[1]; - SECURITY_STATUS status; - TimeStamp ts_expiry; - struct wpabuf *out_buf = NULL; - - if (appl_data) - *appl_data = NULL; - - if (conn->start) - return tls_conn_hs_clienthello(global, conn); - - wpa_printf(MSG_DEBUG, "SChannel: %d bytes handshake data to process", - (int) wpabuf_len(in_data)); - - sspi_flags = ISC_REQ_REPLAY_DETECT | - ISC_REQ_CONFIDENTIALITY | - ISC_RET_EXTENDED_ERROR | - ISC_REQ_ALLOCATE_MEMORY | - ISC_REQ_MANUAL_CRED_VALIDATION; - - /* Input buffer for Schannel */ - inbufs[0].pvBuffer = (u8 *) wpabuf_head(in_data); - inbufs[0].cbBuffer = wpabuf_len(in_data); - inbufs[0].BufferType = SECBUFFER_TOKEN; - - /* Place for leftover data from Schannel */ - inbufs[1].pvBuffer = NULL; - inbufs[1].cbBuffer = 0; - inbufs[1].BufferType = SECBUFFER_EMPTY; - - inbuf.cBuffers = 2; - inbuf.pBuffers = inbufs; - inbuf.ulVersion = SECBUFFER_VERSION; - - /* Output buffer for Schannel */ - outbufs[0].pvBuffer = NULL; - outbufs[0].cbBuffer = 0; - outbufs[0].BufferType = SECBUFFER_TOKEN; - - outbuf.cBuffers = 1; - outbuf.pBuffers = outbufs; - outbuf.ulVersion = SECBUFFER_VERSION; - -#ifdef UNICODE - status = global->sspi->InitializeSecurityContextW( - &conn->creds, &conn->context, NULL, sspi_flags, 0, - SECURITY_NATIVE_DREP, &inbuf, 0, NULL, - &outbuf, &sspi_flags_out, &ts_expiry); -#else /* UNICODE */ - status = global->sspi->InitializeSecurityContextA( - &conn->creds, &conn->context, NULL, sspi_flags, 0, - SECURITY_NATIVE_DREP, &inbuf, 0, NULL, - &outbuf, &sspi_flags_out, &ts_expiry); -#endif /* UNICODE */ - - wpa_printf(MSG_MSGDUMP, "Schannel: InitializeSecurityContext -> " - "status=%d inlen[0]=%d intype[0]=%d inlen[1]=%d " - "intype[1]=%d outlen[0]=%d", - (int) status, (int) inbufs[0].cbBuffer, - (int) inbufs[0].BufferType, (int) inbufs[1].cbBuffer, - (int) inbufs[1].BufferType, - (int) outbufs[0].cbBuffer); - if (status == SEC_E_OK || status == SEC_I_CONTINUE_NEEDED || - (FAILED(status) && (sspi_flags_out & ISC_RET_EXTENDED_ERROR))) { - if (outbufs[0].cbBuffer != 0 && outbufs[0].pvBuffer) { - wpa_hexdump(MSG_MSGDUMP, "SChannel - output", - outbufs[0].pvBuffer, outbufs[0].cbBuffer); - out_buf = wpabuf_alloc_copy(outbufs[0].pvBuffer, - outbufs[0].cbBuffer); - global->sspi->FreeContextBuffer(outbufs[0].pvBuffer); - outbufs[0].pvBuffer = NULL; - if (out_buf == NULL) - return NULL; - } - } - - switch (status) { - case SEC_E_INCOMPLETE_MESSAGE: - wpa_printf(MSG_DEBUG, "Schannel: SEC_E_INCOMPLETE_MESSAGE"); - break; - case SEC_I_CONTINUE_NEEDED: - wpa_printf(MSG_DEBUG, "Schannel: SEC_I_CONTINUE_NEEDED"); - break; - case SEC_E_OK: - /* TODO: verify server certificate chain */ - wpa_printf(MSG_DEBUG, "Schannel: SEC_E_OK - Handshake " - "completed successfully"); - conn->established = 1; - tls_get_eap(global, conn); - - /* Need to return something to get final TLS ACK. */ - if (out_buf == NULL) - out_buf = wpabuf_alloc(0); - - if (inbufs[1].BufferType == SECBUFFER_EXTRA) { - wpa_hexdump(MSG_MSGDUMP, "SChannel - Encrypted " - "application data", - inbufs[1].pvBuffer, inbufs[1].cbBuffer); - if (appl_data) { - *appl_data = wpabuf_alloc_copy( - outbufs[1].pvBuffer, - outbufs[1].cbBuffer); - } - global->sspi->FreeContextBuffer(inbufs[1].pvBuffer); - inbufs[1].pvBuffer = NULL; - } - break; - case SEC_I_INCOMPLETE_CREDENTIALS: - wpa_printf(MSG_DEBUG, - "Schannel: SEC_I_INCOMPLETE_CREDENTIALS"); - break; - case SEC_E_WRONG_PRINCIPAL: - wpa_printf(MSG_DEBUG, "Schannel: SEC_E_WRONG_PRINCIPAL"); - break; - case SEC_E_INTERNAL_ERROR: - wpa_printf(MSG_DEBUG, "Schannel: SEC_E_INTERNAL_ERROR"); - break; - } - - if (FAILED(status)) { - wpa_printf(MSG_DEBUG, "Schannel: Handshake failed " - "(out_buf=%p)", out_buf); - conn->failed++; - global->sspi->DeleteSecurityContext(&conn->context); - return out_buf; - } - - if (inbufs[1].BufferType == SECBUFFER_EXTRA) { - /* TODO: Can this happen? What to do with this data? */ - wpa_hexdump(MSG_MSGDUMP, "SChannel - Leftover data", - inbufs[1].pvBuffer, inbufs[1].cbBuffer); - global->sspi->FreeContextBuffer(inbufs[1].pvBuffer); - inbufs[1].pvBuffer = NULL; - } - - return out_buf; -} - - -struct wpabuf * tls_connection_server_handshake(void *tls_ctx, - struct tls_connection *conn, - const struct wpabuf *in_data, - struct wpabuf **appl_data) -{ - return NULL; -} - - -struct wpabuf * tls_connection_encrypt(void *tls_ctx, - struct tls_connection *conn, - const struct wpabuf *in_data) -{ - struct tls_global *global = tls_ctx; - SECURITY_STATUS status; - SecBufferDesc buf; - SecBuffer bufs[4]; - SecPkgContext_StreamSizes sizes; - int i; - struct wpabuf *out; - - status = global->sspi->QueryContextAttributes(&conn->context, - SECPKG_ATTR_STREAM_SIZES, - &sizes); - if (status != SEC_E_OK) { - wpa_printf(MSG_DEBUG, "%s: QueryContextAttributes failed", - __func__); - return NULL; - } - wpa_printf(MSG_DEBUG, "%s: Stream sizes: header=%u trailer=%u", - __func__, - (unsigned int) sizes.cbHeader, - (unsigned int) sizes.cbTrailer); - - out = wpabuf_alloc(sizes.cbHeader + wpabuf_len(in_data) + - sizes.cbTrailer); - - os_memset(&bufs, 0, sizeof(bufs)); - bufs[0].pvBuffer = wpabuf_put(out, sizes.cbHeader); - bufs[0].cbBuffer = sizes.cbHeader; - bufs[0].BufferType = SECBUFFER_STREAM_HEADER; - - bufs[1].pvBuffer = wpabuf_put(out, 0); - wpabuf_put_buf(out, in_data); - bufs[1].cbBuffer = wpabuf_len(in_data); - bufs[1].BufferType = SECBUFFER_DATA; - - bufs[2].pvBuffer = wpabuf_put(out, sizes.cbTrailer); - bufs[2].cbBuffer = sizes.cbTrailer; - bufs[2].BufferType = SECBUFFER_STREAM_TRAILER; - - buf.ulVersion = SECBUFFER_VERSION; - buf.cBuffers = 3; - buf.pBuffers = bufs; - - status = global->sspi->EncryptMessage(&conn->context, 0, &buf, 0); - - wpa_printf(MSG_MSGDUMP, "Schannel: EncryptMessage -> " - "status=%d len[0]=%d type[0]=%d len[1]=%d type[1]=%d " - "len[2]=%d type[2]=%d", - (int) status, - (int) bufs[0].cbBuffer, (int) bufs[0].BufferType, - (int) bufs[1].cbBuffer, (int) bufs[1].BufferType, - (int) bufs[2].cbBuffer, (int) bufs[2].BufferType); - wpa_printf(MSG_MSGDUMP, "Schannel: EncryptMessage pointers: " - "out_data=%p bufs %p %p %p", - wpabuf_head(out), bufs[0].pvBuffer, bufs[1].pvBuffer, - bufs[2].pvBuffer); - - for (i = 0; i < 3; i++) { - if (bufs[i].pvBuffer && bufs[i].BufferType != SECBUFFER_EMPTY) - { - wpa_hexdump(MSG_MSGDUMP, "SChannel: bufs", - bufs[i].pvBuffer, bufs[i].cbBuffer); - } - } - - if (status == SEC_E_OK) { - wpa_printf(MSG_DEBUG, "%s: SEC_E_OK", __func__); - wpa_hexdump_buf_key(MSG_MSGDUMP, "Schannel: Encrypted data " - "from EncryptMessage", out); - return out; - } - - wpa_printf(MSG_DEBUG, "%s: Failed - status=%d", - __func__, (int) status); - wpabuf_free(out); - return NULL; -} - - -struct wpabuf * tls_connection_decrypt(void *tls_ctx, - struct tls_connection *conn, - const struct wpabuf *in_data) -{ - struct tls_global *global = tls_ctx; - SECURITY_STATUS status; - SecBufferDesc buf; - SecBuffer bufs[4]; - int i; - struct wpabuf *out, *tmp; - - wpa_hexdump_buf(MSG_MSGDUMP, - "Schannel: Encrypted data to DecryptMessage", in_data); - os_memset(&bufs, 0, sizeof(bufs)); - tmp = wpabuf_dup(in_data); - if (tmp == NULL) - return NULL; - bufs[0].pvBuffer = wpabuf_mhead(tmp); - bufs[0].cbBuffer = wpabuf_len(in_data); - bufs[0].BufferType = SECBUFFER_DATA; - - bufs[1].BufferType = SECBUFFER_EMPTY; - bufs[2].BufferType = SECBUFFER_EMPTY; - bufs[3].BufferType = SECBUFFER_EMPTY; - - buf.ulVersion = SECBUFFER_VERSION; - buf.cBuffers = 4; - buf.pBuffers = bufs; - - status = global->sspi->DecryptMessage(&conn->context, &buf, 0, - NULL); - wpa_printf(MSG_MSGDUMP, "Schannel: DecryptMessage -> " - "status=%d len[0]=%d type[0]=%d len[1]=%d type[1]=%d " - "len[2]=%d type[2]=%d len[3]=%d type[3]=%d", - (int) status, - (int) bufs[0].cbBuffer, (int) bufs[0].BufferType, - (int) bufs[1].cbBuffer, (int) bufs[1].BufferType, - (int) bufs[2].cbBuffer, (int) bufs[2].BufferType, - (int) bufs[3].cbBuffer, (int) bufs[3].BufferType); - wpa_printf(MSG_MSGDUMP, "Schannel: DecryptMessage pointers: " - "out_data=%p bufs %p %p %p %p", - wpabuf_head(tmp), bufs[0].pvBuffer, bufs[1].pvBuffer, - bufs[2].pvBuffer, bufs[3].pvBuffer); - - switch (status) { - case SEC_E_INCOMPLETE_MESSAGE: - wpa_printf(MSG_DEBUG, "%s: SEC_E_INCOMPLETE_MESSAGE", - __func__); - break; - case SEC_E_OK: - wpa_printf(MSG_DEBUG, "%s: SEC_E_OK", __func__); - for (i = 0; i < 4; i++) { - if (bufs[i].BufferType == SECBUFFER_DATA) - break; - } - if (i == 4) { - wpa_printf(MSG_DEBUG, "%s: No output data from " - "DecryptMessage", __func__); - wpabuf_free(tmp); - return NULL; - } - wpa_hexdump_key(MSG_MSGDUMP, "Schannel: Decrypted data from " - "DecryptMessage", - bufs[i].pvBuffer, bufs[i].cbBuffer); - out = wpabuf_alloc_copy(bufs[i].pvBuffer, bufs[i].cbBuffer); - wpabuf_free(tmp); - return out; - } - - wpa_printf(MSG_DEBUG, "%s: Failed - status=%d", - __func__, (int) status); - wpabuf_free(tmp); - return NULL; -} - - -int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn) -{ - return 0; -} - - -int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, - u8 *ciphers) -{ - return -1; -} - - -int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn, - char *buf, size_t buflen) -{ - return -1; -} - - -int tls_connection_enable_workaround(void *ssl_ctx, - struct tls_connection *conn) -{ - return 0; -} - - -int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn, - int ext_type, const u8 *data, - size_t data_len) -{ - return -1; -} - - -int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn) -{ - if (conn == NULL) - return -1; - return conn->failed; -} - - -int tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn) -{ - if (conn == NULL) - return -1; - return conn->read_alerts; -} - - -int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn) -{ - if (conn == NULL) - return -1; - return conn->write_alerts; -} - - -int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, - const struct tls_connection_params *params) -{ - struct tls_global *global = tls_ctx; - ALG_ID algs[1]; - SECURITY_STATUS status; - TimeStamp ts_expiry; - - if (conn == NULL) - return -1; - - if (params->subject_match) { - wpa_printf(MSG_INFO, "TLS: subject_match not supported"); - return -1; - } - - if (params->altsubject_match) { - wpa_printf(MSG_INFO, "TLS: altsubject_match not supported"); - return -1; - } - - if (params->suffix_match) { - wpa_printf(MSG_INFO, "TLS: suffix_match not supported"); - return -1; - } - - if (params->domain_match) { - wpa_printf(MSG_INFO, "TLS: domain_match not supported"); - return -1; - } - - if (params->openssl_ciphers) { - wpa_printf(MSG_INFO, "GnuTLS: openssl_ciphers not supported"); - return -1; - } - - if (global->my_cert_store == NULL && - (global->my_cert_store = CertOpenSystemStore(0, TEXT("MY"))) == - NULL) { - wpa_printf(MSG_ERROR, "%s: CertOpenSystemStore failed - 0x%x", - __func__, (unsigned int) GetLastError()); - return -1; - } - - os_memset(&conn->schannel_cred, 0, sizeof(conn->schannel_cred)); - conn->schannel_cred.dwVersion = SCHANNEL_CRED_VERSION; - conn->schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1; - algs[0] = CALG_RSA_KEYX; - conn->schannel_cred.cSupportedAlgs = 1; - conn->schannel_cred.palgSupportedAlgs = algs; - conn->schannel_cred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS; -#ifdef UNICODE - status = global->sspi->AcquireCredentialsHandleW( - NULL, UNISP_NAME_W, SECPKG_CRED_OUTBOUND, NULL, - &conn->schannel_cred, NULL, NULL, &conn->creds, &ts_expiry); -#else /* UNICODE */ - status = global->sspi->AcquireCredentialsHandleA( - NULL, UNISP_NAME_A, SECPKG_CRED_OUTBOUND, NULL, - &conn->schannel_cred, NULL, NULL, &conn->creds, &ts_expiry); -#endif /* UNICODE */ - if (status != SEC_E_OK) { - wpa_printf(MSG_DEBUG, "%s: AcquireCredentialsHandleA failed - " - "0x%x", __func__, (unsigned int) status); - return -1; - } - - return 0; -} - - -unsigned int tls_capabilities(void *tls_ctx) -{ - return 0; -} - - -int tls_get_library_version(char *buf, size_t buf_len) -{ - return os_snprintf(buf, buf_len, "schannel"); -} diff --git a/contrib/wpa/src/drivers/driver.h b/contrib/wpa/src/drivers/driver.h index 03bd1a7..3cdab5a 100644 --- a/contrib/wpa/src/drivers/driver.h +++ b/contrib/wpa/src/drivers/driver.h @@ -20,6 +20,7 @@ #define WPA_SUPPLICANT_DRIVER_VERSION 4 #include "common/defs.h" +#include "common/ieee802_11_defs.h" #include "utils/list.h" #define HOSTAPD_CHAN_DISABLED 0x00000001 @@ -341,7 +342,7 @@ struct wpa_driver_scan_params { * is not needed anymore. */ struct wpa_driver_scan_filter { - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; size_t ssid_len; } *filter_ssids; @@ -1211,6 +1212,8 @@ struct wpa_driver_capa { #define WPA_DRIVER_FLAGS_HT_IBSS 0x0000001000000000ULL /** Driver supports IBSS with VHT datarates */ #define WPA_DRIVER_FLAGS_VHT_IBSS 0x0000002000000000ULL +/** Driver supports automatic band selection */ +#define WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY 0x0000004000000000ULL u64 flags; #define WPA_DRIVER_SMPS_MODE_STATIC 0x00000001 @@ -1294,6 +1297,13 @@ struct wpa_driver_capa { */ #define WPA_DRIVER_FLAGS_TX_POWER_INSERTION 0x00000008 u32 rrm_flags; + + /* Driver concurrency capabilities */ + unsigned int conc_capab; + /* Maximum number of concurrent channels on 2.4 GHz */ + unsigned int max_conc_chan_2_4; + /* Maximum number of concurrent channels on 5 GHz */ + unsigned int max_conc_chan_5_0; }; @@ -1394,6 +1404,16 @@ enum wpa_driver_if_type { * WPA_IF_MESH - Mesh interface */ WPA_IF_MESH, + + /* + * WPA_IF_TDLS - TDLS offchannel interface (used for pref freq only) + */ + WPA_IF_TDLS, + + /* + * WPA_IF_IBSS - IBSS interface (used for pref freq only) + */ + WPA_IF_IBSS, }; struct wpa_init_params { @@ -1477,6 +1497,7 @@ struct wpa_signal_info { int above_threshold; int current_signal; int avg_signal; + int avg_beacon_signal; int current_noise; int current_txrate; enum chan_width chanwidth; @@ -1576,6 +1597,7 @@ enum drv_br_port_attr { enum drv_br_net_param { DRV_BR_NET_PARAM_GARP_ACCEPT, + DRV_BR_MULTICAST_SNOOPING, }; struct drv_acs_params { @@ -1587,6 +1609,17 @@ struct drv_acs_params { /* Indicates whether HT40 is enabled */ int ht40_enabled; + + /* Indicates whether VHT is enabled */ + int vht_enabled; + + /* Configured ACS channel width */ + u16 ch_width; + + /* ACS channel list info */ + unsigned int ch_list_len; + const u8 *ch_list; + const int *freq_list; }; @@ -1925,10 +1958,12 @@ struct wpa_driver_ops { * @data: IEEE 802.11 management frame with IEEE 802.11 header * @data_len: Size of the management frame * @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 * Returns: 0 on success, -1 on failure */ int (*send_mlme)(void *priv, const u8 *data, size_t data_len, - int noack); + int noack, unsigned int freq); /** * update_ft_ies - Update FT (IEEE 802.11r) IEs @@ -2332,7 +2367,8 @@ struct wpa_driver_ops { * Returns: 0 on success, -1 on failure */ int (*sta_set_flags)(void *priv, const u8 *addr, - int total_flags, int flags_or, int flags_and); + unsigned int total_flags, unsigned int flags_or, + unsigned int flags_and); /** * set_tx_queue_params - Set TX queue parameters @@ -2656,18 +2692,6 @@ struct wpa_driver_ops { int encrypt); /** - * shared_freq - Get operating frequency of shared interface(s) - * @priv: Private driver interface data - * Returns: Operating frequency in MHz, 0 if no shared operation in - * use, or -1 on failure - * - * This command can be used to request the current operating frequency - * of any virtual interface that shares the same radio to provide - * information for channel selection for other virtual interfaces. - */ - int (*shared_freq)(void *priv); - - /** * get_noa - Get current Notice of Absence attribute payload * @priv: Private driver interface data * @buf: Buffer for returning NoA @@ -3381,6 +3405,40 @@ struct wpa_driver_ops { * indicates support for such offloading (WPA_DRIVER_FLAGS_ACS_OFFLOAD). */ int (*do_acs)(void *priv, struct drv_acs_params *params); + + /** + * set_band - Notify driver of band selection + * @priv: Private driver interface data + * @band: The selected band(s) + * Returns 0 on success, -1 on failure + */ + int (*set_band)(void *priv, enum set_band band); + + /** + * get_pref_freq_list - Get preferred frequency list for an interface + * @priv: Private driver interface data + * @if_type: Interface type + * @num: Number of channels + * @freq_list: Preferred channel frequency list encoded in MHz values + * Returns 0 on success, -1 on failure + * + * This command can be used to query the preferred frequency list from + * the driver specific to a particular interface type. + */ + int (*get_pref_freq_list)(void *priv, enum wpa_driver_if_type if_type, + unsigned int *num, unsigned int *freq_list); + + /** + * set_prob_oper_freq - Indicate probable P2P operating channel + * @priv: Private driver interface data + * @freq: Channel frequency in MHz + * Returns 0 on success, -1 on failure + * + * This command can be used to inform the driver of the operating + * frequency that an ongoing P2P group formation is likely to come up + * on. Local device is assuming P2P Client role. + */ + int (*set_prob_oper_freq)(void *priv, unsigned int freq); }; @@ -4557,10 +4615,20 @@ union wpa_event_data { * struct acs_selected_channels - Data for EVENT_ACS_CHANNEL_SELECTED * @pri_channel: Selected primary channel * @sec_channel: Selected secondary channel + * @vht_seg0_center_ch: VHT mode Segment0 center channel + * @vht_seg1_center_ch: VHT mode Segment1 center channel + * @ch_width: Selected Channel width by driver. Driver may choose to + * change hostapd configured ACS channel width due driver internal + * channel restrictions. + * hw_mode: Selected band (used with hw_mode=any) */ struct acs_selected_channels { u8 pri_channel; u8 sec_channel; + u8 vht_seg0_center_ch; + u8 vht_seg1_center_ch; + u16 ch_width; + enum hostapd_hw_mode hw_mode; } acs_selected_channels; }; @@ -4631,6 +4699,6 @@ wpa_get_wowlan_triggers(const char *wowlan_triggers, const struct wpa_driver_capa *capa); /* NULL terminated array of linked in driver wrappers */ -extern struct wpa_driver_ops *wpa_drivers[]; +extern const struct wpa_driver_ops *const wpa_drivers[]; #endif /* DRIVER_H */ diff --git a/contrib/wpa/src/drivers/driver_bsd.c b/contrib/wpa/src/drivers/driver_bsd.c index 63b0e19..b57dd03 100644 --- a/contrib/wpa/src/drivers/driver_bsd.c +++ b/contrib/wpa/src/drivers/driver_bsd.c @@ -860,8 +860,7 @@ bad: if (drv->sock >= 0) close(drv->sock); os_free(drv->event_buf); - if (drv != NULL) - os_free(drv); + os_free(drv); return NULL; } @@ -894,7 +893,8 @@ bsd_commit(void *priv) static int bsd_set_sta_authorized(void *priv, const u8 *addr, - int total_flags, int flags_or, int flags_and) + unsigned int total_flags, unsigned int flags_or, + unsigned int flags_and) { int authorized = -1; diff --git a/contrib/wpa/src/drivers/driver_ndis.c b/contrib/wpa/src/drivers/driver_ndis.c index bf19a58..2f5ade6 100644 --- a/contrib/wpa/src/drivers/driver_ndis.c +++ b/contrib/wpa/src/drivers/driver_ndis.c @@ -710,11 +710,11 @@ static int wpa_driver_ndis_radio_off(struct wpa_driver_ndis_data *drv) /* Disconnect by setting SSID to random (i.e., likely not used). */ static int wpa_driver_ndis_disconnect(struct wpa_driver_ndis_data *drv) { - char ssid[32]; + char ssid[SSID_MAX_LEN]; int i; - for (i = 0; i < 32; i++) + for (i = 0; i < SSID_MAX_LEN; i++) ssid[i] = rand() & 0xff; - return wpa_driver_ndis_set_ssid(drv, (u8 *) ssid, 32); + return wpa_driver_ndis_set_ssid(drv, (u8 *) ssid, SSID_MAX_LEN); } @@ -807,7 +807,7 @@ static struct wpa_scan_res * wpa_driver_ndis_add_scan_ssid( if (wpa_scan_get_ie(r, WLAN_EID_SSID)) return r; /* SSID IE already present */ - if (ssid->SsidLength == 0 || ssid->SsidLength > 32) + if (ssid->SsidLength == 0 || ssid->SsidLength > SSID_MAX_LEN) return r; /* No valid SSID inside scan data */ nr = os_realloc(r, sizeof(*r) + r->ie_len + 2 + ssid->SsidLength); diff --git a/contrib/wpa/src/drivers/driver_nl80211.h b/contrib/wpa/src/drivers/driver_nl80211.h index 802589a..5c21e0f 100644 --- a/contrib/wpa/src/drivers/driver_nl80211.h +++ b/contrib/wpa/src/drivers/driver_nl80211.h @@ -110,7 +110,7 @@ struct wpa_driver_nl80211_data { u8 bssid[ETH_ALEN]; u8 prev_bssid[ETH_ALEN]; int associated; - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; size_t ssid_len; enum nl80211_iftype nlmode; enum nl80211_iftype ap_scan_as_station; @@ -145,6 +145,9 @@ struct wpa_driver_nl80211_data { unsigned int get_features_vendor_cmd_avail:1; unsigned int set_rekey_offload:1; unsigned int p2p_go_ctwindow_supported:1; + unsigned int setband_vendor_cmd_avail:1; + unsigned int get_pref_freq_list:1; + unsigned int set_prob_oper_freq:1; u64 remain_on_chan_cookie; u64 send_action_cookie; @@ -169,7 +172,7 @@ struct wpa_driver_nl80211_data { /* From failed authentication command */ int auth_freq; u8 auth_bssid_[ETH_ALEN]; - u8 auth_ssid[32]; + u8 auth_ssid[SSID_MAX_LEN]; size_t auth_ssid_len; int auth_alg; u8 *auth_ie; @@ -232,7 +235,6 @@ int process_bss_event(struct nl_msg *msg, void *arg); #ifdef ANDROID int android_nl_socket_set_nonblocking(struct nl_handle *handle); -int android_genl_ctrl_resolve(struct nl_handle *handle, const char *name); int android_pno_start(struct i802_bss *bss, struct wpa_driver_scan_params *params); int android_pno_stop(struct i802_bss *bss); @@ -270,5 +272,6 @@ int wpa_driver_nl80211_sched_scan(void *priv, 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); #endif /* DRIVER_NL80211_H */ diff --git a/contrib/wpa/src/drivers/driver_nl80211_android.c b/contrib/wpa/src/drivers/driver_nl80211_android.c index 3cc9a65..ba47888 100644 --- a/contrib/wpa/src/drivers/driver_nl80211_android.c +++ b/contrib/wpa/src/drivers/driver_nl80211_android.c @@ -151,7 +151,7 @@ int android_pno_stop(struct i802_bss *bss) #ifdef ANDROID_P2P -#ifdef ANDROID_P2P_STUB +#ifdef ANDROID_LIB_STUB int wpa_driver_set_p2p_noa(void *priv, u8 count, int start, int duration) { @@ -178,7 +178,7 @@ int wpa_driver_set_ap_wps_p2p_ie(void *priv, const struct wpabuf *beacon, return 0; } -#endif /* ANDROID_P2P_STUB */ +#endif /* ANDROID_LIB_STUB */ #endif /* ANDROID_P2P */ @@ -188,33 +188,3 @@ int android_nl_socket_set_nonblocking(struct nl_handle *handle) } -int android_genl_ctrl_resolve(struct nl_handle *handle, const char *name) -{ - /* - * Android ICS has very minimal genl_ctrl_resolve() implementation, so - * need to work around that. - */ - struct nl_cache *cache = NULL; - struct genl_family *nl80211 = NULL; - int id = -1; - - if (genl_ctrl_alloc_cache(handle, &cache) < 0) { - wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic " - "netlink cache"); - goto fail; - } - - nl80211 = genl_ctrl_search_by_name(cache, name); - if (nl80211 == NULL) - goto fail; - - id = genl_family_get_id(nl80211); - -fail: - if (nl80211) - genl_family_put(nl80211); - if (cache) - nl_cache_free(cache); - - return id; -} diff --git a/contrib/wpa/src/drivers/driver_nl80211_capa.c b/contrib/wpa/src/drivers/driver_nl80211_capa.c index e0d1d23..4cf3123 100644 --- a/contrib/wpa/src/drivers/driver_nl80211_capa.c +++ b/contrib/wpa/src/drivers/driver_nl80211_capa.c @@ -335,6 +335,33 @@ static void wiphy_info_tdls(struct wpa_driver_capa *capa, struct nlattr *tdls, } +static int ext_feature_isset(const u8 *ext_features, int ext_features_len, + enum nl80211_ext_feature_index ftidx) +{ + u8 ft_byte; + + if ((int) ftidx / 8 >= ext_features_len) + return 0; + + ft_byte = ext_features[ftidx / 8]; + return (ft_byte & BIT(ftidx % 8)) != 0; +} + + +static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info, + struct nlattr *tb) +{ + struct wpa_driver_capa *capa = info->capa; + + if (tb == NULL) + return; + + if (ext_feature_isset(nla_data(tb), nla_len(tb), + NL80211_EXT_FEATURE_VHT_IBSS)) + capa->flags |= WPA_DRIVER_FLAGS_VHT_IBSS; +} + + static void wiphy_info_feature_flags(struct wiphy_info_data *info, struct nlattr *tb) { @@ -509,6 +536,7 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) info->device_ap_sme = 1; wiphy_info_feature_flags(info, tb[NL80211_ATTR_FEATURE_FLAGS]); + wiphy_info_ext_feature_flags(info, tb[NL80211_ATTR_EXT_FEATURES]); wiphy_info_probe_resp_offload(capa, tb[NL80211_ATTR_PROBE_RESP_OFFLOAD]); @@ -547,22 +575,34 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) continue; } vinfo = nla_data(nl); - switch (vinfo->subcmd) { - case QCA_NL80211_VENDOR_SUBCMD_TEST: - drv->vendor_cmd_test_avail = 1; - break; - case QCA_NL80211_VENDOR_SUBCMD_ROAMING: - drv->roaming_vendor_cmd_avail = 1; - break; - case QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY: - drv->dfs_vendor_cmd_avail = 1; - break; - case QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES: - drv->get_features_vendor_cmd_avail = 1; - break; - case QCA_NL80211_VENDOR_SUBCMD_DO_ACS: - drv->capa.flags |= WPA_DRIVER_FLAGS_ACS_OFFLOAD; - break; + if (vinfo->vendor_id == OUI_QCA) { + switch (vinfo->subcmd) { + case QCA_NL80211_VENDOR_SUBCMD_TEST: + drv->vendor_cmd_test_avail = 1; + break; + case QCA_NL80211_VENDOR_SUBCMD_ROAMING: + drv->roaming_vendor_cmd_avail = 1; + break; + case QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY: + drv->dfs_vendor_cmd_avail = 1; + break; + case QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES: + drv->get_features_vendor_cmd_avail = 1; + break; + case QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST: + drv->get_pref_freq_list = 1; + break; + case QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL: + drv->set_prob_oper_freq = 1; + break; + case QCA_NL80211_VENDOR_SUBCMD_DO_ACS: + drv->capa.flags |= + WPA_DRIVER_FLAGS_ACS_OFFLOAD; + break; + case QCA_NL80211_VENDOR_SUBCMD_SETBAND: + drv->setband_vendor_cmd_avail = 1; + break; + } } wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u", @@ -717,6 +757,7 @@ static void qca_nl80211_check_dfs_capa(struct wpa_driver_nl80211_data *drv) struct features_info { u8 *flags; size_t flags_len; + struct wpa_driver_capa *capa; }; @@ -742,6 +783,19 @@ static int features_info_handler(struct nl_msg *msg, void *arg) info->flags = nla_data(attr); info->flags_len = nla_len(attr); } + attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_CONCURRENCY_CAPA]; + if (attr) + info->capa->conc_capab = nla_get_u32(attr); + + attr = tb_vendor[ + QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_2_4_BAND]; + if (attr) + info->capa->max_conc_chan_2_4 = nla_get_u32(attr); + + attr = tb_vendor[ + QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_5_0_BAND]; + if (attr) + info->capa->max_conc_chan_5_0 = nla_get_u32(attr); } return NL_SKIP; @@ -776,12 +830,16 @@ static void qca_nl80211_get_features(struct wpa_driver_nl80211_data *drv) } os_memset(&info, 0, sizeof(info)); + info.capa = &drv->capa; ret = send_and_recv_msgs(drv, msg, features_info_handler, &info); if (ret || !info.flags) return; if (check_feature(QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD, &info)) drv->capa.flags |= WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD; + + if (check_feature(QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY, &info)) + drv->capa.flags |= WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY; } diff --git a/contrib/wpa/src/drivers/driver_nl80211_event.c b/contrib/wpa/src/drivers/driver_nl80211_event.c index 87e412d..7b0f721 100644 --- a/contrib/wpa/src/drivers/driver_nl80211_event.c +++ b/contrib/wpa/src/drivers/driver_nl80211_event.c @@ -271,6 +271,7 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, struct nlattr *ptk_kek) { union wpa_event_data event; + const u8 *ssid; u16 status_code; if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) { @@ -331,6 +332,16 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, if (req_ie) { event.assoc_info.req_ies = nla_data(req_ie); 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); + if (ssid && ssid[1] > 0 && ssid[1] <= 32) { + drv->ssid_len = ssid[1]; + os_memcpy(drv->ssid, ssid + 2, ssid[1]); + } + } } if (resp_ie) { event.assoc_info.resp_ies = nla_data(resp_ie); @@ -1480,6 +1491,25 @@ static void qca_nl80211_avoid_freq(struct wpa_driver_nl80211_data *drv, } +static enum hostapd_hw_mode get_qca_hw_mode(u8 hw_mode) +{ + switch (hw_mode) { + case QCA_ACS_MODE_IEEE80211B: + return HOSTAPD_MODE_IEEE80211B; + case QCA_ACS_MODE_IEEE80211G: + return HOSTAPD_MODE_IEEE80211G; + case QCA_ACS_MODE_IEEE80211A: + return HOSTAPD_MODE_IEEE80211A; + case QCA_ACS_MODE_IEEE80211AD: + return HOSTAPD_MODE_IEEE80211AD; + case QCA_ACS_MODE_IEEE80211ANY: + return HOSTAPD_MODE_IEEE80211ANY; + default: + return NUM_HOSTAPD_MODES; + } +} + + static void qca_nl80211_acs_select_ch(struct wpa_driver_nl80211_data *drv, const u8 *data, size_t len) { @@ -1500,6 +1530,39 @@ static void qca_nl80211_acs_select_ch(struct wpa_driver_nl80211_data *drv, nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL]); event.acs_selected_channels.sec_channel = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL]); + if (tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL]) + event.acs_selected_channels.vht_seg0_center_ch = + nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL]); + if (tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL]) + event.acs_selected_channels.vht_seg1_center_ch = + nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL]); + if (tb[QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH]) + event.acs_selected_channels.ch_width = + nla_get_u16(tb[QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH]); + if (tb[QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE]) { + u8 hw_mode = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE]); + + event.acs_selected_channels.hw_mode = get_qca_hw_mode(hw_mode); + if (event.acs_selected_channels.hw_mode == NUM_HOSTAPD_MODES || + event.acs_selected_channels.hw_mode == + HOSTAPD_MODE_IEEE80211ANY) { + wpa_printf(MSG_DEBUG, + "nl80211: Invalid hw_mode %d in ACS selection event", + hw_mode); + return; + } + } + + wpa_printf(MSG_INFO, + "nl80211: ACS Results: PCH: %d SCH: %d BW: %d VHT0: %d VHT1: %d HW_MODE: %d", + event.acs_selected_channels.pri_channel, + event.acs_selected_channels.sec_channel, + event.acs_selected_channels.ch_width, + event.acs_selected_channels.vht_seg0_center_ch, + event.acs_selected_channels.vht_seg1_center_ch, + event.acs_selected_channels.hw_mode); + + /* Ignore ACS channel list check for backwards compatibility */ wpa_supplicant_event(drv->ctx, EVENT_ACS_CHANNEL_SELECTED, &event); } diff --git a/contrib/wpa/src/drivers/driver_nl80211_scan.c b/contrib/wpa/src/drivers/driver_nl80211_scan.c index 3911f48..4b762ea 100644 --- a/contrib/wpa/src/drivers/driver_nl80211_scan.c +++ b/contrib/wpa/src/drivers/driver_nl80211_scan.c @@ -221,6 +221,9 @@ int wpa_driver_nl80211_scan(struct i802_bss *bss, wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: scan request"); drv->scan_for_auth = 0; + if (TEST_FAIL()) + return -1; + msg = nl80211_scan_common(bss, NL80211_CMD_TRIGGER_SCAN, params); if (!msg) return -1; @@ -433,7 +436,7 @@ int wpa_driver_nl80211_stop_sched_scan(void *priv) } -static const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie) +const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie) { const u8 *end, *pos; @@ -583,6 +586,11 @@ int bss_info_handler(struct nl_msg *msg, void *arg) r->flags |= WPA_SCAN_LEVEL_INVALID | WPA_SCAN_QUAL_INVALID; if (bss[NL80211_BSS_TSF]) r->tsf = nla_get_u64(bss[NL80211_BSS_TSF]); + if (bss[NL80211_BSS_BEACON_TSF]) { + u64 tsf = nla_get_u64(bss[NL80211_BSS_BEACON_TSF]); + if (tsf > r->tsf) + r->tsf = tsf; + } if (bss[NL80211_BSS_SEEN_MS_AGO]) r->age = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]); r->ie_len = ie_len; diff --git a/contrib/wpa/src/drivers/driver_privsep.c b/contrib/wpa/src/drivers/driver_privsep.c index de23fbd..1f1676a 100644 --- a/contrib/wpa/src/drivers/driver_privsep.c +++ b/contrib/wpa/src/drivers/driver_privsep.c @@ -220,6 +220,56 @@ static int wpa_driver_privsep_set_key(const char *ifname, void *priv, } +static int wpa_driver_privsep_authenticate( + void *priv, struct wpa_driver_auth_params *params) +{ + struct wpa_driver_privsep_data *drv = priv; + struct privsep_cmd_authenticate *data; + int i, res; + size_t buflen; + u8 *pos; + + wpa_printf(MSG_DEBUG, "%s: priv=%p freq=%d bssid=" MACSTR + " auth_alg=%d local_state_change=%d p2p=%d", + __func__, priv, params->freq, MAC2STR(params->bssid), + params->auth_alg, params->local_state_change, params->p2p); + + buflen = sizeof(*data) + params->ie_len + params->sae_data_len; + data = os_zalloc(buflen); + if (data == NULL) + return -1; + + data->freq = params->freq; + os_memcpy(data->bssid, params->bssid, ETH_ALEN); + os_memcpy(data->ssid, params->ssid, params->ssid_len); + data->ssid_len = params->ssid_len; + data->auth_alg = params->auth_alg; + data->ie_len = params->ie_len; + for (i = 0; i < 4; i++) { + if (params->wep_key[i]) + os_memcpy(data->wep_key[i], params->wep_key[i], + params->wep_key_len[i]); + data->wep_key_len[i] = params->wep_key_len[i]; + } + data->wep_tx_keyidx = params->wep_tx_keyidx; + data->local_state_change = params->local_state_change; + data->p2p = params->p2p; + pos = (u8 *) (data + 1); + if (params->ie_len) { + os_memcpy(pos, params->ie, params->ie_len); + pos += params->ie_len; + } + if (params->sae_data_len) + os_memcpy(pos, params->sae_data, params->sae_data_len); + + res = wpa_priv_cmd(drv, PRIVSEP_CMD_AUTHENTICATE, data, buflen, + NULL, NULL); + os_free(data); + + return res; +} + + static int wpa_driver_privsep_associate( void *priv, struct wpa_driver_associate_params *params) { @@ -281,14 +331,15 @@ static int wpa_driver_privsep_get_ssid(void *priv, u8 *ssid) { struct wpa_driver_privsep_data *drv = priv; int res, ssid_len; - u8 reply[sizeof(int) + 32]; + u8 reply[sizeof(int) + SSID_MAX_LEN]; size_t len = sizeof(reply); res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_SSID, NULL, 0, reply, &len); if (res < 0 || len < sizeof(int)) return -1; os_memcpy(&ssid_len, reply, sizeof(int)); - if (ssid_len < 0 || ssid_len > 32 || sizeof(int) + ssid_len > len) { + if (ssid_len < 0 || ssid_len > SSID_MAX_LEN || + sizeof(int) + ssid_len > len) { wpa_printf(MSG_DEBUG, "privsep: Invalid get SSID reply"); return -1; } @@ -308,6 +359,32 @@ static int wpa_driver_privsep_deauthenticate(void *priv, const u8 *addr, } +static void wpa_driver_privsep_event_auth(void *ctx, u8 *buf, size_t len) +{ + union wpa_event_data data; + struct privsep_event_auth *auth; + + os_memset(&data, 0, sizeof(data)); + if (len < sizeof(*auth)) + return; + auth = (struct privsep_event_auth *) buf; + if (len < sizeof(*auth) + auth->ies_len) + return; + + os_memcpy(data.auth.peer, auth->peer, ETH_ALEN); + os_memcpy(data.auth.bssid, auth->bssid, ETH_ALEN); + data.auth.auth_type = auth->auth_type; + data.auth.auth_transaction = auth->auth_transaction; + data.auth.status_code = auth->status_code; + if (auth->ies_len) { + data.auth.ies = (u8 *) (auth + 1); + data.auth.ies_len = auth->ies_len; + } + + wpa_supplicant_event(ctx, EVENT_AUTH, &data); +} + + static void wpa_driver_privsep_event_assoc(void *ctx, enum wpa_event_type event, u8 *buf, size_t len) @@ -467,6 +544,9 @@ static void wpa_driver_privsep_receive(int sock, void *eloop_ctx, case PRIVSEP_EVENT_SCAN_RESULTS: wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL); break; + case PRIVSEP_EVENT_SCAN_STARTED: + wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, NULL); + break; case PRIVSEP_EVENT_ASSOC: wpa_driver_privsep_event_assoc(drv->ctx, EVENT_ASSOC, event_buf, event_len); @@ -502,6 +582,9 @@ static void wpa_driver_privsep_receive(int sock, void *eloop_ctx, wpa_driver_privsep_event_rx_eapol(drv->ctx, event_buf, event_len); break; + case PRIVSEP_EVENT_AUTH: + wpa_driver_privsep_event_auth(drv->ctx, event_buf, event_len); + break; } os_free(buf); @@ -702,6 +785,10 @@ static int wpa_driver_privsep_get_capa(void *priv, res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_CAPA, NULL, 0, capa, &len); if (res < 0 || len != sizeof(*capa)) return -1; + /* For now, no support for passing extended_capa pointers */ + capa->extended_capa = NULL; + capa->extended_capa_mask = NULL; + capa->extended_capa_len = 0; return 0; } @@ -734,6 +821,7 @@ struct wpa_driver_ops wpa_driver_privsep_ops = { .set_param = wpa_driver_privsep_set_param, .scan2 = wpa_driver_privsep_scan, .deauthenticate = wpa_driver_privsep_deauthenticate, + .authenticate = wpa_driver_privsep_authenticate, .associate = wpa_driver_privsep_associate, .get_capa = wpa_driver_privsep_get_capa, .get_mac_addr = wpa_driver_privsep_get_mac_addr, @@ -742,7 +830,7 @@ struct wpa_driver_ops wpa_driver_privsep_ops = { }; -struct wpa_driver_ops *wpa_drivers[] = +const struct wpa_driver_ops *const wpa_drivers[] = { &wpa_driver_privsep_ops, NULL diff --git a/contrib/wpa/src/drivers/drivers.c b/contrib/wpa/src/drivers/drivers.c index f0c3bb3..a98af9ac 100644 --- a/contrib/wpa/src/drivers/drivers.c +++ b/contrib/wpa/src/drivers/drivers.c @@ -47,7 +47,7 @@ extern struct wpa_driver_ops wpa_driver_none_ops; /* driver_none.c */ #endif /* CONFIG_DRIVER_NONE */ -struct wpa_driver_ops *wpa_drivers[] = +const struct wpa_driver_ops *const wpa_drivers[] = { #ifdef CONFIG_DRIVER_NL80211 &wpa_driver_nl80211_ops, diff --git a/contrib/wpa/src/eap_common/eap_common.c b/contrib/wpa/src/eap_common/eap_common.c index 1de1328..51a15d7 100644 --- a/contrib/wpa/src/eap_common/eap_common.c +++ b/contrib/wpa/src/eap_common/eap_common.c @@ -192,7 +192,7 @@ u8 eap_get_id(const struct wpabuf *msg) /** - * eap_get_id - Get EAP Type from wpabuf + * eap_get_type - Get EAP Type from wpabuf * @msg: Buffer starting with an EAP header * Returns: The EAP Type after the EAP header */ diff --git a/contrib/wpa/src/eap_common/eap_fast_common.c b/contrib/wpa/src/eap_common/eap_fast_common.c index fceb1b0..151cc78 100644 --- a/contrib/wpa/src/eap_common/eap_fast_common.c +++ b/contrib/wpa/src/eap_common/eap_fast_common.c @@ -96,49 +96,18 @@ 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) { - struct tls_keys keys; - u8 *rnd = NULL, *out; - int block_size; + u8 *out; - block_size = tls_connection_get_keyblock_size(ssl_ctx, conn); - if (block_size < 0) - return NULL; - - out = os_malloc(block_size + len); + out = os_malloc(len); if (out == NULL) return NULL; - if (tls_connection_prf(ssl_ctx, conn, label, 1, out, block_size + len) - == 0) { - os_memmove(out, out + block_size, len); - return out; + if (tls_connection_prf(ssl_ctx, conn, label, 1, 1, out, len)) { + os_free(out); + return NULL; } - if (tls_connection_get_keys(ssl_ctx, conn, &keys)) - goto fail; - - rnd = os_malloc(keys.client_random_len + keys.server_random_len); - if (rnd == NULL) - goto fail; - - os_memcpy(rnd, keys.server_random, keys.server_random_len); - os_memcpy(rnd + keys.server_random_len, keys.client_random, - keys.client_random_len); - - wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: master_secret for key " - "expansion", keys.master_key, keys.master_key_len); - if (tls_prf_sha1_md5(keys.master_key, keys.master_key_len, - label, rnd, keys.client_random_len + - keys.server_random_len, out, block_size + len)) - goto fail; - os_free(rnd); - os_memmove(out, out + block_size, len); return out; - -fail: - os_free(rnd); - os_free(out); - return NULL; } diff --git a/contrib/wpa/src/eap_common/eap_pwd_common.c b/contrib/wpa/src/eap_common/eap_pwd_common.c index 631c363..4d27623 100644 --- a/contrib/wpa/src/eap_common/eap_pwd_common.c +++ b/contrib/wpa/src/eap_common/eap_pwd_common.c @@ -86,9 +86,10 @@ static int eap_pwd_kdf(const u8 *key, size_t keylen, const u8 *label, * on the password and identities. */ int compute_password_element(EAP_PWD_group *grp, u16 num, - u8 *password, int password_len, - u8 *id_server, int id_server_len, - u8 *id_peer, int id_peer_len, u8 *token) + const u8 *password, size_t password_len, + const u8 *id_server, size_t id_server_len, + const u8 *id_peer, size_t id_peer_len, + const u8 *token) { BIGNUM *x_candidate = NULL, *rnd = NULL, *cofactor = NULL; struct crypto_hash *hash; @@ -283,10 +284,10 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, } -int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, BIGNUM *k, - BIGNUM *peer_scalar, BIGNUM *server_scalar, - u8 *confirm_peer, u8 *confirm_server, - u32 *ciphersuite, u8 *msk, u8 *emsk, u8 *session_id) +int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, const BIGNUM *k, + const BIGNUM *peer_scalar, const BIGNUM *server_scalar, + const u8 *confirm_peer, const u8 *confirm_server, + const u32 *ciphersuite, u8 *msk, u8 *emsk, u8 *session_id) { struct crypto_hash *hash; u8 mk[SHA256_MAC_LEN], *cruft; @@ -306,7 +307,7 @@ int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, BIGNUM *k, os_free(cruft); return -1; } - eap_pwd_h_update(hash, (u8 *) ciphersuite, sizeof(u32)); + eap_pwd_h_update(hash, (const u8 *) ciphersuite, sizeof(u32)); offset = BN_num_bytes(grp->order) - BN_num_bytes(peer_scalar); os_memset(cruft, 0, BN_num_bytes(grp->prime)); BN_bn2bin(peer_scalar, cruft + offset); diff --git a/contrib/wpa/src/eap_common/eap_pwd_common.h b/contrib/wpa/src/eap_common/eap_pwd_common.h index c54c441..a0d717e 100644 --- a/contrib/wpa/src/eap_common/eap_pwd_common.h +++ b/contrib/wpa/src/eap_common/eap_pwd_common.h @@ -56,10 +56,15 @@ struct eap_pwd_id { } STRUCT_PACKED; /* common routines */ -int compute_password_element(EAP_PWD_group *, u16, u8 *, int, u8 *, int, u8 *, - int, u8 *); -int compute_keys(EAP_PWD_group *, BN_CTX *, BIGNUM *, BIGNUM *, BIGNUM *, - u8 *, u8 *, u32 *, u8 *, u8 *, u8 *); +int compute_password_element(EAP_PWD_group *grp, u16 num, + const u8 *password, size_t password_len, + const u8 *id_server, size_t id_server_len, + const u8 *id_peer, size_t id_peer_len, + const u8 *token); +int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, const BIGNUM *k, + const BIGNUM *peer_scalar, const BIGNUM *server_scalar, + const u8 *confirm_peer, const u8 *confirm_server, + const u32 *ciphersuite, u8 *msk, u8 *emsk, u8 *session_id); struct crypto_hash * eap_pwd_h_init(void); void eap_pwd_h_update(struct crypto_hash *hash, const u8 *data, size_t len); void eap_pwd_h_final(struct crypto_hash *hash, u8 *digest); diff --git a/contrib/wpa/src/eap_common/eap_sake_common.c b/contrib/wpa/src/eap_common/eap_sake_common.c index a76253d..c22e43e 100644 --- a/contrib/wpa/src/eap_common/eap_sake_common.c +++ b/contrib/wpa/src/eap_common/eap_sake_common.c @@ -16,99 +16,99 @@ static int eap_sake_parse_add_attr(struct eap_sake_parse_attr *attr, - const u8 *pos) + u8 attr_id, u8 len, const u8 *data) { size_t i; - switch (pos[0]) { + switch (attr_id) { case EAP_SAKE_AT_RAND_S: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_RAND_S"); - if (pos[1] != 2 + EAP_SAKE_RAND_LEN) { + if (len != EAP_SAKE_RAND_LEN) { wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_RAND_S with " - "invalid length %d", pos[1]); + "invalid payload length %d", len); return -1; } - attr->rand_s = pos + 2; + attr->rand_s = data; break; case EAP_SAKE_AT_RAND_P: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_RAND_P"); - if (pos[1] != 2 + EAP_SAKE_RAND_LEN) { + if (len != EAP_SAKE_RAND_LEN) { wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_RAND_P with " - "invalid length %d", pos[1]); + "invalid payload length %d", len); return -1; } - attr->rand_p = pos + 2; + attr->rand_p = data; break; case EAP_SAKE_AT_MIC_S: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_MIC_S"); - if (pos[1] != 2 + EAP_SAKE_MIC_LEN) { + if (len != EAP_SAKE_MIC_LEN) { wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_MIC_S with " - "invalid length %d", pos[1]); + "invalid payload length %d", len); return -1; } - attr->mic_s = pos + 2; + attr->mic_s = data; break; case EAP_SAKE_AT_MIC_P: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_MIC_P"); - if (pos[1] != 2 + EAP_SAKE_MIC_LEN) { + if (len != EAP_SAKE_MIC_LEN) { wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_MIC_P with " - "invalid length %d", pos[1]); + "invalid payload length %d", len); return -1; } - attr->mic_p = pos + 2; + attr->mic_p = data; break; case EAP_SAKE_AT_SERVERID: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_SERVERID"); - attr->serverid = pos + 2; - attr->serverid_len = pos[1] - 2; + attr->serverid = data; + attr->serverid_len = len; break; case EAP_SAKE_AT_PEERID: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_PEERID"); - attr->peerid = pos + 2; - attr->peerid_len = pos[1] - 2; + attr->peerid = data; + attr->peerid_len = len; break; case EAP_SAKE_AT_SPI_S: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_SPI_S"); - attr->spi_s = pos + 2; - attr->spi_s_len = pos[1] - 2; + attr->spi_s = data; + attr->spi_s_len = len; break; case EAP_SAKE_AT_SPI_P: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_SPI_P"); - attr->spi_p = pos + 2; - attr->spi_p_len = pos[1] - 2; + attr->spi_p = data; + attr->spi_p_len = len; break; case EAP_SAKE_AT_ANY_ID_REQ: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_ANY_ID_REQ"); - if (pos[1] != 4) { + if (len != 2) { wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid AT_ANY_ID_REQ" - " length %d", pos[1]); + " payload length %d", len); return -1; } - attr->any_id_req = pos + 2; + attr->any_id_req = data; break; case EAP_SAKE_AT_PERM_ID_REQ: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_PERM_ID_REQ"); - if (pos[1] != 4) { + if (len != 2) { wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid " - "AT_PERM_ID_REQ length %d", pos[1]); + "AT_PERM_ID_REQ payload length %d", len); return -1; } - attr->perm_id_req = pos + 2; + attr->perm_id_req = data; break; case EAP_SAKE_AT_ENCR_DATA: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_ENCR_DATA"); - attr->encr_data = pos + 2; - attr->encr_data_len = pos[1] - 2; + attr->encr_data = data; + attr->encr_data_len = len; break; case EAP_SAKE_AT_IV: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_IV"); - attr->iv = pos + 2; - attr->iv_len = pos[1] - 2; + attr->iv = data; + attr->iv_len = len; break; case EAP_SAKE_AT_PADDING: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_PADDING"); - for (i = 2; i < pos[1]; i++) { - if (pos[i]) { + for (i = 0; i < len; i++) { + if (data[i]) { wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_PADDING " "with non-zero pad byte"); return -1; @@ -117,26 +117,26 @@ static int eap_sake_parse_add_attr(struct eap_sake_parse_attr *attr, break; case EAP_SAKE_AT_NEXT_TMPID: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_NEXT_TMPID"); - attr->next_tmpid = pos + 2; - attr->next_tmpid_len = pos[1] - 2; + attr->next_tmpid = data; + attr->next_tmpid_len = len; break; case EAP_SAKE_AT_MSK_LIFE: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_IV"); - if (pos[1] != 6) { + if (len != 4) { wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid " - "AT_MSK_LIFE length %d", pos[1]); + "AT_MSK_LIFE payload length %d", len); return -1; } - attr->msk_life = pos + 2; + attr->msk_life = data; break; default: - if (pos[0] < 128) { + if (attr_id < 128) { wpa_printf(MSG_DEBUG, "EAP-SAKE: Unknown non-skippable" - " attribute %d", pos[0]); + " attribute %d", attr_id); return -1; } wpa_printf(MSG_DEBUG, "EAP-SAKE: Ignoring unknown skippable " - "attribute %d", pos[0]); + "attribute %d", attr_id); break; } @@ -180,7 +180,7 @@ int eap_sake_parse_attributes(const u8 *buf, size_t len, return -1; } - if (eap_sake_parse_add_attr(attr, pos)) + if (eap_sake_parse_add_attr(attr, pos[0], pos[1] - 2, pos + 2)) return -1; pos += pos[1]; diff --git a/contrib/wpa/src/eap_common/ikev2_common.c b/contrib/wpa/src/eap_common/ikev2_common.c index 4f9e64e..d60358c 100644 --- a/contrib/wpa/src/eap_common/ikev2_common.c +++ b/contrib/wpa/src/eap_common/ikev2_common.c @@ -16,7 +16,7 @@ #include "ikev2_common.h" -static struct ikev2_integ_alg ikev2_integ_algs[] = { +static const struct ikev2_integ_alg ikev2_integ_algs[] = { { AUTH_HMAC_SHA1_96, 20, 12 }, { AUTH_HMAC_MD5_96, 16, 12 } }; @@ -24,7 +24,7 @@ static struct ikev2_integ_alg ikev2_integ_algs[] = { #define NUM_INTEG_ALGS ARRAY_SIZE(ikev2_integ_algs) -static struct ikev2_prf_alg ikev2_prf_algs[] = { +static const struct ikev2_prf_alg ikev2_prf_algs[] = { { PRF_HMAC_SHA1, 20, 20 }, { PRF_HMAC_MD5, 16, 16 } }; @@ -32,7 +32,7 @@ static struct ikev2_prf_alg ikev2_prf_algs[] = { #define NUM_PRF_ALGS ARRAY_SIZE(ikev2_prf_algs) -static struct ikev2_encr_alg ikev2_encr_algs[] = { +static const struct ikev2_encr_alg ikev2_encr_algs[] = { { ENCR_AES_CBC, 16, 16 }, /* only 128-bit keys supported for now */ { ENCR_3DES, 24, 8 } }; diff --git a/contrib/wpa/src/eap_peer/eap.c b/contrib/wpa/src/eap_peer/eap.c index 35433f3..56c24b5 100644 --- a/contrib/wpa/src/eap_peer/eap.c +++ b/contrib/wpa/src/eap_peer/eap.c @@ -584,7 +584,7 @@ static int eap_peer_erp_reauth_start(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP: Valid ERP key found %s (SEQ=%u)", erp->keyname_nai, erp->next_seq); - msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH, + msg = eap_msg_alloc(EAP_VENDOR_IETF, (EapType) EAP_ERP_TYPE_REAUTH, 1 + 2 + 2 + os_strlen(erp->keyname_nai) + 1 + 16, EAP_CODE_INITIATE, hdr->identifier); if (msg == NULL) @@ -708,7 +708,7 @@ SM_STATE(EAP, SEND_RESPONSE) wpabuf_free(sm->lastRespData); if (sm->eapRespData) { if (sm->workaround) - os_memcpy(sm->last_md5, sm->req_md5, 16); + os_memcpy(sm->last_sha1, sm->req_sha1, 20); sm->lastId = sm->reqId; sm->lastRespData = wpabuf_dup(sm->eapRespData); eapol_set_bool(sm, EAPOL_eapResp, TRUE); @@ -914,12 +914,12 @@ static int eap_peer_req_is_duplicate(struct eap_sm *sm) duplicate = (sm->reqId == sm->lastId) && sm->rxReq; if (sm->workaround && duplicate && - os_memcmp(sm->req_md5, sm->last_md5, 16) != 0) { + os_memcmp(sm->req_sha1, sm->last_sha1, 20) != 0) { /* * RFC 4137 uses (reqId == lastId) as the only verification for * duplicate EAP requests. However, this misses cases where the * AS is incorrectly using the same id again; and - * unfortunately, such implementations exist. Use MD5 hash as + * unfortunately, such implementations exist. Use SHA1 hash as * an extra verification for the packets being duplicate to * workaround these issues. */ @@ -1765,7 +1765,7 @@ static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req) if (sm->workaround) { const u8 *addr[1]; addr[0] = wpabuf_head(req); - md5_vector(1, addr, &plen, sm->req_md5); + sha1_vector(1, addr, &plen, sm->req_sha1); } switch (hdr->code) { @@ -1911,7 +1911,7 @@ static void eap_peer_sm_tls_event(void *ctx, enum tls_event ev, * structure remains alive while the EAP state machine is active. */ struct eap_sm * eap_peer_sm_init(void *eapol_ctx, - struct eapol_callbacks *eapol_cb, + const struct eapol_callbacks *eapol_cb, void *msg_ctx, struct eap_config *conf) { struct eap_sm *sm; @@ -2400,7 +2400,7 @@ static int eap_allowed_phase2_type(int vendor, int type) u32 eap_get_phase2_type(const char *name, int *vendor) { int v; - u8 type = eap_peer_get_type(name, &v); + u32 type = eap_peer_get_type(name, &v); if (eap_allowed_phase2_type(v, type)) { *vendor = v; return type; diff --git a/contrib/wpa/src/eap_peer/eap.h b/contrib/wpa/src/eap_peer/eap.h index 702463b..1a645af 100644 --- a/contrib/wpa/src/eap_peer/eap.h +++ b/contrib/wpa/src/eap_peer/eap.h @@ -307,7 +307,7 @@ struct eap_config { }; struct eap_sm * eap_peer_sm_init(void *eapol_ctx, - struct eapol_callbacks *eapol_cb, + const struct eapol_callbacks *eapol_cb, void *msg_ctx, struct eap_config *conf); void eap_peer_sm_deinit(struct eap_sm *sm); int eap_peer_sm_step(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 0662ae7..dc9e8cc 100644 --- a/contrib/wpa/src/eap_peer/eap_aka.c +++ b/contrib/wpa/src/eap_peer/eap_aka.c @@ -1296,7 +1296,7 @@ static struct wpabuf * eap_aka_process(struct eap_sm *sm, void *priv, pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, reqData, &len); - if (pos == NULL || len < 1) { + if (pos == NULL || len < 3) { ret->ignore = TRUE; return NULL; } diff --git a/contrib/wpa/src/eap_peer/eap_eke.c b/contrib/wpa/src/eap_peer/eap_eke.c index 9fec66c..dfbda56 100644 --- a/contrib/wpa/src/eap_peer/eap_eke.c +++ b/contrib/wpa/src/eap_peer/eap_eke.c @@ -195,15 +195,14 @@ static int eap_eke_supp_mac(u8 mac) static struct wpabuf * eap_eke_build_fail(struct eap_eke_data *data, struct eap_method_ret *ret, - const struct wpabuf *reqData, - u32 failure_code) + u8 id, u32 failure_code) { struct wpabuf *resp; wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-Failure/Response - code=0x%x", failure_code); - resp = eap_eke_build_msg(data, eap_get_id(reqData), 4, EAP_EKE_FAILURE); + resp = eap_eke_build_msg(data, id, 4, EAP_EKE_FAILURE); if (resp) wpabuf_put_be32(resp, failure_code); @@ -230,9 +229,10 @@ static struct wpabuf * eap_eke_process_id(struct eap_eke_data *data, const u8 *pos, *end; const u8 *prop = NULL; u8 idtype; + u8 id = eap_get_id(reqData); if (data->state != IDENTITY) { - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PROTO_ERROR); } @@ -240,7 +240,7 @@ static struct wpabuf * eap_eke_process_id(struct eap_eke_data *data, if (payload_len < 2 + 4) { wpa_printf(MSG_DEBUG, "EAP-EKE: Too short ID/Request Data"); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PROTO_ERROR); } @@ -253,7 +253,7 @@ static struct wpabuf * eap_eke_process_id(struct eap_eke_data *data, if (pos + num_prop * 4 > end) { wpa_printf(MSG_DEBUG, "EAP-EKE: Too short ID/Request Data (num_prop=%u)", num_prop); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PROTO_ERROR); } @@ -293,7 +293,7 @@ static struct wpabuf * eap_eke_process_id(struct eap_eke_data *data, if (prop == NULL) { wpa_printf(MSG_DEBUG, "EAP-EKE: No acceptable proposal found"); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_NO_PROPOSAL_CHOSEN); } @@ -301,7 +301,7 @@ static struct wpabuf * eap_eke_process_id(struct eap_eke_data *data, if (pos == end) { wpa_printf(MSG_DEBUG, "EAP-EKE: Too short ID/Request Data to include IDType/Identity"); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PROTO_ERROR); } @@ -312,7 +312,7 @@ static struct wpabuf * eap_eke_process_id(struct eap_eke_data *data, os_free(data->serverid); data->serverid = os_malloc(end - pos); if (data->serverid == NULL) { - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } os_memcpy(data->serverid, pos, end - pos); @@ -320,11 +320,11 @@ static struct wpabuf * eap_eke_process_id(struct eap_eke_data *data, wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-ID/Response"); - resp = eap_eke_build_msg(data, eap_get_id(reqData), + resp = eap_eke_build_msg(data, id, 2 + 4 + 1 + data->peerid_len, EAP_EKE_ID); if (resp == NULL) { - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } @@ -339,7 +339,7 @@ static struct wpabuf * eap_eke_process_id(struct eap_eke_data *data, data->msgs = wpabuf_alloc(wpabuf_len(reqData) + wpabuf_len(resp)); if (data->msgs == NULL) { wpabuf_free(resp); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } wpabuf_put_buf(data->msgs, reqData); @@ -366,10 +366,11 @@ static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, u8 pub[EAP_EKE_MAX_DH_LEN]; const u8 *password; size_t password_len; + u8 id = eap_get_id(reqData); if (data->state != COMMIT) { wpa_printf(MSG_DEBUG, "EAP-EKE: EAP-EKE-Commit/Request received in unexpected state (%d)", data->state); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PROTO_ERROR); } @@ -378,7 +379,7 @@ static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, password = eap_get_config_password(sm, &password_len); if (password == NULL) { wpa_printf(MSG_INFO, "EAP-EKE: No password configured!"); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PASSWD_NOT_FOUND); } @@ -387,7 +388,7 @@ static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, if (pos + data->sess.dhcomp_len > end) { wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Commit"); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PROTO_ERROR); } @@ -405,7 +406,7 @@ static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, data->serverid, data->serverid_len, data->peerid, data->peerid_len, key) < 0) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive key"); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } @@ -416,7 +417,7 @@ static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, if (eap_eke_dh_init(data->sess.dhgroup, data->dh_priv, pub) < 0) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to initialize DH"); os_memset(key, 0, sizeof(key)); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } @@ -424,7 +425,7 @@ static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, { wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive shared secret"); os_memset(key, 0, sizeof(key)); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } @@ -433,18 +434,18 @@ static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, data->peerid, data->peerid_len) < 0) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive Ke/Ki"); os_memset(key, 0, sizeof(key)); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-Commit/Response"); - resp = eap_eke_build_msg(data, eap_get_id(reqData), + resp = eap_eke_build_msg(data, id, data->sess.dhcomp_len + data->sess.pnonce_len, EAP_EKE_COMMIT); if (resp == NULL) { os_memset(key, 0, sizeof(key)); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } @@ -453,7 +454,7 @@ static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, if (eap_eke_dhcomp(&data->sess, key, pub, rpos) < 0) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to build DHComponent_P"); os_memset(key, 0, sizeof(key)); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } os_memset(key, 0, sizeof(key)); @@ -463,7 +464,7 @@ static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, if (random_get_bytes(data->nonce_p, data->sess.nonce_len)) { wpabuf_free(resp); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_P", @@ -472,7 +473,7 @@ static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, if (eap_eke_prot(&data->sess, data->nonce_p, data->sess.nonce_len, wpabuf_put(resp, 0), &prot_len) < 0) { wpabuf_free(resp); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } wpa_hexdump(MSG_DEBUG, "EAP-EKE: PNonce_P", @@ -484,7 +485,7 @@ static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, if (wpabuf_resize(&data->msgs, wpabuf_len(reqData) + wpabuf_len(resp)) < 0) { wpabuf_free(resp); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } wpabuf_put_buf(data->msgs, reqData); @@ -509,11 +510,12 @@ static struct wpabuf * eap_eke_process_confirm(struct eap_eke_data *data, u8 auth_s[EAP_EKE_MAX_HASH_LEN]; size_t decrypt_len; u8 *auth; + u8 id = eap_get_id(reqData); if (data->state != CONFIRM) { wpa_printf(MSG_DEBUG, "EAP-EKE: EAP-EKE-Confirm/Request received in unexpected state (%d)", data->state); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PROTO_ERROR); } @@ -524,7 +526,7 @@ static struct wpabuf * eap_eke_process_confirm(struct eap_eke_data *data, if (pos + data->sess.pnonce_ps_len + data->sess.prf_len > end) { wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Confirm"); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PROTO_ERROR); } @@ -532,19 +534,19 @@ static struct wpabuf * eap_eke_process_confirm(struct eap_eke_data *data, if (eap_eke_decrypt_prot(&data->sess, pos, data->sess.pnonce_ps_len, nonces, &decrypt_len) < 0) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt PNonce_PS"); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_AUTHENTICATION_FAIL); } if (decrypt_len != (size_t) 2 * data->sess.nonce_len) { wpa_printf(MSG_INFO, "EAP-EKE: PNonce_PS protected data length does not match length of Nonce_P and Nonce_S"); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_AUTHENTICATION_FAIL); } wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Received Nonce_P | Nonce_S", nonces, 2 * data->sess.nonce_len); if (os_memcmp(data->nonce_p, nonces, data->sess.nonce_len) != 0) { wpa_printf(MSG_INFO, "EAP-EKE: Received Nonce_P does not match transmitted Nonce_P"); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_AUTHENTICATION_FAIL); } @@ -556,30 +558,30 @@ static struct wpabuf * eap_eke_process_confirm(struct eap_eke_data *data, if (eap_eke_derive_ka(&data->sess, data->serverid, data->serverid_len, data->peerid, data->peerid_len, data->nonce_p, data->nonce_s) < 0) { - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } if (eap_eke_auth(&data->sess, "EAP-EKE server", data->msgs, auth_s) < 0) { - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_S", auth_s, data->sess.prf_len); if (os_memcmp_const(auth_s, pos + data->sess.pnonce_ps_len, data->sess.prf_len) != 0) { wpa_printf(MSG_INFO, "EAP-EKE: Auth_S does not match"); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_AUTHENTICATION_FAIL); } wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-Confirm/Response"); - resp = eap_eke_build_msg(data, eap_get_id(reqData), + resp = eap_eke_build_msg(data, id, data->sess.pnonce_len + data->sess.prf_len, EAP_EKE_CONFIRM); if (resp == NULL) { - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } @@ -587,7 +589,7 @@ static struct wpabuf * eap_eke_process_confirm(struct eap_eke_data *data, if (eap_eke_prot(&data->sess, data->nonce_s, data->sess.nonce_len, wpabuf_put(resp, 0), &prot_len) < 0) { wpabuf_free(resp); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } wpabuf_put(resp, prot_len); @@ -595,7 +597,7 @@ static struct wpabuf * eap_eke_process_confirm(struct eap_eke_data *data, auth = wpabuf_put(resp, data->sess.prf_len); if (eap_eke_auth(&data->sess, "EAP-EKE peer", data->msgs, auth) < 0) { wpabuf_free(resp); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_P", auth, data->sess.prf_len); @@ -606,7 +608,7 @@ static struct wpabuf * eap_eke_process_confirm(struct eap_eke_data *data, data->msk, data->emsk) < 0) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive MSK/EMSK"); wpabuf_free(resp); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } @@ -638,7 +640,8 @@ static struct wpabuf * eap_eke_process_failure(struct eap_eke_data *data, wpa_printf(MSG_INFO, "EAP-EKE: Failure-Code 0x%x", code); } - return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_NO_ERROR); + return eap_eke_build_fail(data, ret, eap_get_id(reqData), + EAP_EKE_FAIL_NO_ERROR); } @@ -741,6 +744,29 @@ static u8 * eap_eke_get_emsk(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_eke_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_eke_data *data = priv; + u8 *sid; + size_t sid_len; + + if (data->state != SUCCESS) + return NULL; + + sid_len = 1 + 2 * data->sess.nonce_len; + sid = os_malloc(sid_len); + if (sid == NULL) + return NULL; + sid[0] = EAP_TYPE_EKE; + os_memcpy(sid + 1, data->nonce_p, data->sess.nonce_len); + os_memcpy(sid + 1 + data->sess.nonce_len, data->nonce_s, + data->sess.nonce_len); + *len = sid_len; + + return sid; +} + + int eap_peer_eke_register(void) { struct eap_method *eap; @@ -757,6 +783,7 @@ int eap_peer_eke_register(void) eap->isKeyAvailable = eap_eke_isKeyAvailable; eap->getKey = eap_eke_getKey; eap->get_emsk = eap_eke_get_emsk; + eap->getSessionId = eap_eke_get_session_id; ret = eap_peer_method_register(eap); if (ret) diff --git a/contrib/wpa/src/eap_peer/eap_fast.c b/contrib/wpa/src/eap_peer/eap_fast.c index 68d7fba..4cbe3ba 100644 --- a/contrib/wpa/src/eap_peer/eap_fast.c +++ b/contrib/wpa/src/eap_peer/eap_fast.c @@ -267,8 +267,8 @@ static int eap_fast_derive_msk(struct eap_fast_data *data) } -static void eap_fast_derive_key_auth(struct eap_sm *sm, - struct eap_fast_data *data) +static int eap_fast_derive_key_auth(struct eap_sm *sm, + struct eap_fast_data *data) { u8 *sks; @@ -281,7 +281,7 @@ static void eap_fast_derive_key_auth(struct eap_sm *sm, if (sks == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive " "session_key_seed"); - return; + return -1; } /* @@ -294,11 +294,12 @@ static void eap_fast_derive_key_auth(struct eap_sm *sm, data->simck_idx = 0; os_memcpy(data->simck, sks, EAP_FAST_SIMCK_LEN); os_free(sks); + return 0; } -static void eap_fast_derive_key_provisioning(struct eap_sm *sm, - struct eap_fast_data *data) +static int eap_fast_derive_key_provisioning(struct eap_sm *sm, + struct eap_fast_data *data) { os_free(data->key_block_p); data->key_block_p = (struct eap_fast_key_block_provisioning *) @@ -307,7 +308,7 @@ static void eap_fast_derive_key_provisioning(struct eap_sm *sm, sizeof(*data->key_block_p)); if (data->key_block_p == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive key block"); - return; + return -1; } /* * RFC 4851, Section 5.2: @@ -326,15 +327,19 @@ static void eap_fast_derive_key_provisioning(struct eap_sm *sm, wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: client_challenge", data->key_block_p->client_challenge, sizeof(data->key_block_p->client_challenge)); + return 0; } -static void eap_fast_derive_keys(struct eap_sm *sm, struct eap_fast_data *data) +static int eap_fast_derive_keys(struct eap_sm *sm, struct eap_fast_data *data) { + int res; + if (data->anon_provisioning) - eap_fast_derive_key_provisioning(sm, data); + res = eap_fast_derive_key_provisioning(sm, data); else - eap_fast_derive_key_auth(sm, data); + res = eap_fast_derive_key_auth(sm, data); + return res; } @@ -1172,7 +1177,7 @@ static struct wpabuf * eap_fast_pac_request(void) static int eap_fast_process_decrypted(struct eap_sm *sm, struct eap_fast_data *data, struct eap_method_ret *ret, - const struct eap_hdr *req, + u8 identifier, struct wpabuf *decrypted, struct wpabuf **out_data) { @@ -1184,18 +1189,18 @@ static int eap_fast_process_decrypted(struct eap_sm *sm, return 0; if (resp) return eap_fast_encrypt_response(sm, data, resp, - req->identifier, out_data); + identifier, out_data); if (tlv.result == EAP_TLV_RESULT_FAILURE) { resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0); return eap_fast_encrypt_response(sm, data, resp, - req->identifier, out_data); + identifier, out_data); } if (tlv.iresult == EAP_TLV_RESULT_FAILURE) { resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 1); return eap_fast_encrypt_response(sm, data, resp, - req->identifier, out_data); + identifier, out_data); } if (tlv.crypto_binding) { @@ -1277,14 +1282,13 @@ static int eap_fast_process_decrypted(struct eap_sm *sm, resp = wpabuf_alloc(1); } - return eap_fast_encrypt_response(sm, data, resp, req->identifier, + return eap_fast_encrypt_response(sm, data, resp, identifier, out_data); } static int eap_fast_decrypt(struct eap_sm *sm, struct eap_fast_data *data, - struct eap_method_ret *ret, - const struct eap_hdr *req, + struct eap_method_ret *ret, u8 identifier, const struct wpabuf *in_data, struct wpabuf **out_data) { @@ -1309,7 +1313,7 @@ static int eap_fast_decrypt(struct eap_sm *sm, struct eap_fast_data *data, /* Received TLS ACK - requesting more fragments */ return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_FAST, data->fast_version, - req->identifier, NULL, out_data); + identifier, NULL, out_data); } res = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted); @@ -1328,7 +1332,7 @@ continue_req: return -1; } - res = eap_fast_process_decrypted(sm, data, ret, req, + res = eap_fast_process_decrypted(sm, data, ret, identifier, in_decrypted, out_data); wpabuf_free(in_decrypted); @@ -1340,7 +1344,7 @@ continue_req: static const u8 * eap_fast_get_a_id(const u8 *buf, size_t len, size_t *id_len) { const u8 *a_id; - struct pac_tlv_hdr *hdr; + const struct pac_tlv_hdr *hdr; /* * Parse authority identity (A-ID) from the EAP-FAST/Start. This @@ -1350,13 +1354,13 @@ static const u8 * eap_fast_get_a_id(const u8 *buf, size_t len, size_t *id_len) *id_len = len; if (len > sizeof(*hdr)) { int tlen; - hdr = (struct pac_tlv_hdr *) buf; + hdr = (const struct pac_tlv_hdr *) buf; tlen = be_to_host16(hdr->len); if (be_to_host16(hdr->type) == PAC_TYPE_A_ID && sizeof(*hdr) + tlen <= len) { wpa_printf(MSG_DEBUG, "EAP-FAST: A-ID was in TLV " "(Start)"); - a_id = (u8 *) (hdr + 1); + a_id = (const u8 *) (hdr + 1); *id_len = tlen; } } @@ -1529,6 +1533,7 @@ static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv, struct wpabuf *resp; const u8 *pos; struct eap_fast_data *data = priv; + struct wpabuf msg; pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_FAST, ret, reqData, &left, &flags); @@ -1545,13 +1550,13 @@ static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv, left = 0; /* A-ID is not used in further packet processing */ } + wpabuf_set(&msg, pos, left); + resp = NULL; if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) && !data->resuming) { /* Process tunneled (encrypted) phase 2 data. */ - struct wpabuf msg; - wpabuf_set(&msg, pos, left); - res = eap_fast_decrypt(sm, data, ret, req, &msg, &resp); + res = eap_fast_decrypt(sm, data, ret, id, &msg, &resp); if (res < 0) { ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; @@ -1565,8 +1570,15 @@ static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv, /* Continue processing TLS handshake (phase 1). */ res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_FAST, - data->fast_version, id, pos, - left, &resp); + data->fast_version, id, &msg, + &resp); + if (res < 0) { + wpa_printf(MSG_DEBUG, + "EAP-FAST: TLS processing failed"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return resp; + } if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { char cipher[80]; @@ -1586,20 +1598,24 @@ static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv, } else data->anon_provisioning = 0; data->resuming = 0; - eap_fast_derive_keys(sm, data); + if (eap_fast_derive_keys(sm, data) < 0) { + wpa_printf(MSG_DEBUG, + "EAP-FAST: Could not derive keys"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + wpabuf_free(resp); + return NULL; + } } if (res == 2) { - struct wpabuf msg; /* * Application data included in the handshake message. */ wpabuf_free(data->pending_phase2_req); data->pending_phase2_req = resp; resp = NULL; - wpabuf_set(&msg, pos, left); - res = eap_fast_decrypt(sm, data, ret, req, &msg, - &resp); + res = eap_fast_decrypt(sm, data, ret, id, &msg, &resp); } } diff --git a/contrib/wpa/src/eap_peer/eap_gpsk.c b/contrib/wpa/src/eap_peer/eap_gpsk.c index c54bf11..902b4ba 100644 --- a/contrib/wpa/src/eap_peer/eap_gpsk.c +++ b/contrib/wpa/src/eap_peer/eap_gpsk.c @@ -274,7 +274,7 @@ static const u8 * eap_gpsk_process_csuite_list(struct eap_sm *sm, static struct wpabuf * eap_gpsk_process_gpsk_1(struct eap_sm *sm, struct eap_gpsk_data *data, struct eap_method_ret *ret, - const struct wpabuf *reqData, + u8 identifier, const u8 *payload, size_t payload_len) { @@ -301,7 +301,7 @@ static struct wpabuf * eap_gpsk_process_gpsk_1(struct eap_sm *sm, return NULL; } - resp = eap_gpsk_send_gpsk_2(data, eap_get_id(reqData), + resp = eap_gpsk_send_gpsk_2(data, identifier, csuite_list, csuite_list_len); if (resp == NULL) return NULL; @@ -583,7 +583,7 @@ static const u8 * eap_gpsk_validate_gpsk_3_mic(struct eap_gpsk_data *data, static struct wpabuf * eap_gpsk_process_gpsk_3(struct eap_sm *sm, struct eap_gpsk_data *data, struct eap_method_ret *ret, - const struct wpabuf *reqData, + u8 identifier, const u8 *payload, size_t payload_len) { @@ -615,7 +615,7 @@ static struct wpabuf * eap_gpsk_process_gpsk_3(struct eap_sm *sm, (unsigned long) (end - pos)); } - resp = eap_gpsk_send_gpsk_4(data, eap_get_id(reqData)); + resp = eap_gpsk_send_gpsk_4(data, identifier); if (resp == NULL) return NULL; @@ -670,6 +670,7 @@ static struct wpabuf * eap_gpsk_process(struct eap_sm *sm, void *priv, struct wpabuf *resp; const u8 *pos; size_t len; + u8 opcode, id; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GPSK, reqData, &len); if (pos == NULL || len < 1) { @@ -677,25 +678,27 @@ static struct wpabuf * eap_gpsk_process(struct eap_sm *sm, void *priv, return NULL; } - wpa_printf(MSG_DEBUG, "EAP-GPSK: Received frame: opcode %d", *pos); + id = eap_get_id(reqData); + opcode = *pos++; + len--; + wpa_printf(MSG_DEBUG, "EAP-GPSK: Received frame: opcode %d", opcode); ret->ignore = FALSE; ret->methodState = METHOD_MAY_CONT; ret->decision = DECISION_FAIL; ret->allowNotifications = FALSE; - switch (*pos) { + switch (opcode) { case EAP_GPSK_OPCODE_GPSK_1: - resp = eap_gpsk_process_gpsk_1(sm, data, ret, reqData, - pos + 1, len - 1); + resp = eap_gpsk_process_gpsk_1(sm, data, ret, id, pos, len); break; case EAP_GPSK_OPCODE_GPSK_3: - resp = eap_gpsk_process_gpsk_3(sm, data, ret, reqData, - pos + 1, len - 1); + resp = eap_gpsk_process_gpsk_3(sm, data, ret, id, pos, len); break; default: - wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignoring message with " - "unknown opcode %d", *pos); + wpa_printf(MSG_DEBUG, + "EAP-GPSK: Ignoring message with unknown opcode %d", + opcode); ret->ignore = TRUE; return NULL; } diff --git a/contrib/wpa/src/eap_peer/eap_i.h b/contrib/wpa/src/eap_peer/eap_i.h index 2d7fdea..99b44da 100644 --- a/contrib/wpa/src/eap_peer/eap_i.h +++ b/contrib/wpa/src/eap_peer/eap_i.h @@ -328,7 +328,7 @@ struct eap_sm { /* not defined in RFC 4137 */ Boolean changed; void *eapol_ctx; - struct eapol_callbacks *eapol_cb; + const struct eapol_callbacks *eapol_cb; void *eap_method_priv; int init_phase2; int fast_reauth; @@ -338,9 +338,9 @@ struct eap_sm { Boolean rxResp /* LEAP only */; Boolean leap_done; Boolean peap_done; - u8 req_md5[16]; /* MD5() of the current EAP packet */ - u8 last_md5[16]; /* MD5() of the previously received EAP packet; used - * in duplicate request detection. */ + u8 req_sha1[20]; /* SHA1() of the current EAP packet */ + u8 last_sha1[20]; /* SHA1() of the previously received EAP packet; used + * in duplicate request detection. */ void *msg_ctx; void *scard_ctx; diff --git a/contrib/wpa/src/eap_peer/eap_mschapv2.c b/contrib/wpa/src/eap_peer/eap_mschapv2.c index 9e486e7..6acf1e8 100644 --- a/contrib/wpa/src/eap_peer/eap_mschapv2.c +++ b/contrib/wpa/src/eap_peer/eap_mschapv2.c @@ -511,6 +511,11 @@ static struct wpabuf * eap_mschapv2_change_password( struct eap_sm *sm, struct eap_mschapv2_data *data, struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req, u8 id) { +#ifdef CONFIG_NO_RC4 + wpa_printf(MSG_ERROR, + "EAP-MSCHAPV2: RC4 not support in the build - cannot change password"); + return NULL; +#else /* CONFIG_NO_RC4 */ struct wpabuf *resp; int ms_len; const u8 *username, *password, *new_password; @@ -628,6 +633,7 @@ static struct wpabuf * eap_mschapv2_change_password( fail: wpabuf_free(resp); return NULL; +#endif /* CONFIG_NO_RC4 */ } diff --git a/contrib/wpa/src/eap_peer/eap_pax.c b/contrib/wpa/src/eap_peer/eap_pax.c index 6d1ff20..c920bcd 100644 --- a/contrib/wpa/src/eap_peer/eap_pax.c +++ b/contrib/wpa/src/eap_peer/eap_pax.c @@ -333,7 +333,7 @@ static struct wpabuf * eap_pax_process(struct eap_sm *sm, void *priv, u16 flen, mlen; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, reqData, &len); - if (pos == NULL || len < EAP_PAX_ICV_LEN) { + if (pos == NULL || len < sizeof(*req) + EAP_PAX_ICV_LEN) { ret->ignore = TRUE; return NULL; } diff --git a/contrib/wpa/src/eap_peer/eap_peap.c b/contrib/wpa/src/eap_peer/eap_peap.c index 86a18bb..98a48a6c 100644 --- a/contrib/wpa/src/eap_peer/eap_peap.c +++ b/contrib/wpa/src/eap_peer/eap_peap.c @@ -968,6 +968,7 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, struct wpabuf *resp; const u8 *pos; struct eap_peap_data *data = priv; + struct wpabuf msg; pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_PEAP, ret, reqData, &left, &flags); @@ -998,18 +999,25 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, * should always be, anyway */ } + wpabuf_set(&msg, pos, left); + resp = NULL; if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) && !data->resuming) { - struct wpabuf msg; - wpabuf_set(&msg, pos, left); res = eap_peap_decrypt(sm, data, ret, req, &msg, &resp); } else { res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_PEAP, - data->peap_version, id, pos, - left, &resp); + data->peap_version, id, &msg, + &resp); + if (res < 0) { + wpa_printf(MSG_DEBUG, + "EAP-PEAP: TLS processing failed"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return resp; + } if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { char *label; wpa_printf(MSG_DEBUG, @@ -1077,14 +1085,12 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, } if (res == 2) { - struct wpabuf msg; /* * Application data included in the handshake message. */ wpabuf_free(data->pending_phase2_req); data->pending_phase2_req = resp; resp = NULL; - wpabuf_set(&msg, pos, left); res = eap_peap_decrypt(sm, data, ret, req, &msg, &resp); } diff --git a/contrib/wpa/src/eap_peer/eap_pwd.c b/contrib/wpa/src/eap_peer/eap_pwd.c index 059bbee..1f78544 100644 --- a/contrib/wpa/src/eap_peer/eap_pwd.c +++ b/contrib/wpa/src/eap_peer/eap_pwd.c @@ -10,6 +10,7 @@ #include "common.h" #include "crypto/sha256.h" +#include "crypto/ms_funcs.h" #include "eap_peer/eap_i.h" #include "eap_common/eap_pwd_common.h" @@ -25,6 +26,7 @@ struct eap_pwd_data { size_t id_server_len; u8 *password; size_t password_len; + int password_hash; u16 group_num; EAP_PWD_group *grp; @@ -86,8 +88,9 @@ static void * eap_pwd_init(struct eap_sm *sm) const u8 *identity, *password; size_t identity_len, password_len; int fragment_size; + int pwhash; - password = eap_get_config_password(sm, &password_len); + password = eap_get_config_password2(sm, &password_len, &pwhash); if (password == NULL) { wpa_printf(MSG_INFO, "EAP-PWD: No password configured!"); return NULL; @@ -129,6 +132,7 @@ static void * eap_pwd_init(struct eap_sm *sm) } os_memcpy(data->password, password, password_len); data->password_len = password_len; + data->password_hash = pwhash; data->out_frag_pos = data->in_frag_pos = 0; data->inbuf = data->outbuf = NULL; @@ -216,6 +220,10 @@ eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data, const u8 *payload, size_t payload_len) { struct eap_pwd_id *id; + const u8 *password; + size_t password_len; + u8 pwhashhash[16]; + int res; if (data->state != PWD_ID_Req) { ret->ignore = TRUE; @@ -231,6 +239,9 @@ eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data, id = (struct eap_pwd_id *) payload; data->group_num = be_to_host16(id->group_num); + wpa_printf(MSG_DEBUG, + "EAP-PWD: Server EAP-pwd-ID proposal: group=%u random=%u prf=%u prep=%u", + data->group_num, id->random_function, id->prf, id->prep); if ((id->random_function != EAP_PWD_DEFAULT_RAND_FUNC) || (id->prf != EAP_PWD_DEFAULT_PRF)) { ret->ignore = TRUE; @@ -238,6 +249,22 @@ eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data, return; } + if (id->prep != EAP_PWD_PREP_NONE && + id->prep != EAP_PWD_PREP_MS) { + wpa_printf(MSG_DEBUG, + "EAP-PWD: Unsupported password pre-processing technique (Prep=%u)", + id->prep); + eap_pwd_state(data, FAILURE); + return; + } + + if (id->prep == EAP_PWD_PREP_NONE && data->password_hash) { + wpa_printf(MSG_DEBUG, + "EAP-PWD: Unhashed password not available"); + eap_pwd_state(data, FAILURE); + return; + } + wpa_printf(MSG_DEBUG, "EAP-PWD (peer): using group %d", data->group_num); @@ -260,12 +287,46 @@ eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data, return; } + if (id->prep == EAP_PWD_PREP_MS) { +#ifdef CONFIG_FIPS + wpa_printf(MSG_ERROR, + "EAP-PWD (peer): MS password hash not supported in FIPS mode"); + eap_pwd_state(data, FAILURE); + return; +#else /* CONFIG_FIPS */ + if (data->password_hash) { + res = hash_nt_password_hash(data->password, pwhashhash); + } else { + u8 pwhash[16]; + + res = nt_password_hash(data->password, + data->password_len, pwhash); + if (res == 0) + res = hash_nt_password_hash(pwhash, pwhashhash); + os_memset(pwhash, 0, sizeof(pwhash)); + } + + if (res) { + eap_pwd_state(data, FAILURE); + return; + } + + password = pwhashhash; + password_len = sizeof(pwhashhash); +#endif /* CONFIG_FIPS */ + } else { + password = data->password; + password_len = data->password_len; + } + /* compute PWE */ - if (compute_password_element(data->grp, data->group_num, - data->password, data->password_len, - data->id_server, data->id_server_len, - data->id_peer, data->id_peer_len, - id->token)) { + res = compute_password_element(data->grp, data->group_num, + password, password_len, + data->id_server, data->id_server_len, + data->id_peer, data->id_peer_len, + id->token); + os_memset(pwhashhash, 0, sizeof(pwhashhash)); + if (res) { wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute PWE"); eap_pwd_state(data, FAILURE); return; @@ -301,6 +362,23 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, BIGNUM *mask = NULL, *x = NULL, *y = NULL, *cofactor = NULL; u16 offset; u8 *ptr, *scalar = NULL, *element = NULL; + size_t prime_len, order_len; + + if (data->state != PWD_Commit_Req) { + ret->ignore = TRUE; + goto fin; + } + + prime_len = BN_num_bytes(data->grp->prime); + order_len = BN_num_bytes(data->grp->order); + + if (payload_len != 2 * prime_len + order_len) { + wpa_printf(MSG_INFO, + "EAP-pwd: Unexpected Commit payload length %u (expected %u)", + (unsigned int) payload_len, + (unsigned int) (2 * prime_len + order_len)); + goto fin; + } if (((data->private_value = BN_new()) == NULL) || ((data->my_element = EC_POINT_new(data->grp->group)) == NULL) || @@ -500,6 +578,18 @@ eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data, u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr; int offset; + if (data->state != PWD_Confirm_Req) { + ret->ignore = TRUE; + goto fin; + } + + if (payload_len != SHA256_MAC_LEN) { + wpa_printf(MSG_INFO, + "EAP-pwd: Unexpected Confirm payload length %u (expected %u)", + (unsigned int) payload_len, SHA256_MAC_LEN); + goto fin; + } + /* * first build up the ciphersuite which is group | random_function | * prf @@ -783,17 +873,30 @@ eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, * if it's the first fragment there'll be a length field */ if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) { + if (len < 2) { + wpa_printf(MSG_DEBUG, + "EAP-pwd: Frame too short to contain Total-Length field"); + ret->ignore = TRUE; + return NULL; + } tot_len = WPA_GET_BE16(pos); wpa_printf(MSG_DEBUG, "EAP-pwd: Incoming fragments whose " "total length = %d", tot_len); if (tot_len > 15000) return NULL; + if (data->inbuf) { + wpa_printf(MSG_DEBUG, + "EAP-pwd: Unexpected new fragment start when previous fragment is still in use"); + ret->ignore = TRUE; + return NULL; + } data->inbuf = wpabuf_alloc(tot_len); if (data->inbuf == NULL) { wpa_printf(MSG_INFO, "Out of memory to buffer " "fragments!"); return NULL; } + data->in_frag_pos = 0; pos += sizeof(u16); len -= sizeof(u16); } @@ -873,6 +976,7 @@ eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, /* * we have output! Do we need to fragment it? */ + lm_exch = EAP_PWD_GET_EXCHANGE(lm_exch); len = wpabuf_len(data->outbuf); if ((len + EAP_PWD_HDR_SIZE) > data->mtu) { resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, data->mtu, diff --git a/contrib/wpa/src/eap_peer/eap_sake.c b/contrib/wpa/src/eap_peer/eap_sake.c index 7d14907..c4f9843 100644 --- a/contrib/wpa/src/eap_peer/eap_sake.c +++ b/contrib/wpa/src/eap_peer/eap_sake.c @@ -141,7 +141,7 @@ static struct wpabuf * eap_sake_build_msg(struct eap_sake_data *data, static struct wpabuf * eap_sake_process_identity(struct eap_sm *sm, struct eap_sake_data *data, struct eap_method_ret *ret, - const struct wpabuf *reqData, + u8 id, const u8 *payload, size_t payload_len) { @@ -166,8 +166,7 @@ static struct wpabuf * eap_sake_process_identity(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Identity"); - resp = eap_sake_build_msg(data, eap_get_id(reqData), - 2 + data->peerid_len, + resp = eap_sake_build_msg(data, id, 2 + data->peerid_len, EAP_SAKE_SUBTYPE_IDENTITY); if (resp == NULL) return NULL; @@ -185,7 +184,7 @@ static struct wpabuf * eap_sake_process_identity(struct eap_sm *sm, static struct wpabuf * eap_sake_process_challenge(struct eap_sm *sm, struct eap_sake_data *data, struct eap_method_ret *ret, - const struct wpabuf *reqData, + u8 id, const u8 *payload, size_t payload_len) { @@ -247,8 +246,7 @@ static struct wpabuf * eap_sake_process_challenge(struct eap_sm *sm, rlen = 2 + EAP_SAKE_RAND_LEN + 2 + EAP_SAKE_MIC_LEN; if (data->peerid) rlen += 2 + data->peerid_len; - resp = eap_sake_build_msg(data, eap_get_id(reqData), rlen, - EAP_SAKE_SUBTYPE_CHALLENGE); + resp = eap_sake_build_msg(data, id, rlen, EAP_SAKE_SUBTYPE_CHALLENGE); if (resp == NULL) return NULL; @@ -285,6 +283,7 @@ static struct wpabuf * eap_sake_process_challenge(struct eap_sm *sm, static struct wpabuf * eap_sake_process_confirm(struct eap_sm *sm, struct eap_sake_data *data, struct eap_method_ret *ret, + u8 id, const struct wpabuf *reqData, const u8 *payload, size_t payload_len) @@ -323,14 +322,13 @@ static struct wpabuf * eap_sake_process_confirm(struct eap_sm *sm, ret->allowNotifications = FALSE; wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending " "Response/Auth-Reject"); - return eap_sake_build_msg(data, eap_get_id(reqData), 0, + return eap_sake_build_msg(data, id, 0, EAP_SAKE_SUBTYPE_AUTH_REJECT); } wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Confirm"); - resp = eap_sake_build_msg(data, eap_get_id(reqData), - 2 + EAP_SAKE_MIC_LEN, + resp = eap_sake_build_msg(data, id, 2 + EAP_SAKE_MIC_LEN, EAP_SAKE_SUBTYPE_CONFIRM); if (resp == NULL) return NULL; @@ -367,7 +365,7 @@ static struct wpabuf * eap_sake_process(struct eap_sm *sm, void *priv, struct wpabuf *resp; const u8 *pos, *end; size_t len; - u8 subtype, session_id; + u8 subtype, session_id, id; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, reqData, &len); if (pos == NULL || len < sizeof(struct eap_sake_hdr)) { @@ -377,6 +375,7 @@ static struct wpabuf * eap_sake_process(struct eap_sm *sm, void *priv, req = (const struct eap_sake_hdr *) pos; end = pos + len; + id = eap_get_id(reqData); subtype = req->subtype; session_id = req->session_id; pos = (const u8 *) (req + 1); @@ -402,15 +401,15 @@ static struct wpabuf * eap_sake_process(struct eap_sm *sm, void *priv, switch (subtype) { case EAP_SAKE_SUBTYPE_IDENTITY: - resp = eap_sake_process_identity(sm, data, ret, reqData, + resp = eap_sake_process_identity(sm, data, ret, id, pos, end - pos); break; case EAP_SAKE_SUBTYPE_CHALLENGE: - resp = eap_sake_process_challenge(sm, data, ret, reqData, + resp = eap_sake_process_challenge(sm, data, ret, id, pos, end - pos); break; case EAP_SAKE_SUBTYPE_CONFIRM: - resp = eap_sake_process_confirm(sm, data, ret, reqData, + resp = eap_sake_process_confirm(sm, data, ret, id, reqData, pos, end - pos); break; default: diff --git a/contrib/wpa/src/eap_peer/eap_sim.c b/contrib/wpa/src/eap_peer/eap_sim.c index bd06df7..99a2816 100644 --- a/contrib/wpa/src/eap_peer/eap_sim.c +++ b/contrib/wpa/src/eap_peer/eap_sim.c @@ -1042,7 +1042,7 @@ static struct wpabuf * eap_sim_process(struct eap_sm *sm, void *priv, } pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, reqData, &len); - if (pos == NULL || len < 1) { + if (pos == NULL || len < 3) { ret->ignore = TRUE; return NULL; } diff --git a/contrib/wpa/src/eap_peer/eap_tls.c b/contrib/wpa/src/eap_peer/eap_tls.c index 5aa3fd5..66a027a 100644 --- a/contrib/wpa/src/eap_peer/eap_tls.c +++ b/contrib/wpa/src/eap_peer/eap_tls.c @@ -156,20 +156,6 @@ static struct wpabuf * eap_tls_failure(struct eap_sm *sm, ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; - if (res == -1) { - struct eap_peer_config *config = eap_get_config(sm); - if (config) { - /* - * The TLS handshake failed. So better forget the old - * PIN. It may be wrong, we cannot be sure but trying - * the wrong one again might block it on the card--so - * better ask the user again. - */ - os_free(config->pin); - config->pin = NULL; - } - } - if (resp) { /* * This is likely an alert message, so send it instead of just @@ -228,6 +214,7 @@ static struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv, u8 flags, id; const u8 *pos; struct eap_tls_data *data = priv; + struct wpabuf msg; pos = eap_peer_tls_process_init(sm, &data->ssl, data->eap_type, ret, reqData, &left, &flags); @@ -242,8 +229,9 @@ static struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv, } resp = NULL; + wpabuf_set(&msg, pos, left); res = eap_peer_tls_process_helper(sm, &data->ssl, data->eap_type, 0, - id, pos, left, &resp); + id, &msg, &resp); if (res < 0) { return eap_tls_failure(sm, data, ret, res, resp, id); diff --git a/contrib/wpa/src/eap_peer/eap_tls_common.c b/contrib/wpa/src/eap_peer/eap_tls_common.c index 8710781..af2b754 100644 --- a/contrib/wpa/src/eap_peer/eap_tls_common.c +++ b/contrib/wpa/src/eap_peer/eap_tls_common.c @@ -68,6 +68,10 @@ static void eap_tls_params_flags(struct tls_connection_params *params, params->flags |= TLS_CONN_DISABLE_SESSION_TICKET; if (os_strstr(txt, "tls_disable_session_ticket=0")) params->flags &= ~TLS_CONN_DISABLE_SESSION_TICKET; + if (os_strstr(txt, "tls_disable_tlsv1_0=1")) + params->flags |= TLS_CONN_DISABLE_TLSv1_0; + if (os_strstr(txt, "tls_disable_tlsv1_0=0")) + params->flags &= ~TLS_CONN_DISABLE_TLSv1_0; if (os_strstr(txt, "tls_disable_tlsv1_1=1")) params->flags |= TLS_CONN_DISABLE_TLSv1_1; if (os_strstr(txt, "tls_disable_tlsv1_1=0")) @@ -196,28 +200,25 @@ static int eap_tls_init_connection(struct eap_sm *sm, } res = tls_connection_set_params(data->ssl_ctx, data->conn, params); - if (res == TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED) { + if (res == TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN) { /* - * At this point with the pkcs11 engine the PIN might be wrong. - * We reset the PIN in the configuration to be sure to not use - * it again and the calling function must request a new one. + * At this point with the pkcs11 engine the PIN is wrong. We + * reset the PIN in the configuration to be sure to not use it + * again and the calling function must request a new one. */ + wpa_printf(MSG_INFO, + "TLS: Bad PIN provided, requesting a new one"); os_free(config->pin); config->pin = NULL; + eap_sm_request_pin(sm); + sm->ignore = TRUE; + } else if (res == TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED) { + wpa_printf(MSG_INFO, "TLS: Failed to initialize engine"); } else if (res == TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED) { wpa_printf(MSG_INFO, "TLS: Failed to load private key"); - /* - * We do not know exactly but maybe the PIN was wrong, - * so ask for a new one. - */ - os_free(config->pin); - config->pin = NULL; - eap_sm_request_pin(sm); sm->ignore = TRUE; - tls_connection_deinit(data->ssl_ctx, data->conn); - data->conn = NULL; - return -1; - } else if (res) { + } + if (res) { wpa_printf(MSG_INFO, "TLS: Failed to set TLS connection " "parameters"); tls_connection_deinit(data->ssl_ctx, data->conn); @@ -313,53 +314,19 @@ void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data) u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, const char *label, size_t len) { -#ifndef CONFIG_FIPS - struct tls_keys keys; -#endif /* CONFIG_FIPS */ - u8 *rnd = NULL, *out; + u8 *out; out = os_malloc(len); if (out == NULL) return NULL; - /* First, try to use TLS library function for PRF, if available. */ - if (tls_connection_prf(data->ssl_ctx, data->conn, label, 0, out, len) - == 0) - return out; - -#ifndef CONFIG_FIPS - /* - * 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 (tls_connection_get_keys(data->ssl_ctx, data->conn, &keys)) - goto fail; - - if (keys.client_random == NULL || keys.server_random == NULL || - keys.master_key == NULL) - goto fail; - - rnd = os_malloc(keys.client_random_len + keys.server_random_len); - if (rnd == NULL) - goto fail; - os_memcpy(rnd, keys.client_random, keys.client_random_len); - os_memcpy(rnd + keys.client_random_len, keys.server_random, - keys.server_random_len); - - if (tls_prf_sha1_md5(keys.master_key, keys.master_key_len, - label, rnd, keys.client_random_len + - keys.server_random_len, out, len)) - goto fail; + if (tls_connection_prf(data->ssl_ctx, data->conn, label, 0, 0, + out, len)) { + os_free(out); + return NULL; + } - os_free(rnd); return out; - -fail: -#endif /* CONFIG_FIPS */ - os_free(out); - os_free(rnd); - return NULL; } @@ -380,10 +347,10 @@ u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm, struct eap_ssl_data *data, u8 eap_type, size_t *len) { - struct tls_keys keys; + struct tls_random keys; u8 *out; - if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys)) + if (tls_connection_get_random(sm->ssl_ctx, data->conn, &keys)) return NULL; if (keys.client_random == NULL || keys.server_random == NULL) @@ -514,22 +481,19 @@ static const struct wpabuf * eap_peer_tls_data_reassemble( * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @data: Data for TLS processing * @in_data: Message received from the server - * @in_len: Length of in_data * @out_data: Buffer for returning a pointer to application data (if available) * Returns: 0 on success, 1 if more input data is needed, 2 if application data * is available, -1 on failure */ static int eap_tls_process_input(struct eap_sm *sm, struct eap_ssl_data *data, - const u8 *in_data, size_t in_len, + const struct wpabuf *in_data, struct wpabuf **out_data) { const struct wpabuf *msg; int need_more_input; struct wpabuf *appl_data; - struct wpabuf buf; - wpabuf_set(&buf, in_data, in_len); - msg = eap_peer_tls_data_reassemble(data, &buf, &need_more_input); + msg = eap_peer_tls_data_reassemble(data, in_data, &need_more_input); if (msg == NULL) return need_more_input ? 1 : -1; @@ -649,7 +613,6 @@ static int eap_tls_process_output(struct eap_ssl_data *data, EapType eap_type, * @peap_version: Version number for EAP-PEAP/TTLS * @id: EAP identifier for the response * @in_data: Message received from the server - * @in_len: Length of in_data * @out_data: Buffer for returning a pointer to the response message * Returns: 0 on success, 1 if more input data is needed, 2 if application data * is available, or -1 on failure @@ -672,14 +635,15 @@ static int eap_tls_process_output(struct eap_ssl_data *data, EapType eap_type, */ int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, EapType eap_type, int peap_version, - u8 id, const u8 *in_data, size_t in_len, + u8 id, const struct wpabuf *in_data, struct wpabuf **out_data) { int ret = 0; *out_data = NULL; - if (data->tls_out && wpabuf_len(data->tls_out) > 0 && in_len > 0) { + if (data->tls_out && wpabuf_len(data->tls_out) > 0 && + wpabuf_len(in_data) > 0) { wpa_printf(MSG_DEBUG, "SSL: Received non-ACK when output " "fragments are waiting to be sent out"); return -1; @@ -690,8 +654,7 @@ int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, * No more data to send out - expect to receive more data from * the AS. */ - int res = eap_tls_process_input(sm, data, in_data, in_len, - out_data); + int res = eap_tls_process_input(sm, data, in_data, out_data); if (res) { /* * Input processing failed (res = -1) or more data is @@ -719,12 +682,18 @@ int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, if (tls_connection_get_failed(data->ssl_ctx, data->conn)) { /* TLS processing has failed - return error */ wpa_printf(MSG_DEBUG, "SSL: Failed - tls_out available to " - "report error"); + "report error (len=%u)", + (unsigned int) wpabuf_len(data->tls_out)); ret = -1; /* TODO: clean pin if engine used? */ + if (wpabuf_len(data->tls_out) == 0) { + wpabuf_free(data->tls_out); + data->tls_out = NULL; + return -1; + } } - if (data->tls_out == NULL || wpabuf_len(data->tls_out) == 0) { + if (wpabuf_len(data->tls_out) == 0) { /* * TLS negotiation should now be complete since all other cases * needing more data should have been caught above based on @@ -790,20 +759,24 @@ int eap_peer_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data) int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data, char *buf, size_t buflen, int verbose) { - char name[128]; + char version[20], name[128]; int len = 0, ret; - if (tls_get_cipher(data->ssl_ctx, data->conn, name, sizeof(name)) == 0) - { - ret = os_snprintf(buf + len, buflen - len, - "EAP TLS cipher=%s\n" - "tls_session_reused=%d\n", - name, tls_connection_resumed(data->ssl_ctx, - data->conn)); - if (os_snprintf_error(buflen - len, ret)) - return len; - len += ret; - } + if (tls_get_version(data->ssl_ctx, data->conn, version, + sizeof(version)) < 0) + version[0] = '\0'; + if (tls_get_cipher(data->ssl_ctx, data->conn, name, sizeof(name)) < 0) + name[0] = '\0'; + + ret = os_snprintf(buf + len, buflen - len, + "eap_tls_version=%s\n" + "EAP TLS cipher=%s\n" + "tls_session_reused=%d\n", + version, name, + tls_connection_resumed(data->ssl_ctx, data->conn)); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; return len; } @@ -1032,7 +1005,7 @@ int eap_peer_select_phase2_methods(struct eap_peer_config *config, { char *start, *pos, *buf; struct eap_method_type *methods = NULL, *_methods; - u8 method; + u32 method; size_t num_methods = 0, prefix_len; if (config == NULL || config->phase2 == NULL) diff --git a/contrib/wpa/src/eap_peer/eap_tls_common.h b/contrib/wpa/src/eap_peer/eap_tls_common.h index 390c216..acd2b78 100644 --- a/contrib/wpa/src/eap_peer/eap_tls_common.h +++ b/contrib/wpa/src/eap_peer/eap_tls_common.h @@ -100,7 +100,7 @@ u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm, size_t *len); int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, EapType eap_type, int peap_version, - u8 id, const u8 *in_data, size_t in_len, + u8 id, const struct wpabuf *in_data, struct wpabuf **out_data); struct wpabuf * eap_peer_tls_build_ack(u8 id, EapType eap_type, int peap_version); diff --git a/contrib/wpa/src/eap_peer/eap_ttls.c b/contrib/wpa/src/eap_peer/eap_ttls.c index b5c028b..b186c91 100644 --- a/contrib/wpa/src/eap_peer/eap_ttls.c +++ b/contrib/wpa/src/eap_peer/eap_ttls.c @@ -175,7 +175,8 @@ static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id, } avp->avp_code = host_to_be32(avp_code); - avp->avp_length = host_to_be32((flags << 24) | (u32) (hdrlen + len)); + avp->avp_length = host_to_be32(((u32) flags << 24) | + (u32) (hdrlen + len)); return avphdr + hdrlen; } @@ -253,11 +254,13 @@ static int eap_ttls_v0_derive_key(struct eap_sm *sm, } +#ifndef CONFIG_FIPS static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm, struct eap_ttls_data *data, size_t len) { return eap_peer_tls_derive_key(sm, &data->ssl, "ttls challenge", len); } +#endif /* CONFIG_FIPS */ static void eap_ttls_phase2_select_eap_method(struct eap_ttls_data *data, @@ -428,6 +431,10 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, struct eap_method_ret *ret, struct wpabuf **resp) { +#ifdef CONFIG_FIPS + wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPV2 not supported in FIPS build"); + return -1; +#else /* CONFIG_FIPS */ #ifdef EAP_MSCHAPv2 struct wpabuf *msg; u8 *buf, *pos, *challenge, *peer_challenge; @@ -510,6 +517,7 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build"); return -1; #endif /* EAP_MSCHAPv2 */ +#endif /* CONFIG_FIPS */ } @@ -518,6 +526,10 @@ static int eap_ttls_phase2_request_mschap(struct eap_sm *sm, struct eap_method_ret *ret, struct wpabuf **resp) { +#ifdef CONFIG_FIPS + wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAP not supported in FIPS build"); + return -1; +#else /* CONFIG_FIPS */ struct wpabuf *msg; u8 *buf, *pos, *challenge; const u8 *identity, *password; @@ -592,6 +604,7 @@ static int eap_ttls_phase2_request_mschap(struct eap_sm *sm, ret->decision = DECISION_COND_SUCC; return 0; +#endif /* CONFIG_FIPS */ } @@ -654,6 +667,10 @@ static int eap_ttls_phase2_request_chap(struct eap_sm *sm, struct eap_method_ret *ret, struct wpabuf **resp) { +#ifdef CONFIG_FIPS + wpa_printf(MSG_ERROR, "EAP-TTLS: CHAP not supported in FIPS build"); + return -1; +#else /* CONFIG_FIPS */ struct wpabuf *msg; u8 *buf, *pos, *challenge; const u8 *identity, *password; @@ -722,6 +739,7 @@ static int eap_ttls_phase2_request_chap(struct eap_sm *sm, ret->decision = DECISION_COND_SUCC; return 0; +#endif /* CONFIG_FIPS */ } @@ -1385,14 +1403,20 @@ static int eap_ttls_process_handshake(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret, u8 identifier, - const u8 *in_data, size_t in_len, + const struct wpabuf *in_data, struct wpabuf **out_data) { int res; res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_TTLS, data->ttls_version, identifier, - in_data, in_len, out_data); + in_data, out_data); + if (res < 0) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS processing failed"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return -1; + } if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS done, proceed to " @@ -1419,15 +1443,13 @@ static int eap_ttls_process_handshake(struct eap_sm *sm, } if (res == 2) { - struct wpabuf msg; /* * Application data included in the handshake message. */ wpabuf_free(data->pending_phase2_req); data->pending_phase2_req = *out_data; *out_data = NULL; - wpabuf_set(&msg, in_data, in_len); - res = eap_ttls_decrypt(sm, data, ret, identifier, &msg, + res = eap_ttls_decrypt(sm, data, ret, identifier, in_data, out_data); } @@ -1477,6 +1499,7 @@ static struct wpabuf * eap_ttls_process(struct eap_sm *sm, void *priv, struct wpabuf *resp; const u8 *pos; struct eap_ttls_data *data = priv; + struct wpabuf msg; pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_TTLS, ret, reqData, &left, &flags); @@ -1497,15 +1520,15 @@ static struct wpabuf * eap_ttls_process(struct eap_sm *sm, void *priv, left = 0; } + wpabuf_set(&msg, pos, left); + resp = NULL; if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) && !data->resuming) { - struct wpabuf msg; - wpabuf_set(&msg, pos, left); res = eap_ttls_decrypt(sm, data, ret, id, &msg, &resp); } else { res = eap_ttls_process_handshake(sm, data, ret, id, - pos, left, &resp); + &msg, &resp); } eap_ttls_check_auth_status(sm, data, ret); diff --git a/contrib/wpa/src/eap_peer/eap_wsc.c b/contrib/wpa/src/eap_peer/eap_wsc.c index 7ce0a53..7ac99c7 100644 --- a/contrib/wpa/src/eap_peer/eap_wsc.c +++ b/contrib/wpa/src/eap_peer/eap_wsc.c @@ -557,6 +557,9 @@ send_msg: if (data->out_buf == NULL) { wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to receive " "message from WPS"); + eap_wsc_state(data, FAIL); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; return NULL; } data->out_used = 0; diff --git a/contrib/wpa/src/eap_server/eap.h b/contrib/wpa/src/eap_server/eap.h index 9de6cb6..69eaab8 100644 --- a/contrib/wpa/src/eap_server/eap.h +++ b/contrib/wpa/src/eap_server/eap.h @@ -131,6 +131,7 @@ struct eap_config { const u8 *server_id; size_t server_id_len; int erp; + unsigned int tls_session_lifetime; #ifdef CONFIG_TESTING_OPTIONS u32 tls_test_flags; @@ -139,7 +140,7 @@ struct eap_config { struct eap_sm * eap_server_sm_init(void *eapol_ctx, - struct eapol_callbacks *eapol_cb, + const struct eapol_callbacks *eapol_cb, struct eap_config *eap_conf); void eap_server_sm_deinit(struct eap_sm *sm); int eap_server_sm_step(struct eap_sm *sm); @@ -149,5 +150,8 @@ int eap_sm_method_pending(struct eap_sm *sm); const u8 * eap_get_identity(struct eap_sm *sm, size_t *len); struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm); void eap_server_clear_identity(struct eap_sm *sm); +void eap_server_mschap_rx_callback(struct eap_sm *sm, const char *source, + const u8 *username, size_t username_len, + const u8 *challenge, const u8 *response); #endif /* EAP_H */ diff --git a/contrib/wpa/src/eap_server/eap_i.h b/contrib/wpa/src/eap_server/eap_i.h index 7d72309..c90443d 100644 --- a/contrib/wpa/src/eap_server/eap_i.h +++ b/contrib/wpa/src/eap_server/eap_i.h @@ -155,7 +155,7 @@ struct eap_sm { /* not defined in RFC 4137 */ Boolean changed; void *eapol_ctx, *msg_ctx; - struct eapol_callbacks *eapol_cb; + const struct eapol_callbacks *eapol_cb; void *eap_method_priv; u8 *identity; size_t identity_len; @@ -210,6 +210,7 @@ struct eap_sm { Boolean initiate_reauth_start_sent; Boolean try_initiate_reauth; int erp; + unsigned int tls_session_lifetime; #ifdef CONFIG_TESTING_OPTIONS u32 tls_test_flags; diff --git a/contrib/wpa/src/eap_server/eap_server.c b/contrib/wpa/src/eap_server/eap_server.c index bd919e5..84ecafc 100644 --- a/contrib/wpa/src/eap_server/eap_server.c +++ b/contrib/wpa/src/eap_server/eap_server.c @@ -96,7 +96,8 @@ static struct wpabuf * eap_sm_buildInitiateReauthStart(struct eap_sm *sm, plen += 2 + domain_len; } - msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH_START, plen, + msg = eap_msg_alloc(EAP_VENDOR_IETF, + (EapType) EAP_ERP_TYPE_REAUTH_START, plen, EAP_CODE_INITIATE, id); if (msg == NULL) return NULL; @@ -714,8 +715,8 @@ static void erp_send_finish_reauth(struct eap_sm *sm, plen = 1 + 2 + 2 + os_strlen(nai); if (hash_len) plen += 1 + hash_len; - msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH, plen, - EAP_CODE_FINISH, id); + msg = eap_msg_alloc(EAP_VENDOR_IETF, (EapType) EAP_ERP_TYPE_REAUTH, + plen, EAP_CODE_FINISH, id); if (msg == NULL) return; wpabuf_put_u8(msg, flags); @@ -745,7 +746,7 @@ static void erp_send_finish_reauth(struct eap_sm *sm, wpabuf_free(sm->lastReqData); sm->lastReqData = NULL; - if (flags & 0x80) { + if ((flags & 0x80) || !erp) { sm->eap_if.eapFail = TRUE; wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE MACSTR, MAC2STR(sm->peer_addr)); @@ -799,7 +800,7 @@ SM_STATE(EAP, INITIATE_RECEIVED) sm->rxInitiate = FALSE; - pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH, + pos = eap_hdr_validate(EAP_VENDOR_IETF, (EapType) EAP_ERP_TYPE_REAUTH, sm->eap_if.eapRespData, &len); if (pos == NULL) { wpa_printf(MSG_INFO, "EAP-Initiate: Invalid frame"); @@ -1246,6 +1247,17 @@ SM_STEP(EAP) break; } SM_ENTER(EAP, SEND_REQUEST); + if (sm->eap_if.eapNoReq && !sm->eap_if.eapReq) { + /* + * This transition is not mentioned in RFC 4137, but it + * is needed to handle cleanly a case where EAP method + * buildReq fails. + */ + wpa_printf(MSG_DEBUG, + "EAP: Method did not return a request"); + SM_ENTER(EAP, FAILURE); + break; + } break; case EAP_METHOD_RESPONSE: /* @@ -1802,7 +1814,7 @@ static void eap_user_free(struct eap_user *user) * This function allocates and initializes an EAP state machine. */ struct eap_sm * eap_server_sm_init(void *eapol_ctx, - struct eapol_callbacks *eapol_cb, + const struct eapol_callbacks *eapol_cb, struct eap_config *conf) { struct eap_sm *sm; @@ -1853,6 +1865,7 @@ struct eap_sm * eap_server_sm_init(void *eapol_ctx, sm->server_id = conf->server_id; sm->server_id_len = conf->server_id_len; sm->erp = conf->erp; + sm->tls_session_lifetime = conf->tls_session_lifetime; #ifdef CONFIG_TESTING_OPTIONS sm->tls_test_flags = conf->tls_test_flags; @@ -1979,3 +1992,25 @@ void eap_server_clear_identity(struct eap_sm *sm) os_free(sm->identity); sm->identity = NULL; } + + +#ifdef CONFIG_TESTING_OPTIONS +void eap_server_mschap_rx_callback(struct eap_sm *sm, const char *source, + const u8 *username, size_t username_len, + const u8 *challenge, const u8 *response) +{ + char hex_challenge[30], hex_response[90], user[100]; + + /* Print out Challenge and Response in format supported by asleap. */ + if (username) + printf_encode(user, sizeof(user), username, username_len); + else + user[0] = '\0'; + wpa_snprintf_hex_sep(hex_challenge, sizeof(hex_challenge), + challenge, sizeof(challenge), ':'); + wpa_snprintf_hex_sep(hex_response, sizeof(hex_response), response, 24, + ':'); + wpa_printf(MSG_DEBUG, "[%s/user=%s] asleap -C %s -R %s", + source, user, hex_challenge, hex_response); +} +#endif /* CONFIG_TESTING_OPTIONS */ diff --git a/contrib/wpa/src/eap_server/eap_server_eke.c b/contrib/wpa/src/eap_server/eap_server_eke.c index 966f511..ba82be9 100644 --- a/contrib/wpa/src/eap_server/eap_server_eke.c +++ b/contrib/wpa/src/eap_server/eap_server_eke.c @@ -766,6 +766,29 @@ static Boolean eap_eke_isSuccess(struct eap_sm *sm, void *priv) } +static u8 * eap_eke_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_eke_data *data = priv; + u8 *sid; + size_t sid_len; + + if (data->state != SUCCESS) + return NULL; + + sid_len = 1 + 2 * data->sess.nonce_len; + sid = os_malloc(sid_len); + if (sid == NULL) + return NULL; + sid[0] = EAP_TYPE_EKE; + os_memcpy(sid + 1, data->nonce_p, data->sess.nonce_len); + os_memcpy(sid + 1 + data->sess.nonce_len, data->nonce_s, + data->sess.nonce_len); + *len = sid_len; + + return sid; +} + + int eap_server_eke_register(void) { struct eap_method *eap; @@ -785,6 +808,7 @@ int eap_server_eke_register(void) eap->getKey = eap_eke_getKey; eap->isSuccess = eap_eke_isSuccess; eap->get_emsk = eap_eke_get_emsk; + eap->getSessionId = eap_eke_get_session_id; ret = eap_server_method_register(eap); if (ret) diff --git a/contrib/wpa/src/eap_server/eap_server_fast.c b/contrib/wpa/src/eap_server/eap_server_fast.c index 6745100..bd9018e 100644 --- a/contrib/wpa/src/eap_server/eap_server_fast.c +++ b/contrib/wpa/src/eap_server/eap_server_fast.c @@ -428,7 +428,7 @@ static void * eap_fast_init(struct eap_sm *sm) } data->state = START; - if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) { + if (eap_server_tls_ssl_init(sm, &data->ssl, 0, EAP_TYPE_FAST)) { wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize SSL."); eap_fast_reset(sm, data); return NULL; diff --git a/contrib/wpa/src/eap_server/eap_server_mschapv2.c b/contrib/wpa/src/eap_server/eap_server_mschapv2.c index 05848d2..98d74e0 100644 --- a/contrib/wpa/src/eap_server/eap_server_mschapv2.c +++ b/contrib/wpa/src/eap_server/eap_server_mschapv2.c @@ -360,6 +360,19 @@ static void eap_mschapv2_process_response(struct eap_sm *sm, } } +#ifdef CONFIG_TESTING_OPTIONS + { + u8 challenge[8]; + + if (challenge_hash(peer_challenge, data->auth_challenge, + username, username_len, challenge) == 0) { + eap_server_mschap_rx_callback(sm, "EAP-MSCHAPV2", + username, username_len, + challenge, nt_response); + } + } +#endif /* CONFIG_TESTING_OPTIONS */ + if (username_len != user_len || os_memcmp(username, user, username_len) != 0) { wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Mismatch in user names"); diff --git a/contrib/wpa/src/eap_server/eap_server_peap.c b/contrib/wpa/src/eap_server/eap_server_peap.c index faa0fd2..51062b0 100644 --- a/contrib/wpa/src/eap_server/eap_server_peap.c +++ b/contrib/wpa/src/eap_server/eap_server_peap.c @@ -95,6 +95,37 @@ static void eap_peap_state(struct eap_peap_data *data, int state) eap_peap_state_txt(data->state), eap_peap_state_txt(state)); data->state = state; + if (state == FAILURE || state == FAILURE_REQ) + tls_connection_remove_session(data->ssl.conn); +} + + +static void eap_peap_valid_session(struct eap_sm *sm, + struct eap_peap_data *data) +{ + struct wpabuf *buf; + + if (!sm->tls_session_lifetime || + tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) + return; + + buf = wpabuf_alloc(1 + 1 + sm->identity_len); + if (!buf) + return; + wpabuf_put_u8(buf, EAP_TYPE_PEAP); + if (sm->identity) { + u8 id_len; + + if (sm->identity_len <= 255) + id_len = sm->identity_len; + else + id_len = 255; + wpabuf_put_u8(buf, id_len); + wpabuf_put_data(buf, sm->identity, id_len); + } else { + wpabuf_put_u8(buf, 0); + } + tls_connection_set_success_data(data->ssl.conn, buf); } @@ -151,7 +182,7 @@ static void * eap_peap_init(struct eap_sm *sm) data->state = START; data->crypto_binding = OPTIONAL_BINDING; - if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) { + if (eap_server_tls_ssl_init(sm, &data->ssl, 0, EAP_TYPE_PEAP)) { wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL."); eap_peap_reset(sm, data); return NULL; @@ -539,15 +570,14 @@ static Boolean eap_peap_check(struct eap_sm *sm, void *priv, static int eap_peap_phase2_init(struct eap_sm *sm, struct eap_peap_data *data, - EapType eap_type) + int vendor, EapType eap_type) { if (data->phase2_priv && data->phase2_method) { data->phase2_method->reset(sm, data->phase2_priv); data->phase2_method = NULL; data->phase2_priv = NULL; } - data->phase2_method = eap_server_get_eap_method(EAP_VENDOR_IETF, - eap_type); + data->phase2_method = eap_server_get_eap_method(vendor, eap_type); if (!data->phase2_method) return -1; @@ -709,10 +739,12 @@ static void eap_peap_process_phase2_tlv(struct eap_sm *sm, if (status == EAP_TLV_RESULT_SUCCESS) { wpa_printf(MSG_INFO, "EAP-PEAP: TLV Result - Success " "- requested %s", requested); - if (data->tlv_request == TLV_REQ_SUCCESS) + if (data->tlv_request == TLV_REQ_SUCCESS) { eap_peap_state(data, SUCCESS); - else + eap_peap_valid_session(sm, data); + } else { eap_peap_state(data, FAILURE); + } } else if (status == EAP_TLV_RESULT_FAILURE) { wpa_printf(MSG_INFO, "EAP-PEAP: TLV Result - Failure " @@ -737,7 +769,7 @@ static void eap_peap_process_phase2_soh(struct eap_sm *sm, const u8 *soh_tlv = NULL; size_t soh_tlv_len = 0; int tlv_type, mandatory, tlv_len, vtlv_len; - u8 next_type; + u32 next_type; u32 vendor_id; pos = eap_hdr_validate(EAP_VENDOR_MICROSOFT, 0x21, in_data, &left); @@ -852,8 +884,9 @@ auth_method: eap_peap_state(data, PHASE2_METHOD); next_type = sm->user->methods[0].method; sm->user_eap_method_index = 1; - wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP type %d", next_type); - eap_peap_phase2_init(sm, data, next_type); + wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP vendor %d type %d", + sm->user->methods[0].vendor, next_type); + eap_peap_phase2_init(sm, data, sm->user->methods[0].vendor, next_type); } #endif /* EAP_SERVER_TNC */ @@ -862,7 +895,8 @@ static void eap_peap_process_phase2_response(struct eap_sm *sm, struct eap_peap_data *data, struct wpabuf *in_data) { - u8 next_type = EAP_TYPE_NONE; + int next_vendor = EAP_VENDOR_IETF; + u32 next_type = EAP_TYPE_NONE; const struct eap_hdr *hdr; const u8 *pos; size_t left; @@ -894,17 +928,23 @@ static void eap_peap_process_phase2_response(struct eap_sm *sm, "allowed types", pos + 1, left - 1); eap_sm_process_nak(sm, pos + 1, left - 1); if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS && - sm->user->methods[sm->user_eap_method_index].method != - EAP_TYPE_NONE) { + (sm->user->methods[sm->user_eap_method_index].vendor != + EAP_VENDOR_IETF || + sm->user->methods[sm->user_eap_method_index].method != + EAP_TYPE_NONE)) { + next_vendor = sm->user->methods[ + sm->user_eap_method_index].vendor; next_type = sm->user->methods[ sm->user_eap_method_index++].method; - wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP type %d", - next_type); + wpa_printf(MSG_DEBUG, + "EAP-PEAP: try EAP vendor %d type 0x%x", + next_vendor, next_type); } else { eap_peap_req_failure(sm, data); + next_vendor = EAP_VENDOR_IETF; next_type = EAP_TYPE_NONE; } - eap_peap_phase2_init(sm, data, next_type); + eap_peap_phase2_init(sm, data, next_vendor, next_type); return; } @@ -929,8 +969,9 @@ static void eap_peap_process_phase2_response(struct eap_sm *sm, if (!data->phase2_method->isSuccess(sm, data->phase2_priv)) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 method failed"); eap_peap_req_failure(sm, data); + next_vendor = EAP_VENDOR_IETF; next_type = EAP_TYPE_NONE; - eap_peap_phase2_init(sm, data, next_type); + eap_peap_phase2_init(sm, data, next_vendor, next_type); return; } @@ -942,7 +983,8 @@ static void eap_peap_process_phase2_response(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 getKey " "failed"); eap_peap_req_failure(sm, data); - eap_peap_phase2_init(sm, data, EAP_TYPE_NONE); + eap_peap_phase2_init(sm, data, EAP_VENDOR_IETF, + EAP_TYPE_NONE); return; } } @@ -957,6 +999,7 @@ static void eap_peap_process_phase2_response(struct eap_sm *sm, "database", sm->identity, sm->identity_len); eap_peap_req_failure(sm, data); + next_vendor = EAP_VENDOR_IETF; next_type = EAP_TYPE_NONE; break; } @@ -967,18 +1010,22 @@ static void eap_peap_process_phase2_response(struct eap_sm *sm, eap_peap_state(data, PHASE2_SOH); wpa_printf(MSG_DEBUG, "EAP-PEAP: Try to initialize " "TNC (NAP SOH)"); + next_vendor = EAP_VENDOR_IETF; next_type = EAP_TYPE_NONE; break; } #endif /* EAP_SERVER_TNC */ eap_peap_state(data, PHASE2_METHOD); + next_vendor = sm->user->methods[0].vendor; next_type = sm->user->methods[0].method; sm->user_eap_method_index = 1; - wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP type %d", next_type); + wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP vendor %d type 0x%x", + next_vendor, next_type); break; case PHASE2_METHOD: eap_peap_req_success(sm, data); + next_vendor = EAP_VENDOR_IETF; next_type = EAP_TYPE_NONE; break; case FAILURE: @@ -989,7 +1036,7 @@ static void eap_peap_process_phase2_response(struct eap_sm *sm, break; } - eap_peap_phase2_init(sm, data, next_type); + eap_peap_phase2_init(sm, data, next_vendor, next_type); } @@ -1080,6 +1127,7 @@ static void eap_peap_process_phase2(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Success"); if (data->state == SUCCESS_REQ) { eap_peap_state(data, SUCCESS); + eap_peap_valid_session(sm, data); } break; case EAP_CODE_FAILURE: @@ -1133,7 +1181,8 @@ static void eap_peap_process_msg(struct eap_sm *sm, void *priv, break; case PHASE2_START: eap_peap_state(data, PHASE2_ID); - eap_peap_phase2_init(sm, data, EAP_TYPE_IDENTITY); + eap_peap_phase2_init(sm, data, EAP_VENDOR_IETF, + EAP_TYPE_IDENTITY); break; case PHASE1_ID2: case PHASE2_ID: @@ -1144,6 +1193,7 @@ static void eap_peap_process_msg(struct eap_sm *sm, void *priv, break; case SUCCESS_REQ: eap_peap_state(data, SUCCESS); + eap_peap_valid_session(sm, data); break; case FAILURE_REQ: eap_peap_state(data, FAILURE); @@ -1160,10 +1210,65 @@ static void eap_peap_process(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_peap_data *data = priv; + const struct wpabuf *buf; + const u8 *pos; + u8 id_len; + if (eap_server_tls_process(sm, &data->ssl, respData, data, EAP_TYPE_PEAP, eap_peap_process_version, - eap_peap_process_msg) < 0) + eap_peap_process_msg) < 0) { eap_peap_state(data, FAILURE); + return; + } + + if (data->state == SUCCESS || + !tls_connection_established(sm->ssl_ctx, data->ssl.conn) || + !tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) + return; + + buf = tls_connection_get_success_data(data->ssl.conn); + if (!buf || wpabuf_len(buf) < 2) { + wpa_printf(MSG_DEBUG, + "EAP-PEAP: No success data in resumed session - reject attempt"); + eap_peap_state(data, FAILURE); + return; + } + + pos = wpabuf_head(buf); + if (*pos != EAP_TYPE_PEAP) { + wpa_printf(MSG_DEBUG, + "EAP-PEAP: Resumed session for another EAP type (%u) - reject attempt", + *pos); + eap_peap_state(data, FAILURE); + return; + } + + pos++; + id_len = *pos++; + wpa_hexdump_ascii(MSG_DEBUG, "EAP-PEAP: Identity from cached session", + pos, id_len); + os_free(sm->identity); + sm->identity = os_malloc(id_len ? id_len : 1); + if (!sm->identity) { + sm->identity_len = 0; + eap_peap_state(data, FAILURE); + return; + } + + os_memcpy(sm->identity, pos, id_len); + sm->identity_len = id_len; + + if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP-PEAP: Phase2 Identity not found in the user database", + sm->identity, sm->identity_len); + eap_peap_state(data, FAILURE); + return; + } + + 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); } diff --git a/contrib/wpa/src/eap_server/eap_server_pwd.c b/contrib/wpa/src/eap_server/eap_server_pwd.c index 943af0d1..cb83ff7 100644 --- a/contrib/wpa/src/eap_server/eap_server_pwd.c +++ b/contrib/wpa/src/eap_server/eap_server_pwd.c @@ -10,6 +10,7 @@ #include "common.h" #include "crypto/sha256.h" +#include "crypto/ms_funcs.h" #include "eap_server/eap_i.h" #include "eap_common/eap_pwd_common.h" @@ -24,6 +25,7 @@ struct eap_pwd_data { size_t id_server_len; u8 *password; size_t password_len; + int password_hash; u32 token; u16 group_num; EAP_PWD_group *grp; @@ -112,6 +114,7 @@ static void * eap_pwd_init(struct eap_sm *sm) } data->password_len = sm->user->password_len; os_memcpy(data->password, sm->user->password, data->password_len); + data->password_hash = sm->user->password_hash; data->bnctx = BN_CTX_new(); if (data->bnctx == NULL) { @@ -181,7 +184,8 @@ static void eap_pwd_build_id_req(struct eap_sm *sm, struct eap_pwd_data *data, wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_RAND_FUNC); wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_PRF); wpabuf_put_data(data->outbuf, &data->token, sizeof(data->token)); - wpabuf_put_u8(data->outbuf, EAP_PWD_PREP_NONE); + wpabuf_put_u8(data->outbuf, data->password_hash ? EAP_PWD_PREP_MS : + EAP_PWD_PREP_NONE); wpabuf_put_data(data->outbuf, data->id_server, data->id_server_len); } @@ -579,6 +583,10 @@ static void eap_pwd_process_id_resp(struct eap_sm *sm, const u8 *payload, size_t payload_len) { struct eap_pwd_id *id; + const u8 *password; + size_t password_len; + u8 pwhashhash[16]; + int res; if (payload_len < sizeof(struct eap_pwd_id)) { wpa_printf(MSG_INFO, "EAP-pwd: Invalid ID response"); @@ -610,11 +618,25 @@ static void eap_pwd_process_id_resp(struct eap_sm *sm, "group"); return; } - if (compute_password_element(data->grp, data->group_num, - data->password, data->password_len, - data->id_server, data->id_server_len, - data->id_peer, data->id_peer_len, - (u8 *) &data->token)) { + + if (data->password_hash) { + res = hash_nt_password_hash(data->password, pwhashhash); + if (res) + return; + password = pwhashhash; + password_len = sizeof(pwhashhash); + } else { + password = data->password; + password_len = data->password_len; + } + + res = compute_password_element(data->grp, data->group_num, + password, password_len, + data->id_server, data->id_server_len, + data->id_peer, data->id_peer_len, + (u8 *) &data->token); + os_memset(pwhashhash, 0, sizeof(pwhashhash)); + if (res) { wpa_printf(MSG_INFO, "EAP-PWD (server): unable to compute " "PWE"); return; @@ -634,9 +656,21 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data, BIGNUM *x = NULL, *y = NULL, *cofactor = NULL; EC_POINT *K = NULL, *point = NULL; int res = 0; + size_t prime_len, order_len; wpa_printf(MSG_DEBUG, "EAP-pwd: Received commit response"); + prime_len = BN_num_bytes(data->grp->prime); + order_len = BN_num_bytes(data->grp->order); + + if (payload_len != 2 * prime_len + order_len) { + wpa_printf(MSG_INFO, + "EAP-pwd: Unexpected Commit payload length %u (expected %u)", + (unsigned int) payload_len, + (unsigned int) (2 * prime_len + order_len)); + goto fin; + } + if (((data->peer_scalar = BN_new()) == NULL) || ((data->k = BN_new()) == NULL) || ((cofactor = BN_new()) == NULL) || @@ -752,6 +786,13 @@ eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data, u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr; int offset; + if (payload_len != SHA256_MAC_LEN) { + wpa_printf(MSG_INFO, + "EAP-pwd: Unexpected Confirm payload length %u (expected %u)", + (unsigned int) payload_len, SHA256_MAC_LEN); + goto fin; + } + /* build up the ciphersuite: group | random_function | prf */ grp = htons(data->group_num); ptr = (u8 *) &cs; @@ -901,17 +942,28 @@ static void eap_pwd_process(struct eap_sm *sm, void *priv, * the first fragment has a total length */ if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) { + if (len < 2) { + wpa_printf(MSG_DEBUG, + "EAP-pwd: Frame too short to contain Total-Length field"); + return; + } tot_len = WPA_GET_BE16(pos); wpa_printf(MSG_DEBUG, "EAP-pwd: Incoming fragments, total " "length = %d", tot_len); if (tot_len > 15000) return; + if (data->inbuf) { + wpa_printf(MSG_DEBUG, + "EAP-pwd: Unexpected new fragment start when previous fragment is still in use"); + return; + } data->inbuf = wpabuf_alloc(tot_len); if (data->inbuf == NULL) { wpa_printf(MSG_INFO, "EAP-pwd: Out of memory to " "buffer fragments!"); return; } + data->in_frag_pos = 0; pos += sizeof(u16); len -= sizeof(u16); } diff --git a/contrib/wpa/src/eap_server/eap_server_tls.c b/contrib/wpa/src/eap_server/eap_server_tls.c index 58cfe8a..bd18a4b 100644 --- a/contrib/wpa/src/eap_server/eap_server_tls.c +++ b/contrib/wpa/src/eap_server/eap_server_tls.c @@ -48,6 +48,23 @@ static void eap_tls_state(struct eap_tls_data *data, int state) eap_tls_state_txt(data->state), eap_tls_state_txt(state)); data->state = state; + if (state == FAILURE) + tls_connection_remove_session(data->ssl.conn); +} + + +static void eap_tls_valid_session(struct eap_sm *sm, struct eap_tls_data *data) +{ + struct wpabuf *buf; + + if (!sm->tls_session_lifetime) + return; + + buf = wpabuf_alloc(1); + if (!buf) + return; + wpabuf_put_u8(buf, data->eap_type); + tls_connection_set_success_data(data->ssl.conn, buf); } @@ -60,7 +77,7 @@ static void * eap_tls_init(struct eap_sm *sm) return NULL; data->state = START; - if (eap_server_tls_ssl_init(sm, &data->ssl, 1)) { + if (eap_server_tls_ssl_init(sm, &data->ssl, 1, EAP_TYPE_TLS)) { wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); eap_tls_reset(sm, data); return NULL; @@ -82,7 +99,7 @@ static void * eap_unauth_tls_init(struct eap_sm *sm) return NULL; data->state = START; - if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) { + if (eap_server_tls_ssl_init(sm, &data->ssl, 0, EAP_UNAUTH_TLS_TYPE)) { wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); eap_tls_reset(sm, data); return NULL; @@ -104,7 +121,8 @@ static void * eap_wfa_unauth_tls_init(struct eap_sm *sm) return NULL; data->state = START; - if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) { + if (eap_server_tls_ssl_init(sm, &data->ssl, 0, + EAP_WFA_UNAUTH_TLS_TYPE)) { wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); eap_tls_reset(sm, data); return NULL; @@ -183,6 +201,7 @@ check_established: * fragments waiting to be sent out. */ wpa_printf(MSG_DEBUG, "EAP-TLS: Done"); eap_tls_state(data, SUCCESS); + eap_tls_valid_session(sm, data); } return res; @@ -234,10 +253,41 @@ static void eap_tls_process(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_tls_data *data = priv; + const struct wpabuf *buf; + const u8 *pos; + if (eap_server_tls_process(sm, &data->ssl, respData, data, data->eap_type, NULL, eap_tls_process_msg) < - 0) + 0) { eap_tls_state(data, FAILURE); + return; + } + + if (!tls_connection_established(sm->ssl_ctx, data->ssl.conn) || + !tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) + return; + + buf = tls_connection_get_success_data(data->ssl.conn); + if (!buf || wpabuf_len(buf) < 1) { + wpa_printf(MSG_DEBUG, + "EAP-TLS: No success data in resumed session - reject attempt"); + eap_tls_state(data, FAILURE); + return; + } + + pos = wpabuf_head(buf); + if (*pos != data->eap_type) { + wpa_printf(MSG_DEBUG, + "EAP-TLS: Resumed session for another EAP type (%u) - reject attempt", + *pos); + eap_tls_state(data, FAILURE); + return; + } + + wpa_printf(MSG_DEBUG, + "EAP-TLS: Resuming previous session"); + eap_tls_state(data, SUCCESS); + tls_connection_set_success_data_resumed(data->ssl.conn); } 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 56916c4..05677b7 100644 --- a/contrib/wpa/src/eap_server/eap_server_tls_common.c +++ b/contrib/wpa/src/eap_server/eap_server_tls_common.c @@ -44,8 +44,11 @@ static void eap_server_tls_log_cb(void *ctx, const char *msg) int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, - int verify_peer) + int verify_peer, int eap_type) { + u8 session_ctx[8]; + unsigned int flags = 0; + if (sm->ssl_ctx == NULL) { wpa_printf(MSG_ERROR, "TLS context not initialized - cannot use TLS-based EAP method"); return -1; @@ -68,7 +71,13 @@ int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, #endif /* CONFIG_TESTING_OPTIONS */ #endif /* CONFIG_TLS_INTERNAL */ - if (tls_connection_set_verify(sm->ssl_ctx, data->conn, verify_peer)) { + if (eap_type != EAP_TYPE_FAST) + flags |= TLS_CONN_DISABLE_SESSION_TICKET; + os_memcpy(session_ctx, "hostapd", 7); + session_ctx[7] = (u8) eap_type; + if (tls_connection_set_verify(sm->ssl_ctx, data->conn, verify_peer, + flags, session_ctx, + sizeof(session_ctx))) { wpa_printf(MSG_INFO, "SSL: Failed to configure verification " "of TLS peer certificate"); tls_connection_deinit(sm->ssl_ctx, data->conn); @@ -100,43 +109,19 @@ void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data) u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, char *label, size_t len) { - struct tls_keys keys; - u8 *rnd = NULL, *out; + u8 *out; out = os_malloc(len); if (out == NULL) return NULL; - if (tls_connection_prf(sm->ssl_ctx, data->conn, label, 0, out, len) == - 0) - return out; - - if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys)) - goto fail; - - if (keys.client_random == NULL || keys.server_random == NULL || - keys.master_key == NULL) - goto fail; - - rnd = os_malloc(keys.client_random_len + keys.server_random_len); - if (rnd == NULL) - goto fail; - os_memcpy(rnd, keys.client_random, keys.client_random_len); - os_memcpy(rnd + keys.client_random_len, keys.server_random, - keys.server_random_len); - - if (tls_prf_sha1_md5(keys.master_key, keys.master_key_len, - label, rnd, keys.client_random_len + - keys.server_random_len, out, len)) - goto fail; + if (tls_connection_prf(sm->ssl_ctx, data->conn, label, 0, 0, + out, len)) { + os_free(out); + return NULL; + } - os_free(rnd); return out; - -fail: - os_free(out); - os_free(rnd); - return NULL; } @@ -157,10 +142,10 @@ u8 * eap_server_tls_derive_session_id(struct eap_sm *sm, struct eap_ssl_data *data, u8 eap_type, size_t *len) { - struct tls_keys keys; + struct tls_random keys; u8 *out; - if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys)) + if (tls_connection_get_random(sm->ssl_ctx, data->conn, &keys)) return NULL; if (keys.client_random == NULL || keys.server_random == NULL) diff --git a/contrib/wpa/src/eap_server/eap_server_ttls.c b/contrib/wpa/src/eap_server/eap_server_ttls.c index 12a31b0..53ffa1e 100644 --- a/contrib/wpa/src/eap_server/eap_server_ttls.c +++ b/contrib/wpa/src/eap_server/eap_server_ttls.c @@ -71,6 +71,36 @@ static void eap_ttls_state(struct eap_ttls_data *data, int state) eap_ttls_state_txt(data->state), eap_ttls_state_txt(state)); data->state = state; + if (state == FAILURE) + tls_connection_remove_session(data->ssl.conn); +} + + +static void eap_ttls_valid_session(struct eap_sm *sm, + struct eap_ttls_data *data) +{ + struct wpabuf *buf; + + if (!sm->tls_session_lifetime) + return; + + buf = wpabuf_alloc(1 + 1 + sm->identity_len); + if (!buf) + return; + wpabuf_put_u8(buf, EAP_TYPE_TTLS); + if (sm->identity) { + u8 id_len; + + if (sm->identity_len <= 255) + id_len = sm->identity_len; + else + id_len = 255; + wpabuf_put_u8(buf, id_len); + wpabuf_put_data(buf, sm->identity, id_len); + } else { + wpabuf_put_u8(buf, 0); + } + tls_connection_set_success_data(data->ssl.conn, buf); } @@ -317,7 +347,7 @@ static void * eap_ttls_init(struct eap_sm *sm) data->ttls_version = EAP_TTLS_VERSION; data->state = START; - if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) { + if (eap_server_tls_ssl_init(sm, &data->ssl, 0, EAP_TYPE_TTLS)) { wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL."); eap_ttls_reset(sm, data); return NULL; @@ -518,6 +548,7 @@ static void eap_ttls_process_phase2_pap(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: Correct user password"); eap_ttls_state(data, SUCCESS); + eap_ttls_valid_session(sm, data); } @@ -576,6 +607,7 @@ static void eap_ttls_process_phase2_chap(struct eap_sm *sm, 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Correct user password"); eap_ttls_state(data, SUCCESS); + eap_ttls_valid_session(sm, data); } else { wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Invalid user password"); eap_ttls_state(data, FAILURE); @@ -618,6 +650,12 @@ static void eap_ttls_process_phase2_mschap(struct eap_sm *sm, return; } +#ifdef CONFIG_TESTING_OPTIONS + eap_server_mschap_rx_callback(sm, "TTLS-MSCHAP", + sm->identity, sm->identity_len, + challenge, response + 2 + 24); +#endif /* CONFIG_TESTING_OPTIONS */ + if (os_memcmp_const(challenge, chal, EAP_TTLS_MSCHAP_CHALLENGE_LEN) != 0 || response[0] != chal[EAP_TTLS_MSCHAP_CHALLENGE_LEN]) { @@ -637,6 +675,7 @@ static void eap_ttls_process_phase2_mschap(struct eap_sm *sm, if (os_memcmp_const(nt_response, response + 2 + 24, 24) == 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Correct response"); eap_ttls_state(data, SUCCESS); + eap_ttls_valid_session(sm, data); } else { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Invalid NT-Response"); wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAP: Received", @@ -740,6 +779,18 @@ static void eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, } rx_resp = response + 2 + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 8; +#ifdef CONFIG_TESTING_OPTIONS + { + u8 challenge2[8]; + + if (challenge_hash(peer_challenge, auth_challenge, + username, username_len, challenge2) == 0) { + eap_server_mschap_rx_callback(sm, "TTLS-MSCHAPV2", + username, username_len, + challenge2, rx_resp); + } + } +#endif /* CONFIG_TESTING_OPTIONS */ if (os_memcmp_const(nt_response, rx_resp, 24) == 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Correct " "NT-Response"); @@ -888,6 +939,7 @@ static void eap_ttls_process_phase2_eap_response(struct eap_sm *sm, break; case PHASE2_METHOD: eap_ttls_state(data, SUCCESS); + eap_ttls_valid_session(sm, data); break; case FAILURE: break; @@ -1111,6 +1163,7 @@ static void eap_ttls_process_msg(struct eap_sm *sm, void *priv, wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer " "acknowledged response"); eap_ttls_state(data, SUCCESS); + eap_ttls_valid_session(sm, data); } else if (!data->mschapv2_resp_ok) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer " "acknowledged error"); @@ -1137,10 +1190,64 @@ static void eap_ttls_process(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_ttls_data *data = priv; + const struct wpabuf *buf; + const u8 *pos; + u8 id_len; + if (eap_server_tls_process(sm, &data->ssl, respData, data, EAP_TYPE_TTLS, eap_ttls_process_version, - eap_ttls_process_msg) < 0) + eap_ttls_process_msg) < 0) { + eap_ttls_state(data, FAILURE); + return; + } + + if (!tls_connection_established(sm->ssl_ctx, data->ssl.conn) || + !tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) + return; + + buf = tls_connection_get_success_data(data->ssl.conn); + if (!buf || wpabuf_len(buf) < 1) { + wpa_printf(MSG_DEBUG, + "EAP-TTLS: No success data in resumed session - reject attempt"); + eap_ttls_state(data, FAILURE); + return; + } + + pos = wpabuf_head(buf); + if (*pos != EAP_TYPE_TTLS) { + wpa_printf(MSG_DEBUG, + "EAP-TTLS: Resumed session for another EAP type (%u) - reject attempt", + *pos); + eap_ttls_state(data, FAILURE); + return; + } + + pos++; + id_len = *pos++; + wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: Identity from cached session", + pos, id_len); + os_free(sm->identity); + sm->identity = os_malloc(id_len ? id_len : 1); + if (!sm->identity) { + sm->identity_len = 0; eap_ttls_state(data, FAILURE); + return; + } + + os_memcpy(sm->identity, pos, id_len); + sm->identity_len = id_len; + + if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: Phase2 Identity not found in the user database", + sm->identity, sm->identity_len); + eap_ttls_state(data, FAILURE); + return; + } + + wpa_printf(MSG_DEBUG, + "EAP-TTLS: Resuming previous session - skip Phase2"); + eap_ttls_state(data, SUCCESS); + tls_connection_set_success_data_resumed(data->ssl.conn); } diff --git a/contrib/wpa/src/eap_server/eap_tls_common.h b/contrib/wpa/src/eap_server/eap_tls_common.h index ddf90b8..dc943eb 100644 --- a/contrib/wpa/src/eap_server/eap_tls_common.h +++ b/contrib/wpa/src/eap_server/eap_tls_common.h @@ -70,7 +70,7 @@ struct eap_ssl_data { struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len, u8 code, u8 identifier); int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, - int verify_peer); + int verify_peer, int eap_type); void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data); u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, char *label, size_t len); diff --git a/contrib/wpa/src/eapol_auth/eapol_auth_sm.c b/contrib/wpa/src/eapol_auth/eapol_auth_sm.c index 0df6eb5..ff33d28 100644 --- a/contrib/wpa/src/eapol_auth/eapol_auth_sm.c +++ b/contrib/wpa/src/eapol_auth/eapol_auth_sm.c @@ -1,6 +1,6 @@ /* * IEEE 802.1X-2004 Authenticator - EAPOL state machine - * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -22,7 +22,7 @@ #define STATE_MACHINE_DEBUG_PREFIX "IEEE 802.1X" #define STATE_MACHINE_ADDR sm->addr -static struct eapol_callbacks eapol_cb; +static const struct eapol_callbacks eapol_cb; /* EAPOL state machines are described in IEEE Std 802.1X-2004, Chap. 8.2 */ @@ -198,6 +198,18 @@ SM_STATE(AUTH_PAE, INITIALIZE) { SM_ENTRY_MA(AUTH_PAE, INITIALIZE, auth_pae); sm->portMode = Auto; + + /* + * Clearing keyRun here is not specified in IEEE Std 802.1X-2004, but + * it looks like this would be logical thing to do here since the + * EAPOL-Key exchange is not possible in this state. It is possible to + * get here on disconnection event without advancing to the + * AUTHENTICATING state to clear keyRun before the IEEE 802.11 RSN + * authenticator state machine runs and that may advance from + * AUTHENTICATION2 to INITPMK if keyRun = TRUE has been left from the + * last association. This can be avoided by clearing keyRun here. + */ + sm->keyRun = FALSE; } @@ -835,6 +847,7 @@ eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, eap_conf.server_id = eapol->conf.server_id; eap_conf.server_id_len = eapol->conf.server_id_len; eap_conf.erp = eapol->conf.erp; + eap_conf.tls_session_lifetime = eapol->conf.tls_session_lifetime; sm->eap = eap_server_sm_init(sm, &eapol_cb, &eap_conf); if (sm->eap == NULL) { eapol_auth_free(sm); @@ -1056,7 +1069,7 @@ static int eapol_sm_erp_add_key(void *ctx, struct eap_server_erp_key *erp) } -static struct eapol_callbacks eapol_cb = +static const struct eapol_callbacks eapol_cb = { eapol_sm_get_eap_user, eapol_sm_get_eap_req_id_text, @@ -1080,6 +1093,87 @@ int eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx) } +void eapol_auth_reauthenticate(struct eapol_state_machine *sm) +{ + wpa_printf(MSG_DEBUG, "EAPOL: External reauthentication trigger for " + MACSTR, MAC2STR(sm->addr)); + sm->reAuthenticate = TRUE; + eapol_auth_step(sm); +} + + +int eapol_auth_set_conf(struct eapol_state_machine *sm, const char *param, + const char *value) +{ + wpa_printf(MSG_DEBUG, "EAPOL: External configuration operation for " + MACSTR " - param=%s value=%s", + MAC2STR(sm->addr), param, value); + + if (os_strcasecmp(param, "AdminControlledDirections") == 0) { + if (os_strcmp(value, "Both") == 0) + sm->adminControlledDirections = Both; + else if (os_strcmp(value, "In") == 0) + sm->adminControlledDirections = In; + else + return -1; + eapol_auth_step(sm); + return 0; + } + + if (os_strcasecmp(param, "AdminControlledPortControl") == 0) { + if (os_strcmp(value, "ForceAuthorized") == 0) + sm->portControl = ForceAuthorized; + else if (os_strcmp(value, "ForceUnauthorized") == 0) + sm->portControl = ForceUnauthorized; + else if (os_strcmp(value, "Auto") == 0) + sm->portControl = Auto; + else + return -1; + eapol_auth_step(sm); + return 0; + } + + if (os_strcasecmp(param, "quietPeriod") == 0) { + sm->quietPeriod = atoi(value); + return 0; + } + + if (os_strcasecmp(param, "serverTimeout") == 0) { + sm->serverTimeout = atoi(value); + return 0; + } + + if (os_strcasecmp(param, "reAuthPeriod") == 0) { + sm->reAuthPeriod = atoi(value); + return 0; + } + + if (os_strcasecmp(param, "reAuthEnabled") == 0) { + if (os_strcmp(value, "TRUE") == 0) + sm->reAuthEnabled = TRUE; + else if (os_strcmp(value, "FALSE") == 0) + sm->reAuthEnabled = FALSE; + else + return -1; + eapol_auth_step(sm); + return 0; + } + + if (os_strcasecmp(param, "KeyTransmissionEnabled") == 0) { + if (os_strcmp(value, "TRUE") == 0) + sm->keyTxEnabled = TRUE; + else if (os_strcmp(value, "FALSE") == 0) + sm->keyTxEnabled = FALSE; + else + return -1; + eapol_auth_step(sm); + return 0; + } + + return -1; +} + + static int eapol_auth_conf_clone(struct eapol_auth_config *dst, struct eapol_auth_config *src) { @@ -1148,6 +1242,7 @@ static int eapol_auth_conf_clone(struct eapol_auth_config *dst, } dst->erp_send_reauth_start = src->erp_send_reauth_start; dst->erp = src->erp; + dst->tls_session_lifetime = src->tls_session_lifetime; return 0; diff --git a/contrib/wpa/src/eapol_auth/eapol_auth_sm.h b/contrib/wpa/src/eapol_auth/eapol_auth_sm.h index ebed19a..e1974e4 100644 --- a/contrib/wpa/src/eapol_auth/eapol_auth_sm.h +++ b/contrib/wpa/src/eapol_auth/eapol_auth_sm.h @@ -1,6 +1,6 @@ /* * IEEE 802.1X-2004 Authenticator - EAPOL state machine - * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -27,6 +27,7 @@ struct eapol_auth_config { int erp_send_reauth_start; char *erp_domain; /* a copy of this will be allocated */ int erp; /* Whether ERP is enabled on authentication server */ + unsigned int tls_session_lifetime; u8 *pac_opaque_encr_key; u8 *eap_fast_a_id; size_t eap_fast_a_id_len; @@ -94,5 +95,8 @@ void eapol_auth_step(struct eapol_state_machine *sm); int eapol_auth_dump_state(struct eapol_state_machine *sm, char *buf, size_t buflen); int eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx); +void eapol_auth_reauthenticate(struct eapol_state_machine *sm); +int eapol_auth_set_conf(struct eapol_state_machine *sm, const char *param, + const char *value); #endif /* EAPOL_AUTH_SM_H */ diff --git a/contrib/wpa/src/eapol_supp/eapol_supp_sm.c b/contrib/wpa/src/eapol_supp/eapol_supp_sm.c index 9cc234a..09cf4f6 100644 --- a/contrib/wpa/src/eapol_supp/eapol_supp_sm.c +++ b/contrib/wpa/src/eapol_supp/eapol_supp_sm.c @@ -244,7 +244,8 @@ SM_STATE(SUPP_PAE, DISCONNECTED) SM_STATE(SUPP_PAE, CONNECTING) { - int send_start = sm->SUPP_PAE_state == SUPP_PAE_CONNECTING; + int send_start = sm->SUPP_PAE_state == SUPP_PAE_CONNECTING || + sm->SUPP_PAE_state == SUPP_PAE_HELD; SM_ENTRY(SUPP_PAE, CONNECTING); if (sm->eapTriggerStart) @@ -653,7 +654,9 @@ static void eapol_sm_processKey(struct eapol_sm *sm) struct ieee802_1x_eapol_key *key; struct eap_key_data keydata; u8 orig_key_sign[IEEE8021X_KEY_SIGN_LEN], datakey[32]; +#ifndef CONFIG_NO_RC4 u8 ekey[IEEE8021X_KEY_IV_LEN + IEEE8021X_ENCR_KEY_LEN]; +#endif /* CONFIG_NO_RC4 */ int key_len, res, sign_key_len, encr_key_len; u16 rx_key_length; size_t plen; @@ -747,6 +750,13 @@ static void eapol_sm_processKey(struct eapol_sm *sm) return; } if (key_len == rx_key_length) { +#ifdef CONFIG_NO_RC4 + if (encr_key_len) { + /* otherwise unused */ + } + wpa_printf(MSG_ERROR, "EAPOL: RC4 not supported in the build"); + return; +#else /* CONFIG_NO_RC4 */ os_memcpy(ekey, key->key_iv, IEEE8021X_KEY_IV_LEN); os_memcpy(ekey + IEEE8021X_KEY_IV_LEN, keydata.encr_key, encr_key_len); @@ -755,6 +765,7 @@ static void eapol_sm_processKey(struct eapol_sm *sm) datakey, key_len); wpa_hexdump_key(MSG_DEBUG, "EAPOL: Decrypted(RC4) key", datakey, key_len); +#endif /* CONFIG_NO_RC4 */ } else if (key_len == 0) { /* * IEEE 802.1X-2004 specifies that least significant Key Length @@ -1997,7 +2008,7 @@ static void eapol_sm_set_anon_id(void *ctx, const u8 *id, size_t len) } -static struct eapol_callbacks eapol_cb = +static const struct eapol_callbacks eapol_cb = { eapol_sm_get_config, eapol_sm_get_bool, diff --git a/contrib/wpa/src/fst/Makefile b/contrib/wpa/src/fst/Makefile new file mode 100644 index 0000000..9c41962 --- /dev/null +++ b/contrib/wpa/src/fst/Makefile @@ -0,0 +1,8 @@ +all: + @echo Nothing to be made. + +clean: + rm -f *~ *.o *.d + +install: + @echo Nothing to be made. diff --git a/contrib/wpa/src/fst/fst.c b/contrib/wpa/src/fst/fst.c new file mode 100644 index 0000000..2880870 --- /dev/null +++ b/contrib/wpa/src/fst/fst.c @@ -0,0 +1,225 @@ +/* + * FST module implementation + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "fst/fst.h" +#include "fst/fst_internal.h" +#include "fst/fst_defs.h" +#include "fst/fst_ctrl_iface.h" + +struct dl_list fst_global_ctrls_list; + + +static void fst_ctrl_iface_notify_peer_state_change(struct fst_iface *iface, + Boolean connected, + const u8 *peer_addr) +{ + union fst_event_extra extra; + + extra.peer_state.connected = connected; + os_strlcpy(extra.peer_state.ifname, fst_iface_get_name(iface), + sizeof(extra.peer_state.ifname)); + os_memcpy(extra.peer_state.addr, peer_addr, ETH_ALEN); + + foreach_fst_ctrl_call(on_event, EVENT_PEER_STATE_CHANGED, + iface, NULL, &extra); +} + + +struct fst_iface * fst_attach(const char *ifname, const u8 *own_addr, + const struct fst_wpa_obj *iface_obj, + const struct fst_iface_cfg *cfg) +{ + struct fst_group *g; + struct fst_group *group = NULL; + struct fst_iface *iface = NULL; + Boolean new_group = FALSE; + + WPA_ASSERT(ifname != NULL); + WPA_ASSERT(iface_obj != NULL); + WPA_ASSERT(cfg != NULL); + + foreach_fst_group(g) { + if (os_strcmp(cfg->group_id, fst_group_get_id(g)) == 0) { + group = g; + break; + } + } + + if (!group) { + group = fst_group_create(cfg->group_id); + if (!group) { + fst_printf(MSG_ERROR, "%s: FST group cannot be created", + cfg->group_id); + return NULL; + } + new_group = TRUE; + } + + iface = fst_iface_create(group, ifname, own_addr, iface_obj, cfg); + if (!iface) { + fst_printf_group(group, MSG_ERROR, "cannot create iface for %s", + ifname); + if (new_group) + fst_group_delete(group); + return NULL; + } + + fst_group_attach_iface(group, iface); + fst_group_update_ie(group); + + foreach_fst_ctrl_call(on_iface_added, iface); + + fst_printf_iface(iface, MSG_DEBUG, + "iface attached to group %s (prio=%d, llt=%d)", + cfg->group_id, cfg->priority, cfg->llt); + + return iface; +} + + +void fst_detach(struct fst_iface *iface) +{ + struct fst_group *group = fst_iface_get_group(iface); + + fst_printf_iface(iface, MSG_DEBUG, "iface detached from group %s", + fst_group_get_id(group)); + fst_session_global_on_iface_detached(iface); + foreach_fst_ctrl_call(on_iface_removed, iface); + fst_group_detach_iface(group, iface); + fst_iface_delete(iface); + fst_group_update_ie(group); + fst_group_delete_if_empty(group); +} + + +int fst_global_init(void) +{ + dl_list_init(&fst_global_groups_list); + dl_list_init(&fst_global_ctrls_list); + fst_session_global_init(); + return 0; +} + + +void fst_global_deinit(void) +{ + struct fst_group *group; + struct fst_ctrl_handle *h; + + fst_session_global_deinit(); + while ((group = fst_first_group()) != NULL) + fst_group_delete(group); + while ((h = dl_list_first(&fst_global_ctrls_list, + struct fst_ctrl_handle, + global_ctrls_lentry))) + fst_global_del_ctrl(h); +} + + +struct fst_ctrl_handle * fst_global_add_ctrl(const struct fst_ctrl *ctrl) +{ + struct fst_ctrl_handle *h; + + if (!ctrl) + return NULL; + + h = os_zalloc(sizeof(*h)); + if (!h) + return NULL; + + if (ctrl->init && ctrl->init()) { + os_free(h); + return NULL; + } + + h->ctrl = *ctrl; + dl_list_add_tail(&fst_global_ctrls_list, &h->global_ctrls_lentry); + + return h; +} + + +void fst_global_del_ctrl(struct fst_ctrl_handle *h) +{ + dl_list_del(&h->global_ctrls_lentry); + if (h->ctrl.deinit) + h->ctrl.deinit(); + os_free(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)) + fst_session_on_action_rx(iface, mgmt, len); + else + wpa_printf(MSG_DEBUG, + "FST: Ignore FST Action frame - no FST connection with " + MACSTR, MAC2STR(mgmt->sa)); +} + + +void fst_notify_peer_connected(struct fst_iface *iface, const u8 *addr) +{ + if (is_zero_ether_addr(addr)) + return; + +#ifndef HOSTAPD + fst_group_update_ie(fst_iface_get_group(iface)); +#endif /* HOSTAPD */ + + fst_printf_iface(iface, MSG_DEBUG, MACSTR " became connected", + MAC2STR(addr)); + + fst_ctrl_iface_notify_peer_state_change(iface, TRUE, addr); +} + + +void fst_notify_peer_disconnected(struct fst_iface *iface, const u8 *addr) +{ + if (is_zero_ether_addr(addr)) + return; + +#ifndef HOSTAPD + fst_group_update_ie(fst_iface_get_group(iface)); +#endif /* HOSTAPD */ + + fst_printf_iface(iface, MSG_DEBUG, MACSTR " became disconnected", + MAC2STR(addr)); + + fst_ctrl_iface_notify_peer_state_change(iface, FALSE, addr); +} + + +Boolean fst_are_ifaces_aggregated(struct fst_iface *iface1, + struct fst_iface *iface2) +{ + return fst_iface_get_group(iface1) == fst_iface_get_group(iface2); +} + + +enum mb_band_id fst_hw_mode_to_band(enum hostapd_hw_mode mode) +{ + switch (mode) { + case HOSTAPD_MODE_IEEE80211B: + case HOSTAPD_MODE_IEEE80211G: + return MB_BAND_ID_WIFI_2_4GHZ; + case HOSTAPD_MODE_IEEE80211A: + return MB_BAND_ID_WIFI_5GHZ; + case HOSTAPD_MODE_IEEE80211AD: + return MB_BAND_ID_WIFI_60GHZ; + default: + WPA_ASSERT(0); + return MB_BAND_ID_WIFI_2_4GHZ; + } +} diff --git a/contrib/wpa/src/fst/fst.h b/contrib/wpa/src/fst/fst.h new file mode 100644 index 0000000..0c0e435 --- /dev/null +++ b/contrib/wpa/src/fst/fst.h @@ -0,0 +1,296 @@ +/* + * FST module - interface definitions + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef FST_H +#define FST_H + +#ifdef CONFIG_FST + +#include "common/defs.h" +#include "fst/fst_ctrl_iface.h" + +/* FST module hostap integration API */ + +#define US_IN_MS 1000 +#define LLT_UNIT_US 32 /* See 10.32.2.2 Transitioning between states */ + +#define FST_LLT_MS_TO_VAL(m) (((u32) (m)) * US_IN_MS / LLT_UNIT_US) +#define FST_LLT_VAL_TO_MS(v) (((u32) (v)) * LLT_UNIT_US / US_IN_MS) + +#define FST_MAX_LLT_MS FST_LLT_VAL_TO_MS(-1) +#define FST_MAX_PRIO_VALUE ((u8) -1) +#define FST_MAX_GROUP_ID_LEN IFNAMSIZ + +#define FST_DEFAULT_LLT_CFG_VALUE 50 + +struct hostapd_hw_modes; +struct ieee80211_mgmt; +struct fst_iface; +struct fst_group; +struct fst_session; +struct fst_get_peer_ctx; +struct fst_ctrl_handle; + +struct fst_wpa_obj { + void *ctx; + + /** + * get_bssid - Get BSSID of the interface + * @ctx: User context %ctx + * Returns: BSSID for success, %NULL for failure. + * + * NOTE: For AP it returns the own BSSID, while for STA - the BSSID of + * the associated AP. + */ + const u8 * (*get_bssid)(void *ctx); + + /** + * get_channel_info - Get current channel info + * @ctx: User context %ctx + * @hw_mode: OUT, current HW mode + * @channel: OUT, current channel + */ + void (*get_channel_info)(void *ctx, enum hostapd_hw_mode *hw_mode, + u8 *channel); + + /** + * get_hw_modes - Get hardware modes + * @ctx: User context %ctx + * @modes: OUT, pointer on array of hw modes + * + * Returns: Number of hw modes available. + */ + int (*get_hw_modes)(void *ctx, struct hostapd_hw_modes **modes); + + /** + * set_ies - Set interface's MB IE + * @ctx: User context %ctx + * @fst_ies: MB IE buffer (owned by FST module) + */ + void (*set_ies)(void *ctx, const struct wpabuf *fst_ies); + + /** + * send_action - Send FST Action frame via the interface + * @ctx: User context %ctx + * @addr: Address of the destination STA + * @data: Action frame buffer + * Returns: 0 for success, negative error code for failure. + */ + int (*send_action)(void *ctx, const u8 *addr, struct wpabuf *data); + + /** + * get_mb_ie - Get last MB IE received from STA + * @ctx: User context %ctx + * @addr: Address of the STA + * Returns: MB IE buffer, %NULL if no MB IE received from the STA + */ + const struct wpabuf * (*get_mb_ie)(void *ctx, const u8 *addr); + + /** + * update_mb_ie - Update last MB IE received from STA + * @ctx: User context %ctx + * @addr: Address of the STA + * @buf: Buffer that contains the MB IEs data + * @size: Size of data in %buf + */ + void (*update_mb_ie)(void *ctx, const u8 *addr, + const u8 *buf, size_t size); + + /** + * get_peer_first - Get MAC address of the 1st connected STA + * @ctx: User context %ctx + * @get_ctx: Context to be used for %get_peer_next call + * @mb_only: %TRUE if only multi-band capable peer should be reported + * Returns: Address of the 1st connected STA, %NULL if no STAs connected + */ + const u8 * (*get_peer_first)(void *ctx, + struct fst_get_peer_ctx **get_ctx, + Boolean mb_only); + /** + * get_peer_next - Get MAC address of the next connected STA + * @ctx: User context %ctx + * @get_ctx: Context received from %get_peer_first or previous + * %get_peer_next call + * @mb_only: %TRUE if only multi-band capable peer should be reported + * Returns: Address of the next connected STA, %NULL if no more STAs + * connected + */ + const u8 * (*get_peer_next)(void *ctx, + struct fst_get_peer_ctx **get_ctx, + Boolean mb_only); +}; + +/** + * fst_global_init - Global FST module initiator + * Returns: 0 for success, negative error code for failure. + * Note: The purpose of this function is to allocate and initiate global + * FST module data structures (linked lists, static data etc.) + * This function should be called prior to the 1st %fst_attach call. + */ +int fst_global_init(void); + +/** + * fst_global_deinit - Global FST module de-initiator + * Note: The purpose of this function is to deallocate and de-initiate global + * FST module data structures (linked lists, static data etc.) + */ +void fst_global_deinit(void); + +/** + * struct fst_ctrl - Notification interface for FST module + */ +struct fst_ctrl { + /** + * init - Initialize the notification interface + * Returns: 0 for success, negative error code for failure. + */ + int (*init)(void); + + /** + * deinit - Deinitialize the notification interface + */ + void (*deinit)(void); + + /** + * on_group_created - Notify about FST group creation + * Returns: 0 for success, negative error code for failure. + */ + int (*on_group_created)(struct fst_group *g); + + /** + * on_group_deleted - Notify about FST group deletion + */ + void (*on_group_deleted)(struct fst_group *g); + + /** + * on_iface_added - Notify about interface addition + * Returns: 0 for success, negative error code for failure. + */ + int (*on_iface_added)(struct fst_iface *i); + + /** + * on_iface_removed - Notify about interface removal + */ + void (*on_iface_removed)(struct fst_iface *i); + + /** + * on_session_added - Notify about FST session addition + * Returns: 0 for success, negative error code for failure. + */ + int (*on_session_added)(struct fst_session *s); + + /** + * on_session_removed - Notify about FST session removal + */ + void (*on_session_removed)(struct fst_session *s); + + /** + * on_event - Notify about FST event + * @event_type: Event type + * @i: Interface object that relates to the event or NULL + * @g: Group object that relates to the event or NULL + * @extra - Event specific data (see fst_ctrl_iface.h for more info) + */ + void (*on_event)(enum fst_event_type event_type, struct fst_iface *i, + struct fst_session *s, + const union fst_event_extra *extra); +}; + +struct fst_ctrl_handle * fst_global_add_ctrl(const struct fst_ctrl *ctrl); +void fst_global_del_ctrl(struct fst_ctrl_handle *h); + +/** + * NOTE: These values have to be read from configuration file + */ +struct fst_iface_cfg { + char group_id[FST_MAX_GROUP_ID_LEN + 1]; + u8 priority; + u32 llt; +}; + +/** + * fst_attach - Attach interface to an FST group according to configuration read + * @ifname: Interface name + * @own_addr: Own interface MAC address + * @iface_obj: Callbacks to be used by FST module to communicate with + * hostapd/wpa_supplicant + * @cfg: FST-related interface configuration read from the configuration file + * Returns: FST interface object for success, %NULL for failure. + */ +struct fst_iface * fst_attach(const char *ifname, + const u8 *own_addr, + const struct fst_wpa_obj *iface_obj, + const struct fst_iface_cfg *cfg); + +/** + * fst_detach - Detach an interface + * @iface: FST interface object + */ +void fst_detach(struct fst_iface *iface); + +/* FST module inputs */ +/** + * fst_rx_action - FST Action frames handler + * @iface: FST interface object + * @mgmt: Action frame arrived + * @len: Action frame length + */ +void fst_rx_action(struct fst_iface *iface, const struct ieee80211_mgmt *mgmt, + size_t len); + +/** + * fst_notify_peer_connected - FST STA connect handler + * @iface: FST interface object + * @addr: Address of the connected STA + */ +void fst_notify_peer_connected(struct fst_iface *iface, const u8 *addr); + +/** + * fst_notify_peer_disconnected - FST STA disconnect handler + * @iface: FST interface object + * @addr: Address of the disconnected STA + */ +void fst_notify_peer_disconnected(struct fst_iface *iface, const u8 *addr); + +/* FST module auxiliary routines */ + +/** + * fst_are_ifaces_aggregated - Determines whether 2 interfaces belong to the + * same FST group + * @iface1: 1st FST interface object + * @iface1: 2nd FST interface object + * + * Returns: %TRUE if the interfaces belong to the same FST group, + * %FALSE otherwise + */ +Boolean fst_are_ifaces_aggregated(struct fst_iface *iface1, + struct fst_iface *iface2); + +#else /* CONFIG_FST */ + +static inline int fst_global_init(void) +{ + return 0; +} + +static inline int fst_global_start(void) +{ + return 0; +} + +static inline void fst_global_stop(void) +{ +} + +static inline void fst_global_deinit(void) +{ +} + +#endif /* CONFIG_FST */ + +#endif /* FST_H */ diff --git a/contrib/wpa/src/fst/fst_ctrl_aux.c b/contrib/wpa/src/fst/fst_ctrl_aux.c new file mode 100644 index 0000000..dc7b2a7 --- /dev/null +++ b/contrib/wpa/src/fst/fst_ctrl_aux.c @@ -0,0 +1,69 @@ +/* + * FST module implementation + * Copyright (c) 2014, 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/defs.h" +#include "fst_ctrl_defs.h" +#include "fst_ctrl_aux.h" + + +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, +}; + +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, +}; + +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_CS_PVAL_STATE_TRANSITION_CONFIRMED, +}; + + +/* helpers */ +const char * fst_get_str_name(unsigned index, const char *names[], + size_t names_size) +{ + if (index >= names_size || !names[index]) + return FST_NAME_UNKNOWN; + return names[index]; +} + + +const char * fst_session_event_type_name(enum fst_event_type event) +{ + return fst_get_str_name(event, session_event_names, + ARRAY_SIZE(session_event_names)); +} + + +const char * fst_reason_name(enum fst_reason reason) +{ + return fst_get_str_name(reason, reason_names, ARRAY_SIZE(reason_names)); +} + + +const char * fst_session_state_name(enum fst_session_state state) +{ + return fst_get_str_name(state, session_state_names, + ARRAY_SIZE(session_state_names)); +} diff --git a/contrib/wpa/src/fst/fst_ctrl_aux.h b/contrib/wpa/src/fst/fst_ctrl_aux.h new file mode 100644 index 0000000..e2133f5 --- /dev/null +++ b/contrib/wpa/src/fst/fst_ctrl_aux.h @@ -0,0 +1,91 @@ +/* + * FST module - miscellaneous definitions + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef FST_CTRL_AUX_H +#define FST_CTRL_AUX_H + +#include "common/defs.h" + +/* FST module control interface API */ +#define FST_INVALID_SESSION_ID ((u32) -1) +#define FST_MAX_GROUP_ID_SIZE 32 +#define FST_MAX_INTERFACE_SIZE 32 + +enum fst_session_state { + FST_SESSION_STATE_INITIAL, + FST_SESSION_STATE_SETUP_COMPLETION, + FST_SESSION_STATE_TRANSITION_DONE, + FST_SESSION_STATE_TRANSITION_CONFIRMED, + FST_SESSION_STATE_LAST +}; + +enum fst_event_type { + EVENT_FST_IFACE_STATE_CHANGED, /* An interface has been either attached + * to or detached from an FST group */ + EVENT_FST_ESTABLISHED, /* FST Session has been established */ + EVENT_FST_SETUP, /* FST Session request received */ + EVENT_FST_SESSION_STATE_CHANGED,/* FST Session state has been changed */ + EVENT_PEER_STATE_CHANGED /* FST related generic event occurred, + * see struct fst_hostap_event_data for + * more info */ +}; + +enum fst_initiator { + FST_INITIATOR_UNDEFINED, + FST_INITIATOR_LOCAL, + FST_INITIATOR_REMOTE, +}; + +union fst_event_extra { + struct fst_event_extra_iface_state { + Boolean attached; + char ifname[FST_MAX_INTERFACE_SIZE]; + char group_id[FST_MAX_GROUP_ID_SIZE]; + } iface_state; /* for EVENT_FST_IFACE_STATE_CHANGED */ + struct fst_event_extra_peer_state { + Boolean connected; + char ifname[FST_MAX_INTERFACE_SIZE]; + u8 addr[ETH_ALEN]; + } peer_state; /* for EVENT_PEER_STATE_CHANGED */ + struct fst_event_extra_session_state { + enum fst_session_state old_state; + enum fst_session_state new_state; + union fst_session_state_switch_extra { + struct { + enum fst_reason { + REASON_TEARDOWN, + REASON_SETUP, + REASON_SWITCH, + REASON_STT, + REASON_REJECT, + REASON_ERROR_PARAMS, + REASON_RESET, + REASON_DETACH_IFACE, + } reason; + u8 reject_code; /* REASON_REJECT */ + /* REASON_SWITCH, + * REASON_TEARDOWN, + * REASON_REJECT + */ + enum fst_initiator initiator; + } to_initial; + } extra; + } session_state; /* for EVENT_FST_SESSION_STATE_CHANGED */ +}; + +/* helpers - prints enum in string form */ +#define FST_NAME_UNKNOWN "UNKNOWN" + +const char * fst_get_str_name(unsigned index, const char *names[], + size_t names_size); + +const char * fst_session_event_type_name(enum fst_event_type); +const char * fst_reason_name(enum fst_reason reason); +const char * fst_session_state_name(enum fst_session_state state); + +#endif /* FST_CTRL_AUX_H */ diff --git a/contrib/wpa/src/fst/fst_ctrl_defs.h b/contrib/wpa/src/fst/fst_ctrl_defs.h new file mode 100644 index 0000000..6735389 --- /dev/null +++ b/contrib/wpa/src/fst/fst_ctrl_defs.h @@ -0,0 +1,109 @@ +/* + * FST module - shared Control interface definitions + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef FST_CTRL_DEFS_H +#define FST_CTRL_DEFS_H + +/* Undefined value */ +#define FST_CTRL_PVAL_NONE "NONE" + +/* FST-ATTACH parameters */ +#define FST_ATTACH_CMD_PNAME_LLT "llt" /* pval = desired LLT */ +#define FST_ATTACH_CMD_PNAME_PRIORITY "priority" /* pval = desired priority */ + +/* FST-MANAGER parameters */ +/* FST Session states */ +#define FST_CS_PVAL_STATE_INITIAL "INITIAL" +#define FST_CS_PVAL_STATE_SETUP_COMPLETION "SETUP_COMPLETION" +#define FST_CS_PVAL_STATE_TRANSITION_DONE "TRANSITION_DONE" +#define FST_CS_PVAL_STATE_TRANSITION_CONFIRMED "TRANSITION_CONFIRMED" + +/* FST Session reset reasons */ +#define FST_CS_PVAL_REASON_TEARDOWN "REASON_TEARDOWN" +#define FST_CS_PVAL_REASON_SETUP "REASON_SETUP" +#define FST_CS_PVAL_REASON_SWITCH "REASON_SWITCH" +#define FST_CS_PVAL_REASON_STT "REASON_STT" +#define FST_CS_PVAL_REASON_REJECT "REASON_REJECT" +#define FST_CS_PVAL_REASON_ERROR_PARAMS "REASON_ERROR_PARAMS" +#define FST_CS_PVAL_REASON_RESET "REASON_RESET" +#define FST_CS_PVAL_REASON_DETACH_IFACE "REASON_DETACH_IFACE" + +/* FST Session responses */ +#define FST_CS_PVAL_RESPONSE_ACCEPT "ACCEPT" +#define FST_CS_PVAL_RESPONSE_REJECT "REJECT" + +/* FST Session action initiator */ +#define FST_CS_PVAL_INITIATOR_LOCAL "LOCAL" +#define FST_CS_PVAL_INITIATOR_REMOTE "REMOTE" + +/* FST-CLI subcommands and parameter names */ +#define FST_CMD_LIST_GROUPS "list_groups" +#define FST_CMD_LIST_IFACES "list_ifaces" +#define FST_CMD_IFACE_PEERS "iface_peers" +#define FST_CMD_GET_PEER_MBIES "get_peer_mbies" +#define FST_CMD_LIST_SESSIONS "list_sessions" +#define FST_CMD_SESSION_ADD "session_add" +#define FST_CMD_SESSION_REMOVE "session_remove" +#define FST_CMD_SESSION_GET "session_get" +#define FST_CSG_PNAME_OLD_PEER_ADDR "old_peer_addr" /* pval = address string */ +#define FST_CSG_PNAME_NEW_PEER_ADDR "new_peer_addr" /* pval = address string */ +#define FST_CSG_PNAME_OLD_IFNAME "old_ifname" /* pval = ifname */ +#define FST_CSG_PNAME_NEW_IFNAME "new_ifname" /* pval = ifname */ +#define FST_CSG_PNAME_LLT "llt" /* pval = numeric llt value */ +#define FST_CSG_PNAME_STATE "state" /* pval = FST_CS_PVAL_STATE_... */ +#define FST_CMD_SESSION_SET "session_set" +#define FST_CSS_PNAME_OLD_PEER_ADDR FST_CSG_PNAME_OLD_PEER_ADDR +#define FST_CSS_PNAME_NEW_PEER_ADDR FST_CSG_PNAME_NEW_PEER_ADDR +#define FST_CSS_PNAME_OLD_IFNAME FST_CSG_PNAME_OLD_IFNAME +#define FST_CSS_PNAME_NEW_IFNAME FST_CSG_PNAME_NEW_IFNAME +#define FST_CSS_PNAME_LLT FST_CSG_PNAME_LLT +#define FST_CMD_SESSION_INITIATE "session_initiate" +#define FST_CMD_SESSION_RESPOND "session_respond" +#define FST_CMD_SESSION_TRANSFER "session_transfer" +#define FST_CMD_SESSION_TEARDOWN "session_teardown" + +#ifdef CONFIG_FST_TEST +#define FST_CTR_PVAL_BAD_NEW_BAND "bad_new_band" + +#define FST_CMD_TEST_REQUEST "test_request" +#define FST_CTR_IS_SUPPORTED "is_supported" +#define FST_CTR_SEND_SETUP_REQUEST "send_setup_request" +#define FST_CTR_SEND_SETUP_RESPONSE "send_setup_response" +#define FST_CTR_SEND_ACK_REQUEST "send_ack_request" +#define FST_CTR_SEND_ACK_RESPONSE "send_ack_response" +#define FST_CTR_SEND_TEAR_DOWN "send_tear_down" +#define FST_CTR_GET_FSTS_ID "get_fsts_id" +#define FST_CTR_GET_LOCAL_MBIES "get_local_mbies" +#endif /* CONFIG_FST_TEST */ + +/* Events */ +#define FST_CTRL_EVENT_IFACE "FST-EVENT-IFACE" +#define FST_CEI_PNAME_IFNAME "ifname" +#define FST_CEI_PNAME_GROUP "group" +#define FST_CEI_PNAME_ATTACHED "attached" +#define FST_CEI_PNAME_DETACHED "detached" +#define FST_CTRL_EVENT_PEER "FST-EVENT-PEER" +#define FST_CEP_PNAME_IFNAME "ifname" +#define FST_CEP_PNAME_ADDR "peer_addr" +#define FST_CEP_PNAME_CONNECTED "connected" +#define FST_CEP_PNAME_DISCONNECTED "disconnected" +#define FST_CTRL_EVENT_SESSION "FST-EVENT-SESSION" +#define FST_CES_PNAME_SESSION_ID "session_id" +#define FST_CES_PNAME_EVT_TYPE "event_type" +#define FST_PVAL_EVT_TYPE_SESSION_STATE "EVENT_FST_SESSION_STATE" +/* old_state/new_state: pval = FST_CS_PVAL_STATE_... */ +#define FST_CES_PNAME_OLD_STATE "old_state" +#define FST_CES_PNAME_NEW_STATE "new_state" +#define FST_CES_PNAME_REASON "reason" /* pval = FST_CS_PVAL_REASON_... */ +#define FST_CES_PNAME_REJECT_CODE "reject_code" /* pval = u8 code */ +/* pval = FST_CS_PVAL_INITIATOR_... */ +#define FST_CES_PNAME_INITIATOR "initiator" +#define FST_PVAL_EVT_TYPE_ESTABLISHED "EVENT_FST_ESTABLISHED" +#define FST_PVAL_EVT_TYPE_SETUP "EVENT_FST_SETUP" + +#endif /* FST_CTRL_DEFS_H */ diff --git a/contrib/wpa/src/fst/fst_ctrl_iface.c b/contrib/wpa/src/fst/fst_ctrl_iface.c new file mode 100644 index 0000000..d090718 --- /dev/null +++ b/contrib/wpa/src/fst/fst_ctrl_iface.c @@ -0,0 +1,948 @@ +/* + * FST module - Control Interface implementation + * Copyright (c) 2014, 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/defs.h" +#include "list.h" +#include "fst/fst.h" +#include "fst/fst_internal.h" +#include "fst_ctrl_defs.h" +#include "fst_ctrl_iface.h" + + +static struct fst_group * get_fst_group_by_id(const char *id) +{ + struct fst_group *g; + + foreach_fst_group(g) { + const char *group_id = fst_group_get_id(g); + + if (os_strncmp(group_id, id, os_strlen(group_id)) == 0) + return g; + } + + return NULL; +} + + +/* notifications */ +static Boolean format_session_state_extra(const union fst_event_extra *extra, + char *buffer, size_t size) +{ + int len; + char reject_str[32] = FST_CTRL_PVAL_NONE; + const char *initiator = FST_CTRL_PVAL_NONE; + const struct fst_event_extra_session_state *ss; + + ss = &extra->session_state; + if (ss->new_state != FST_SESSION_STATE_INITIAL) + return TRUE; + + switch (ss->extra.to_initial.reason) { + case REASON_REJECT: + if (ss->extra.to_initial.reject_code != WLAN_STATUS_SUCCESS) + os_snprintf(reject_str, sizeof(reject_str), "%u", + ss->extra.to_initial.reject_code); + /* no break */ + case REASON_TEARDOWN: + case REASON_SWITCH: + switch (ss->extra.to_initial.initiator) { + case FST_INITIATOR_LOCAL: + initiator = FST_CS_PVAL_INITIATOR_LOCAL; + break; + case FST_INITIATOR_REMOTE: + initiator = FST_CS_PVAL_INITIATOR_REMOTE; + break; + default: + break; + } + break; + default: + break; + } + + len = os_snprintf(buffer, size, + FST_CES_PNAME_REASON "=%s " + FST_CES_PNAME_REJECT_CODE "=%s " + FST_CES_PNAME_INITIATOR "=%s", + fst_reason_name(ss->extra.to_initial.reason), + reject_str, initiator); + + return !os_snprintf_error(size, len); +} + + +static void fst_ctrl_iface_notify(struct fst_iface *f, u32 session_id, + enum fst_event_type event_type, + const union fst_event_extra *extra) +{ + struct fst_group *g; + char extra_str[128] = ""; + const struct fst_event_extra_session_state *ss; + const struct fst_event_extra_iface_state *is; + const struct fst_event_extra_peer_state *ps; + + /* + * FST can use any of interface objects as it only sends messages + * on global Control Interface, so we just pick the 1st one. + */ + + if (!f) { + foreach_fst_group(g) { + f = fst_group_first_iface(g); + if (f) + break; + } + if (!f) + return; + } + + WPA_ASSERT(f->iface_obj.ctx); + + switch (event_type) { + case EVENT_FST_IFACE_STATE_CHANGED: + if (!extra) + return; + is = &extra->iface_state; + wpa_msg_global_only(f->iface_obj.ctx, MSG_INFO, + FST_CTRL_EVENT_IFACE " %s " + FST_CEI_PNAME_IFNAME "=%s " + FST_CEI_PNAME_GROUP "=%s", + is->attached ? FST_CEI_PNAME_ATTACHED : + FST_CEI_PNAME_DETACHED, + is->ifname, is->group_id); + break; + case EVENT_PEER_STATE_CHANGED: + if (!extra) + return; + ps = &extra->peer_state; + wpa_msg_global_only(fst_iface_get_wpa_obj_ctx(f), MSG_INFO, + FST_CTRL_EVENT_PEER " %s " + FST_CEP_PNAME_IFNAME "=%s " + FST_CEP_PNAME_ADDR "=" MACSTR, + ps->connected ? FST_CEP_PNAME_CONNECTED : + FST_CEP_PNAME_DISCONNECTED, + ps->ifname, MAC2STR(ps->addr)); + break; + case EVENT_FST_SESSION_STATE_CHANGED: + if (!extra) + return; + if (!format_session_state_extra(extra, extra_str, + sizeof(extra_str))) { + fst_printf(MSG_ERROR, + "CTRL: Cannot format STATE_CHANGE extra"); + extra_str[0] = 0; + } + ss = &extra->session_state; + wpa_msg_global_only(fst_iface_get_wpa_obj_ctx(f), MSG_INFO, + FST_CTRL_EVENT_SESSION " " + FST_CES_PNAME_SESSION_ID "=%u " + FST_CES_PNAME_EVT_TYPE "=%s " + FST_CES_PNAME_OLD_STATE "=%s " + FST_CES_PNAME_NEW_STATE "=%s %s", + session_id, + fst_session_event_type_name(event_type), + fst_session_state_name(ss->old_state), + fst_session_state_name(ss->new_state), + extra_str); + break; + case EVENT_FST_ESTABLISHED: + case EVENT_FST_SETUP: + wpa_msg_global_only(fst_iface_get_wpa_obj_ctx(f), MSG_INFO, + FST_CTRL_EVENT_SESSION " " + FST_CES_PNAME_SESSION_ID "=%u " + FST_CES_PNAME_EVT_TYPE "=%s", + session_id, + fst_session_event_type_name(event_type)); + break; + } +} + + +/* command processors */ + +/* fst session_get */ +static int session_get(const char *session_id, char *buf, size_t buflen) +{ + struct fst_session *s; + struct fst_iface *new_iface, *old_iface; + const u8 *old_peer_addr, *new_peer_addr; + u32 id; + + id = strtoul(session_id, NULL, 0); + + s = fst_session_get_by_id(id); + if (!s) { + fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + old_peer_addr = fst_session_get_peer_addr(s, TRUE); + new_peer_addr = fst_session_get_peer_addr(s, FALSE); + new_iface = fst_session_get_iface(s, FALSE); + old_iface = fst_session_get_iface(s, TRUE); + + return os_snprintf(buf, buflen, + FST_CSG_PNAME_OLD_PEER_ADDR "=" MACSTR "\n" + FST_CSG_PNAME_NEW_PEER_ADDR "=" MACSTR "\n" + FST_CSG_PNAME_NEW_IFNAME "=%s\n" + FST_CSG_PNAME_OLD_IFNAME "=%s\n" + FST_CSG_PNAME_LLT "=%u\n" + FST_CSG_PNAME_STATE "=%s\n", + MAC2STR(old_peer_addr), + MAC2STR(new_peer_addr), + new_iface ? fst_iface_get_name(new_iface) : + FST_CTRL_PVAL_NONE, + old_iface ? fst_iface_get_name(old_iface) : + FST_CTRL_PVAL_NONE, + fst_session_get_llt(s), + fst_session_state_name(fst_session_get_state(s))); +} + + +/* fst session_set */ +static int session_set(const char *session_id, char *buf, size_t buflen) +{ + struct fst_session *s; + char *p, *q; + u32 id; + int ret; + + id = strtoul(session_id, &p, 0); + + s = fst_session_get_by_id(id); + if (!s) { + fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + if (*p != ' ' || !(q = os_strchr(p + 1, '='))) + return os_snprintf(buf, buflen, "FAIL\n"); + p++; + + if (os_strncasecmp(p, FST_CSS_PNAME_OLD_IFNAME, q - p) == 0) { + ret = fst_session_set_str_ifname(s, q + 1, TRUE); + } else if (os_strncasecmp(p, FST_CSS_PNAME_NEW_IFNAME, q - p) == 0) { + ret = fst_session_set_str_ifname(s, q + 1, FALSE); + } else if (os_strncasecmp(p, FST_CSS_PNAME_OLD_PEER_ADDR, q - p) == 0) { + ret = fst_session_set_str_peer_addr(s, q + 1, TRUE); + } else if (os_strncasecmp(p, FST_CSS_PNAME_NEW_PEER_ADDR, q - p) == 0) { + ret = fst_session_set_str_peer_addr(s, q + 1, FALSE); + } else if (os_strncasecmp(p, FST_CSS_PNAME_LLT, q - p) == 0) { + ret = fst_session_set_str_llt(s, q + 1); + } else { + fst_printf(MSG_ERROR, "CTRL: Unknown parameter: %s", p); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + return os_snprintf(buf, buflen, "%s\n", ret ? "FAIL" : "OK"); +} + + +/* fst session_add/remove */ +static int session_add(const char *group_id, char *buf, size_t buflen) +{ + struct fst_group *g; + struct fst_session *s; + + g = get_fst_group_by_id(group_id); + if (!g) { + fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'", + group_id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + s = fst_session_create(g); + if (!s) { + fst_printf(MSG_ERROR, + "CTRL: Cannot create session for group '%s'", + group_id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + return os_snprintf(buf, buflen, "%u\n", fst_session_get_id(s)); +} + + +static int session_remove(const char *session_id, char *buf, size_t buflen) +{ + struct fst_session *s; + struct fst_group *g; + u32 id; + + id = strtoul(session_id, NULL, 0); + + s = fst_session_get_by_id(id); + if (!s) { + fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + g = fst_session_get_group(s); + fst_session_reset(s); + fst_session_delete(s); + fst_group_delete_if_empty(g); + + return os_snprintf(buf, buflen, "OK\n"); +} + + +/* fst session_initiate */ +static int session_initiate(const char *session_id, char *buf, size_t buflen) +{ + struct fst_session *s; + u32 id; + + id = strtoul(session_id, NULL, 0); + + s = fst_session_get_by_id(id); + if (!s) { + fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + if (fst_session_initiate_setup(s)) { + fst_printf(MSG_WARNING, "CTRL: Cannot initiate session %u", id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + return os_snprintf(buf, buflen, "OK\n"); +} + + +/* fst session_respond */ +static int session_respond(const char *session_id, char *buf, size_t buflen) +{ + struct fst_session *s; + char *p; + u32 id; + u8 status_code; + + id = strtoul(session_id, &p, 0); + + s = fst_session_get_by_id(id); + if (!s) { + fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + if (*p != ' ') + return os_snprintf(buf, buflen, "FAIL\n"); + p++; + + if (!os_strcasecmp(p, FST_CS_PVAL_RESPONSE_ACCEPT)) { + status_code = WLAN_STATUS_SUCCESS; + } else if (!os_strcasecmp(p, FST_CS_PVAL_RESPONSE_REJECT)) { + status_code = WLAN_STATUS_PENDING_ADMITTING_FST_SESSION; + } else { + fst_printf(MSG_WARNING, + "CTRL: session %u: unknown response status: %s", + id, p); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + if (fst_session_respond(s, status_code)) { + fst_printf(MSG_WARNING, "CTRL: Cannot respond to session %u", + id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + fst_printf(MSG_INFO, "CTRL: session %u responded", id); + + return os_snprintf(buf, buflen, "OK\n"); +} + + +/* fst session_transfer */ +static int session_transfer(const char *session_id, char *buf, size_t buflen) +{ + struct fst_session *s; + u32 id; + + id = strtoul(session_id, NULL, 0); + + s = fst_session_get_by_id(id); + if (!s) { + fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + if (fst_session_initiate_switch(s)) { + fst_printf(MSG_WARNING, + "CTRL: Cannot initiate ST for session %u", id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + return os_snprintf(buf, buflen, "OK\n"); +} + + +/* fst session_teardown */ +static int session_teardown(const char *session_id, char *buf, size_t buflen) +{ + struct fst_session *s; + u32 id; + + id = strtoul(session_id, NULL, 0); + + s = fst_session_get_by_id(id); + if (!s) { + fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + if (fst_session_tear_down_setup(s)) { + fst_printf(MSG_WARNING, "CTRL: Cannot tear down session %u", + id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + return os_snprintf(buf, buflen, "OK\n"); +} + + +#ifdef CONFIG_FST_TEST +/* fst test_request */ +static int test_request(const char *request, char *buf, size_t buflen) +{ + const char *p = request; + int ret; + + if (!os_strncasecmp(p, FST_CTR_SEND_SETUP_REQUEST, + os_strlen(FST_CTR_SEND_SETUP_REQUEST))) { + ret = fst_test_req_send_fst_request( + p + os_strlen(FST_CTR_SEND_SETUP_REQUEST)); + } else if (!os_strncasecmp(p, FST_CTR_SEND_SETUP_RESPONSE, + os_strlen(FST_CTR_SEND_SETUP_RESPONSE))) { + ret = fst_test_req_send_fst_response( + p + os_strlen(FST_CTR_SEND_SETUP_RESPONSE)); + } else if (!os_strncasecmp(p, FST_CTR_SEND_ACK_REQUEST, + os_strlen(FST_CTR_SEND_ACK_REQUEST))) { + ret = fst_test_req_send_ack_request( + p + os_strlen(FST_CTR_SEND_ACK_REQUEST)); + } else if (!os_strncasecmp(p, FST_CTR_SEND_ACK_RESPONSE, + os_strlen(FST_CTR_SEND_ACK_RESPONSE))) { + ret = fst_test_req_send_ack_response( + p + os_strlen(FST_CTR_SEND_ACK_RESPONSE)); + } else if (!os_strncasecmp(p, FST_CTR_SEND_TEAR_DOWN, + os_strlen(FST_CTR_SEND_TEAR_DOWN))) { + ret = fst_test_req_send_tear_down( + p + os_strlen(FST_CTR_SEND_TEAR_DOWN)); + } else if (!os_strncasecmp(p, FST_CTR_GET_FSTS_ID, + os_strlen(FST_CTR_GET_FSTS_ID))) { + u32 fsts_id = fst_test_req_get_fsts_id( + p + os_strlen(FST_CTR_GET_FSTS_ID)); + if (fsts_id != FST_FSTS_ID_NOT_FOUND) + return os_snprintf(buf, buflen, "%u\n", fsts_id); + return os_snprintf(buf, buflen, "FAIL\n"); + } else if (!os_strncasecmp(p, FST_CTR_GET_LOCAL_MBIES, + os_strlen(FST_CTR_GET_LOCAL_MBIES))) { + return fst_test_req_get_local_mbies( + p + os_strlen(FST_CTR_GET_LOCAL_MBIES), buf, buflen); + } else if (!os_strncasecmp(p, FST_CTR_IS_SUPPORTED, + os_strlen(FST_CTR_IS_SUPPORTED))) { + ret = 0; + } else { + fst_printf(MSG_ERROR, "CTRL: Unknown parameter: %s", p); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + return os_snprintf(buf, buflen, "%s\n", ret ? "FAIL" : "OK"); +} +#endif /* CONFIG_FST_TEST */ + + +/* fst list_sessions */ +struct list_sessions_cb_ctx { + char *buf; + size_t buflen; + size_t reply_len; +}; + + +static void list_session_enum_cb(struct fst_group *g, struct fst_session *s, + void *ctx) +{ + struct list_sessions_cb_ctx *c = ctx; + int ret; + + ret = os_snprintf(c->buf, c->buflen, " %u", fst_session_get_id(s)); + + c->buf += ret; + c->buflen -= ret; + c->reply_len += ret; +} + + +static int list_sessions(const char *group_id, char *buf, size_t buflen) +{ + struct list_sessions_cb_ctx ctx; + struct fst_group *g; + + g = get_fst_group_by_id(group_id); + if (!g) { + fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'", + group_id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + ctx.buf = buf; + ctx.buflen = buflen; + ctx.reply_len = 0; + + fst_session_enum(g, list_session_enum_cb, &ctx); + + ctx.reply_len += os_snprintf(buf + ctx.reply_len, ctx.buflen, "\n"); + + return ctx.reply_len; +} + + +/* fst iface_peers */ +static int iface_peers(const char *group_id, char *buf, size_t buflen) +{ + const char *ifname; + struct fst_group *g; + struct fst_iface *f; + struct fst_get_peer_ctx *ctx; + const u8 *addr; + unsigned found = 0; + int ret = 0; + + g = get_fst_group_by_id(group_id); + if (!g) { + fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'", + group_id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + ifname = os_strchr(group_id, ' '); + if (!ifname) + return os_snprintf(buf, buflen, "FAIL\n"); + ifname++; + + foreach_fst_group_iface(g, f) { + const char *in = fst_iface_get_name(f); + + if (os_strncmp(ifname, in, os_strlen(in)) == 0) { + found = 1; + break; + } + } + + if (!found) + return os_snprintf(buf, buflen, "FAIL\n"); + + addr = fst_iface_get_peer_first(f, &ctx, FALSE); + for (; addr != NULL; addr = fst_iface_get_peer_next(f, &ctx, FALSE)) { + int res; + + res = os_snprintf(buf + ret, buflen - ret, MACSTR "\n", + MAC2STR(addr)); + if (os_snprintf_error(buflen - ret, res)) + break; + ret += res; + } + + return ret; +} + + +static int get_peer_mbies(const char *params, char *buf, size_t buflen) +{ + char *endp; + char ifname[FST_MAX_INTERFACE_SIZE]; + u8 peer_addr[ETH_ALEN]; + struct fst_group *g; + struct fst_iface *iface = NULL; + const struct wpabuf *mbies; + + if (fst_read_next_text_param(params, ifname, sizeof(ifname), &endp) || + !*ifname) + goto problem; + + while (isspace(*endp)) + endp++; + if (fst_read_peer_addr(endp, peer_addr)) + goto problem; + + foreach_fst_group(g) { + iface = fst_group_get_iface_by_name(g, ifname); + if (iface) + break; + } + if (!iface) + goto problem; + + mbies = fst_iface_get_peer_mb_ie(iface, peer_addr); + if (!mbies) + goto problem; + + return wpa_snprintf_hex(buf, buflen, wpabuf_head(mbies), + wpabuf_len(mbies)); + +problem: + return os_snprintf(buf, buflen, "FAIL\n"); +} + + +/* fst list_ifaces */ +static int list_ifaces(const char *group_id, char *buf, size_t buflen) +{ + struct fst_group *g; + struct fst_iface *f; + int ret = 0; + + g = get_fst_group_by_id(group_id); + if (!g) { + fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'", + group_id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + foreach_fst_group_iface(g, f) { + int res; + const u8 *iface_addr = fst_iface_get_addr(f); + + res = os_snprintf(buf + ret, buflen - ret, + "%s|" MACSTR "|%u|%u\n", + fst_iface_get_name(f), + MAC2STR(iface_addr), + fst_iface_get_priority(f), + fst_iface_get_llt(f)); + if (os_snprintf_error(buflen - ret, res)) + break; + ret += res; + } + + return ret; +} + + +/* fst list_groups */ +static int list_groups(const char *cmd, char *buf, size_t buflen) +{ + struct fst_group *g; + int ret = 0; + + foreach_fst_group(g) { + int res; + + res = os_snprintf(buf + ret, buflen - ret, "%s\n", + fst_group_get_id(g)); + if (os_snprintf_error(buflen - ret, res)) + break; + ret += res; + } + + return ret; +} + + +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", + }; + + return fst_get_str_name(band, band_names, ARRAY_SIZE(band_names)); +} + + +static int print_band(unsigned num, struct fst_iface *iface, const u8 *addr, + char *buf, size_t buflen) +{ + const struct wpabuf *wpabuf; + enum hostapd_hw_mode hw_mode; + u8 channel; + int ret = 0; + + fst_iface_get_channel_info(iface, &hw_mode, &channel); + + ret += os_snprintf(buf + ret, buflen - ret, "band%u_frequency=%s\n", + num, band_freq(fst_hw_mode_to_band(hw_mode))); + ret += os_snprintf(buf + ret, buflen - ret, "band%u_iface=%s\n", + num, fst_iface_get_name(iface)); + wpabuf = fst_iface_get_peer_mb_ie(iface, addr); + if (wpabuf) { + ret += os_snprintf(buf + ret, buflen - ret, "band%u_mb_ies=", + num); + ret += wpa_snprintf_hex(buf + ret, buflen - ret, + wpabuf_head(wpabuf), + wpabuf_len(wpabuf)); + ret += os_snprintf(buf + ret, buflen - ret, "\n"); + } + ret += os_snprintf(buf + ret, buflen - ret, "band%u_fst_group_id=%s\n", + num, fst_iface_get_group_id(iface)); + ret += os_snprintf(buf + ret, buflen - ret, "band%u_fst_priority=%u\n", + num, fst_iface_get_priority(iface)); + ret += os_snprintf(buf + ret, buflen - ret, "band%u_fst_llt=%u\n", + num, fst_iface_get_llt(iface)); + + return ret; +} + + +static void fst_ctrl_iface_on_iface_state_changed(struct fst_iface *i, + Boolean attached) +{ + union fst_event_extra extra; + + os_memset(&extra, 0, sizeof(extra)); + extra.iface_state.attached = attached; + os_strlcpy(extra.iface_state.ifname, fst_iface_get_name(i), + sizeof(extra.iface_state.ifname)); + os_strlcpy(extra.iface_state.group_id, fst_iface_get_group_id(i), + sizeof(extra.iface_state.group_id)); + + fst_ctrl_iface_notify(i, FST_INVALID_SESSION_ID, + EVENT_FST_IFACE_STATE_CHANGED, &extra); +} + + +static int fst_ctrl_iface_on_iface_added(struct fst_iface *i) +{ + fst_ctrl_iface_on_iface_state_changed(i, TRUE); + return 0; +} + + +static void fst_ctrl_iface_on_iface_removed(struct fst_iface *i) +{ + fst_ctrl_iface_on_iface_state_changed(i, FALSE); +} + + +static void fst_ctrl_iface_on_event(enum fst_event_type event_type, + struct fst_iface *i, struct fst_session *s, + const union fst_event_extra *extra) +{ + u32 session_id = s ? fst_session_get_id(s) : FST_INVALID_SESSION_ID; + + fst_ctrl_iface_notify(i, session_id, event_type, extra); +} + + +static const struct fst_ctrl ctrl_cli = { + .on_iface_added = fst_ctrl_iface_on_iface_added, + .on_iface_removed = fst_ctrl_iface_on_iface_removed, + .on_event = fst_ctrl_iface_on_event, +}; + +const struct fst_ctrl *fst_ctrl_cli = &ctrl_cli; + + +int fst_ctrl_iface_mb_info(const u8 *addr, char *buf, size_t buflen) +{ + struct fst_group *g; + struct fst_iface *f; + unsigned num = 0; + int ret = 0; + + foreach_fst_group(g) { + foreach_fst_group_iface(g, f) { + if (fst_iface_is_connected(f, addr)) { + ret += print_band(num++, f, addr, + buf + ret, buflen - ret); + } + } + } + + return ret; +} + + +/* fst ctrl processor */ +int fst_ctrl_iface_receive(const char *cmd, char *reply, size_t reply_size) +{ + static const struct fst_command { + const char *name; + unsigned has_param; + int (*process)(const char *group_id, char *buf, size_t buflen); + } commands[] = { + { FST_CMD_LIST_GROUPS, 0, list_groups}, + { FST_CMD_LIST_IFACES, 1, list_ifaces}, + { FST_CMD_IFACE_PEERS, 1, iface_peers}, + { FST_CMD_GET_PEER_MBIES, 1, get_peer_mbies}, + { FST_CMD_LIST_SESSIONS, 1, list_sessions}, + { FST_CMD_SESSION_ADD, 1, session_add}, + { FST_CMD_SESSION_REMOVE, 1, session_remove}, + { FST_CMD_SESSION_GET, 1, session_get}, + { FST_CMD_SESSION_SET, 1, session_set}, + { FST_CMD_SESSION_INITIATE, 1, session_initiate}, + { FST_CMD_SESSION_RESPOND, 1, session_respond}, + { FST_CMD_SESSION_TRANSFER, 1, session_transfer}, + { FST_CMD_SESSION_TEARDOWN, 1, session_teardown}, +#ifdef CONFIG_FST_TEST + { FST_CMD_TEST_REQUEST, 1, test_request }, +#endif /* CONFIG_FST_TEST */ + { NULL, 0, NULL } + }; + const struct fst_command *c; + const char *p; + const char *temp; + Boolean non_spaces_found; + + for (c = commands; c->name; c++) { + if (os_strncasecmp(cmd, c->name, os_strlen(c->name)) != 0) + continue; + p = cmd + os_strlen(c->name); + if (c->has_param) { + if (!isspace(p[0])) + return os_snprintf(reply, reply_size, "FAIL\n"); + p++; + temp = p; + non_spaces_found = FALSE; + while (*temp) { + if (!isspace(*temp)) { + non_spaces_found = TRUE; + break; + } + temp++; + } + if (!non_spaces_found) + return os_snprintf(reply, reply_size, "FAIL\n"); + } + return c->process(p, reply, reply_size); + } + + return os_snprintf(reply, reply_size, "UNKNOWN FST COMMAND\n"); +} + + +int fst_read_next_int_param(const char *params, Boolean *valid, char **endp) +{ + int ret = -1; + const char *curp; + + *valid = FALSE; + *endp = (char *) params; + curp = params; + if (*curp) { + ret = (int) strtol(curp, endp, 0); + if (!**endp || isspace(**endp)) + *valid = TRUE; + } + + return ret; +} + + +int fst_read_next_text_param(const char *params, char *buf, size_t buflen, + char **endp) +{ + size_t max_chars_to_copy; + char *cur_dest; + + *endp = (char *) params; + while (isspace(**endp)) + (*endp)++; + if (!**endp || buflen <= 1) + return -EINVAL; + + max_chars_to_copy = buflen - 1; + /* We need 1 byte for the terminating zero */ + cur_dest = buf; + while (**endp && !isspace(**endp) && max_chars_to_copy > 0) { + *cur_dest = **endp; + (*endp)++; + cur_dest++; + max_chars_to_copy--; + } + *cur_dest = 0; + + return 0; +} + + +int fst_read_peer_addr(const char *mac, u8 *peer_addr) +{ + if (hwaddr_aton(mac, peer_addr)) { + fst_printf(MSG_WARNING, "Bad peer_mac %s: invalid addr string", + mac); + return -1; + } + + if (is_zero_ether_addr(peer_addr) || + is_multicast_ether_addr(peer_addr)) { + fst_printf(MSG_WARNING, "Bad peer_mac %s: not a unicast addr", + mac); + return -1; + } + + return 0; +} + + +int fst_parse_attach_command(const char *cmd, char *ifname, size_t ifname_size, + struct fst_iface_cfg *cfg) +{ + char *pos; + char *endp; + Boolean is_valid; + int val; + + if (fst_read_next_text_param(cmd, ifname, ifname_size, &endp) || + fst_read_next_text_param(endp, cfg->group_id, sizeof(cfg->group_id), + &endp)) + return -EINVAL; + + cfg->llt = FST_DEFAULT_LLT_CFG_VALUE; + cfg->priority = 0; + pos = os_strstr(endp, FST_ATTACH_CMD_PNAME_LLT); + if (pos) { + pos += os_strlen(FST_ATTACH_CMD_PNAME_LLT); + if (*pos == '=') { + val = fst_read_next_int_param(pos + 1, &is_valid, + &endp); + if (is_valid) + cfg->llt = val; + } + } + pos = os_strstr(endp, FST_ATTACH_CMD_PNAME_PRIORITY); + if (pos) { + pos += os_strlen(FST_ATTACH_CMD_PNAME_PRIORITY); + if (*pos == '=') { + val = fst_read_next_int_param(pos + 1, &is_valid, + &endp); + if (is_valid) + cfg->priority = (u8) val; + } + } + + return 0; +} + + +int fst_parse_detach_command(const char *cmd, char *ifname, size_t ifname_size) +{ + char *endp; + + return fst_read_next_text_param(cmd, ifname, ifname_size, &endp); +} + + +int fst_iface_detach(const char *ifname) +{ + struct fst_group *g; + + foreach_fst_group(g) { + struct fst_iface *f; + + f = fst_group_get_iface_by_name(g, ifname); + if (f) { + fst_detach(f); + return 0; + } + } + + return -EINVAL; +} diff --git a/contrib/wpa/src/fst/fst_ctrl_iface.h b/contrib/wpa/src/fst/fst_ctrl_iface.h new file mode 100644 index 0000000..4d0cd9f --- /dev/null +++ b/contrib/wpa/src/fst/fst_ctrl_iface.h @@ -0,0 +1,45 @@ +/* + * FST module - internal Control interface definitions + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef FST_CTRL_IFACE_H +#define FST_CTRL_IFACE_H + +#include "fst/fst_ctrl_aux.h" + +#ifdef CONFIG_FST + +/* receiver */ +int fst_ctrl_iface_mb_info(const u8 *addr, char *buf, size_t buflen); + +int fst_ctrl_iface_receive(const char *txtaddr, char *buf, size_t buflen); + +extern const struct fst_ctrl *fst_ctrl_cli; + +#else /* CONFIG_FST */ + +static inline int +fst_ctrl_iface_mb_info(const u8 *addr, char *buf, size_t buflen) +{ + return 0; +} + +#endif /* CONFIG_FST */ + +int fst_read_next_int_param(const char *params, Boolean *valid, char **endp); +int fst_read_next_text_param(const char *params, char *buf, size_t buflen, + char **endp); +int fst_read_peer_addr(const char *mac, u8 *peer_addr); + +struct fst_iface_cfg; + +int fst_parse_attach_command(const char *cmd, char *ifname, size_t ifname_size, + struct fst_iface_cfg *cfg); +int fst_parse_detach_command(const char *cmd, char *ifname, size_t ifname_size); +int fst_iface_detach(const char *ifname); + +#endif /* CTRL_IFACE_FST_H */ diff --git a/contrib/wpa/src/fst/fst_defs.h b/contrib/wpa/src/fst/fst_defs.h new file mode 100644 index 0000000..8ddcc61 --- /dev/null +++ b/contrib/wpa/src/fst/fst_defs.h @@ -0,0 +1,87 @@ +/* + * FST module - FST related definitions + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef IEEE_80211_FST_DEFS_H +#define IEEE_80211_FST_DEFS_H + +/* IEEE Std 802.11ad */ + +#define MB_STA_CHANNEL_ALL 0 + +enum session_type { + SESSION_TYPE_BSS = 0, /* Infrastructure BSS */ + SESSION_TYPE_IBSS = 1, + SESSION_TYPE_DLS = 2, + SESSION_TYPE_TDLS = 3, + SESSION_TYPE_PBSS = 4 +}; + +#define SESSION_CONTROL(session_type, switch_intent) \ + (((u8) ((session_type) & 0x7)) | ((switch_intent) ? 0x10 : 0x00)) + +#define GET_SESSION_CONTROL_TYPE(session_control) \ + ((u8) ((session_control) & 0x7)) + +#define GET_SESSION_CONTROL_SWITCH_INTENT(session_control) \ + (((session_control) & 0x10) >> 4) + +/* 8.4.2.147 Session Transition element */ +struct session_transition_ie { + u8 element_id; + u8 length; + u32 fsts_id; + u8 session_control; + u8 new_band_id; + u8 new_band_setup; + u8 new_band_op; + u8 old_band_id; + u8 old_band_setup; + u8 old_band_op; +} STRUCT_PACKED; + +struct fst_setup_req { + u8 action; + u8 dialog_token; + u32 llt; + struct session_transition_ie stie; + /* Multi-band (optional) */ + /* Wakeup Schedule (optional) */ + /* Awake Window (optional) */ + /* Switching Stream (optional) */ +} STRUCT_PACKED; + +struct fst_setup_res { + u8 action; + u8 dialog_token; + u8 status_code; + struct session_transition_ie stie; + /* Multi-band (optional) */ + /* Wakeup Schedule (optional) */ + /* Awake Window (optional) */ + /* Switching Stream (optional) */ + /* Timeout Interval (optional) */ +} STRUCT_PACKED; + +struct fst_ack_req { + u8 action; + u8 dialog_token; + u32 fsts_id; +} STRUCT_PACKED; + +struct fst_ack_res { + u8 action; + u8 dialog_token; + u32 fsts_id; +} STRUCT_PACKED; + +struct fst_tear_down { + u8 action; + u32 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 new file mode 100644 index 0000000..f6c7be9 --- /dev/null +++ b/contrib/wpa/src/fst/fst_group.c @@ -0,0 +1,462 @@ +/* + * FST module - FST group object implementation + * Copyright (c) 2014, 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/defs.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "drivers/driver.h" +#include "fst/fst_internal.h" +#include "fst/fst_defs.h" + + +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) +{ + 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 *) p; + WPA_ASSERT(mbie->eid == WLAN_EID_MULTI_BAND); + WPA_ASSERT(2 + mbie->len >= sizeof(*mbie)); + + fst_printf(MSG_WARNING, + "%s: %s: mb_ctrl=%u band_id=%u op_class=%u chan=%u bssid=" + MACSTR + " beacon_int=%u tsf_offs=[%u %u %u %u %u %u %u %u] mb_cc=0x%02x tmout=%u", + group_id, ifname, + mbie->mb_ctrl, mbie->band_id, mbie->op_class, + mbie->chan, MAC2STR(mbie->bssid), mbie->beacon_int, + mbie->tsf_offs[0], mbie->tsf_offs[1], + mbie->tsf_offs[2], mbie->tsf_offs[3], + mbie->tsf_offs[4], mbie->tsf_offs[5], + mbie->tsf_offs[6], mbie->tsf_offs[7], + mbie->mb_connection_capability, + mbie->fst_session_tmout); + + p += 2 + mbie->len; + s -= 2 + mbie->len; + } +} + + +static void fst_fill_mb_ie(struct wpabuf *buf, const u8 *bssid, + const u8 *own_addr, enum mb_band_id band, u8 channel) +{ + struct multi_band_ie *mbie; + size_t len = sizeof(*mbie); + + if (own_addr) + len += ETH_ALEN; + + mbie = wpabuf_put(buf, len); + + os_memset(mbie, 0, len); + + mbie->eid = WLAN_EID_MULTI_BAND; + mbie->len = len - 2; +#ifdef HOSTAPD + mbie->mb_ctrl = MB_STA_ROLE_AP; + mbie->mb_connection_capability = MB_CONNECTION_CAPABILITY_AP; +#else /* HOSTAPD */ + mbie->mb_ctrl = MB_STA_ROLE_NON_PCP_NON_AP; + mbie->mb_connection_capability = 0; +#endif /* HOSTAPD */ + if (bssid) + os_memcpy(mbie->bssid, bssid, ETH_ALEN); + mbie->band_id = band; + mbie->op_class = 0; /* means all */ + mbie->chan = channel; + mbie->fst_session_tmout = FST_DEFAULT_SESSION_TIMEOUT_TU; + + if (own_addr) { + mbie->mb_ctrl |= MB_CTRL_STA_MAC_PRESENT; + os_memcpy(&mbie[1], own_addr, ETH_ALEN); + } +} + + +static unsigned fst_fill_iface_mb_ies(struct fst_iface *f, struct wpabuf *buf) +{ + const u8 *bssid; + + bssid = fst_iface_get_bssid(f); + if (bssid) { + enum hostapd_hw_mode hw_mode; + u8 channel; + + if (buf) { + fst_iface_get_channel_info(f, &hw_mode, &channel); + fst_fill_mb_ie(buf, bssid, fst_iface_get_addr(f), + fst_hw_mode_to_band(hw_mode), channel); + } + return 1; + } else { + unsigned bands[MB_BAND_ID_WIFI_60GHZ + 1] = {}; + struct hostapd_hw_modes *modes; + enum mb_band_id b; + int num_modes = fst_iface_get_hw_modes(f, &modes); + int ret = 0; + + while (num_modes--) { + b = fst_hw_mode_to_band(modes->mode); + modes++; + if (b >= ARRAY_SIZE(bands) || bands[b]++) + continue; + ret++; + if (buf) + fst_fill_mb_ie(buf, NULL, fst_iface_get_addr(f), + b, MB_STA_CHANNEL_ALL); + } + return ret; + } +} + + +static struct wpabuf * fst_group_create_mb_ie(struct fst_group *g, + struct fst_iface *i) +{ + struct wpabuf *buf; + 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) + continue; + nof_mbies += fst_fill_iface_mb_ies(f, NULL); + } + + buf = wpabuf_alloc(nof_mbies * + (sizeof(struct multi_band_ie) + ETH_ALEN)); + if (!buf) { + fst_printf_iface(i, MSG_ERROR, + "cannot allocate mem for %u MB IEs", + nof_mbies); + return NULL; + } + + /* The list is sorted in descending order by priorities, so MB IEs will + * be arranged in the same order, as required by spec (see corresponding + * comment in.fst_attach(). + */ + foreach_fst_group_iface(g, f) { + if (f == i) + continue; + + fst_fill_iface_mb_ies(f, buf); + ++nof_ifaces_added; + + fst_printf_iface(i, MSG_DEBUG, "added to MB IE"); + } + + if (!nof_ifaces_added) { + wpabuf_free(buf); + buf = NULL; + fst_printf_iface(i, MSG_INFO, + "cannot add MB IE: no backup ifaces"); + } else { + fst_dump_mb_ies(fst_group_get_id(g), fst_iface_get_name(i), + buf); + } + + return buf; +} + + +static const u8 * fst_mbie_get_peer_addr(const struct multi_band_ie *mbie) +{ + const u8 *peer_addr = NULL; + + switch (MB_CTRL_ROLE(mbie->mb_ctrl)) { + case MB_STA_ROLE_AP: + peer_addr = mbie->bssid; + break; + case MB_STA_ROLE_NON_PCP_NON_AP: + if (mbie->mb_ctrl & MB_CTRL_STA_MAC_PRESENT && + (size_t) 2 + mbie->len >= sizeof(*mbie) + ETH_ALEN) + peer_addr = (const u8 *) &mbie[1]; + break; + default: + break; + } + + return peer_addr; +} + + +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) +{ + while (mb_ies_size >= 2) { + const struct multi_band_ie *mbie = + (const struct multi_band_ie *) mb_ies_buff; + + 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; + } + + mb_ies_buff += 2 + mbie->len; + mb_ies_size -= 2 + mbie->len; + } + + return NULL; +} + + +struct fst_iface * fst_group_get_iface_by_name(struct fst_group *g, + const char *ifname) +{ + struct fst_iface *f; + + foreach_fst_group_iface(g, f) { + const char *in = fst_iface_get_name(f); + + if (os_strncmp(in, ifname, os_strlen(in)) == 0) + return f; + } + + return NULL; +} + + +u8 fst_group_assign_dialog_token(struct fst_group *g) +{ + g->dialog_token++; + if (g->dialog_token == 0) + g->dialog_token++; + return g->dialog_token; +} + + +u32 fst_group_assign_fsts_id(struct fst_group *g) +{ + g->fsts_id++; + return g->fsts_id; +} + + +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) +{ + struct fst_get_peer_ctx *ctx; + const u8 *addr; + const u8 *iface_addr; + enum mb_band_id iface_band_id; + + WPA_ASSERT(g == fst_iface_get_group(iface)); + WPA_ASSERT(g == fst_iface_get_group(other)); + + iface_addr = fst_iface_get_addr(iface); + iface_band_id = fst_iface_get_band_id(iface); + + 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; + + mbies = fst_iface_get_peer_mb_ie(other, addr); + if (!mbies) + 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; + } + } + + 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) +{ + 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); +} + + +struct fst_group * fst_group_create(const char *group_id) +{ + struct fst_group *g; + + g = os_zalloc(sizeof(*g)); + if (g == NULL) { + fst_printf(MSG_ERROR, "%s: Cannot alloc group", group_id); + return NULL; + } + + dl_list_init(&g->ifaces); + os_strlcpy(g->group_id, group_id, sizeof(g->group_id)); + + dl_list_add_tail(&fst_global_groups_list, &g->global_groups_lentry); + fst_printf_group(g, MSG_DEBUG, "instance created"); + + foreach_fst_ctrl_call(on_group_created, g); + + return g; +} + + +void fst_group_attach_iface(struct fst_group *g, struct fst_iface *i) +{ + struct dl_list *list = &g->ifaces; + struct fst_iface *f; + + /* + * Add new interface to the list. + * The list is sorted in descending order by priority to allow + * multiple MB IEs creation according to the spec (see 10.32 Multi-band + * operation, 10.32.1 General), as they should be ordered according to + * priorities. + */ + foreach_fst_group_iface(g, f) { + if (fst_iface_get_priority(f) < fst_iface_get_priority(i)) + break; + list = &f->group_lentry; + } + dl_list_add(list, &i->group_lentry); +} + + +void fst_group_detach_iface(struct fst_group *g, struct fst_iface *i) +{ + dl_list_del(&i->group_lentry); +} + + +void fst_group_delete(struct fst_group *group) +{ + struct fst_session *s; + + dl_list_del(&group->global_groups_lentry); + WPA_ASSERT(dl_list_empty(&group->ifaces)); + foreach_fst_ctrl_call(on_group_deleted, group); + fst_printf_group(group, MSG_DEBUG, "instance deleted"); + while ((s = fst_session_global_get_first_by_group(group)) != NULL) + fst_session_delete(s); + os_free(group); +} + + +Boolean fst_group_delete_if_empty(struct fst_group *group) +{ + Boolean is_empty = !fst_group_has_ifaces(group) && + !fst_session_global_get_first_by_group(group); + + if (is_empty) + fst_group_delete(group); + + return is_empty; +} + + +void fst_group_update_ie(struct fst_group *g) +{ + struct fst_iface *i; + + foreach_fst_group_iface(g, i) { + struct wpabuf *mbie = fst_group_create_mb_ie(g, i); + + if (!mbie) + fst_printf_iface(i, MSG_WARNING, "cannot create MB IE"); + + fst_iface_attach_mbie(i, mbie); + fst_iface_set_ies(i, mbie); + fst_printf_iface(i, MSG_DEBUG, "multi-band IE set to %p", mbie); + } +} diff --git a/contrib/wpa/src/fst/fst_group.h b/contrib/wpa/src/fst/fst_group.h new file mode 100644 index 0000000..3a87c0b --- /dev/null +++ b/contrib/wpa/src/fst/fst_group.h @@ -0,0 +1,75 @@ +/* + * FST module - FST group object definitions + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef FST_GROUP_H +#define FST_GROUP_H + +struct fst_group { + char group_id[IFNAMSIZ + 1]; + struct dl_list ifaces; + u8 dialog_token; + u32 fsts_id; + struct dl_list global_groups_lentry; +}; + +struct session_transition_ie; + +#define foreach_fst_group_iface(g, i) \ + dl_list_for_each((i), &(g)->ifaces, struct fst_iface, group_lentry) + +struct fst_group * fst_group_create(const char *group_id); +void fst_group_attach_iface(struct fst_group *g, struct fst_iface *i); +void fst_group_detach_iface(struct fst_group *g, struct fst_iface *i); +void fst_group_delete(struct fst_group *g); + +void fst_group_update_ie(struct fst_group *g); + +static inline Boolean fst_group_has_ifaces(struct fst_group *g) +{ + return !dl_list_empty(&g->ifaces); +} + +static inline struct fst_iface * fst_group_first_iface(struct fst_group *g) +{ + return dl_list_first(&g->ifaces, struct fst_iface, group_lentry); +} + +static inline const char * fst_group_get_id(struct fst_group *g) +{ + return g->group_id; +} + +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); +u8 fst_group_assign_dialog_token(struct fst_group *g); +u32 fst_group_assign_fsts_id(struct fst_group *g); + +extern struct dl_list fst_global_groups_list; + +#define foreach_fst_group(g) \ + dl_list_for_each((g), &fst_global_groups_list, \ + struct fst_group, global_groups_lentry) + +static inline struct fst_group * fst_first_group(void) +{ + return dl_list_first(&fst_global_groups_list, struct fst_group, + global_groups_lentry); +} + +#endif /* FST_GROUP_H */ diff --git a/contrib/wpa/src/fst/fst_iface.c b/contrib/wpa/src/fst/fst_iface.c new file mode 100644 index 0000000..5a92d2c --- /dev/null +++ b/contrib/wpa/src/fst/fst_iface.c @@ -0,0 +1,79 @@ +/* + * FST module - FST interface object implementation + * Copyright (c) 2014, 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 "fst/fst_internal.h" +#include "fst/fst_defs.h" + + +struct fst_iface * fst_iface_create(struct fst_group *g, const char *ifname, + const u8 *own_addr, + const struct fst_wpa_obj *iface_obj, + const struct fst_iface_cfg *cfg) +{ + struct fst_iface *i; + + i = os_zalloc(sizeof(*i)); + if (!i) { + fst_printf_group(g, MSG_ERROR, "cannot allocate iface for %s", + ifname); + return NULL; + } + + i->cfg = *cfg; + i->iface_obj = *iface_obj; + i->group = g; + os_strlcpy(i->ifname, ifname, sizeof(i->ifname)); + os_memcpy(i->own_addr, own_addr, sizeof(i->own_addr)); + + if (!i->cfg.llt) { + fst_printf_iface(i, MSG_WARNING, "Zero llt adjusted"); + i->cfg.llt = FST_DEFAULT_LLT_CFG_VALUE; + } + + return i; +} + + +void fst_iface_delete(struct fst_iface *i) +{ + fst_iface_set_ies(i, NULL); + wpabuf_free(i->mb_ie); + os_free(i); +} + + +Boolean fst_iface_is_connected(struct fst_iface *iface, const u8 *addr) +{ + struct fst_get_peer_ctx *ctx; + const u8 *a = fst_iface_get_peer_first(iface, &ctx, TRUE); + + for (; a != NULL; a = fst_iface_get_peer_next(iface, &ctx, TRUE)) + if (os_memcmp(addr, a, ETH_ALEN) == 0) + return TRUE; + + return FALSE; +} + + +void fst_iface_attach_mbie(struct fst_iface *i, struct wpabuf *mbie) +{ + wpabuf_free(i->mb_ie); + i->mb_ie = mbie; +} + + +enum mb_band_id fst_iface_get_band_id(struct fst_iface *i) +{ + enum hostapd_hw_mode hw_mode; + u8 channel; + + fst_iface_get_channel_info(i, &hw_mode, &channel); + return fst_hw_mode_to_band(hw_mode); +} diff --git a/contrib/wpa/src/fst/fst_iface.h b/contrib/wpa/src/fst/fst_iface.h new file mode 100644 index 0000000..4670d89 --- /dev/null +++ b/contrib/wpa/src/fst/fst_iface.h @@ -0,0 +1,135 @@ +/* + * FST module - FST interface object definitions + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + + +#ifndef FST_IFACE_H +#define FST_IFACE_H + +#include "utils/includes.h" +#include "utils/common.h" +#include "list.h" +#include "fst.h" + +struct fst_iface { + struct fst_group *group; + struct fst_wpa_obj iface_obj; + u8 own_addr[ETH_ALEN]; + struct wpabuf *mb_ie; + char ifname[IFNAMSIZ + 1]; + struct fst_iface_cfg cfg; + struct dl_list group_lentry; +}; + +struct fst_iface * fst_iface_create(struct fst_group *g, const char *ifname, + const u8 *own_addr, + const struct fst_wpa_obj *iface_obj, + const struct fst_iface_cfg *cfg); +void fst_iface_delete(struct fst_iface *i); + +static inline struct fst_group * fst_iface_get_group(struct fst_iface *i) +{ + return i->group; +} + +static inline const char * fst_iface_get_name(struct fst_iface *i) +{ + return i->ifname; +} + +static inline const u8 * fst_iface_get_addr(struct fst_iface *i) +{ + return i->own_addr; +} + +static inline const char * fst_iface_get_group_id(struct fst_iface *i) +{ + return i->cfg.group_id; +} + +static inline u8 fst_iface_get_priority(struct fst_iface *i) +{ + return i->cfg.priority; +} + +static inline u32 fst_iface_get_llt(struct fst_iface *i) +{ + return i->cfg.llt; +} + +static inline const struct wpabuf * fst_iface_get_mbie(struct fst_iface *i) +{ + return i->mb_ie; +} + +static inline const u8 * fst_iface_get_bssid(struct fst_iface *i) +{ + return i->iface_obj.get_bssid(i->iface_obj.ctx); +} + +static inline void fst_iface_get_channel_info(struct fst_iface *i, + enum hostapd_hw_mode *hw_mode, + u8 *channel) +{ + i->iface_obj.get_channel_info(i->iface_obj.ctx, hw_mode, channel); +} + +static inline int fst_iface_get_hw_modes(struct fst_iface *i, + struct hostapd_hw_modes **modes) +{ + return i->iface_obj.get_hw_modes(i->iface_obj.ctx, modes); +} + +static inline void fst_iface_set_ies(struct fst_iface *i, + const struct wpabuf *fst_ies) +{ + i->iface_obj.set_ies(i->iface_obj.ctx, fst_ies); +} + +static inline int fst_iface_send_action(struct fst_iface *i, + const u8 *addr, struct wpabuf *data) +{ + return i->iface_obj.send_action(i->iface_obj.ctx, addr, data); +} + +static inline const struct wpabuf * +fst_iface_get_peer_mb_ie(struct fst_iface *i, const u8 *addr) +{ + return i->iface_obj.get_mb_ie(i->iface_obj.ctx, addr); +} + +static inline void fst_iface_update_mb_ie(struct fst_iface *i, + const u8 *addr, + const u8 *buf, size_t size) +{ + return i->iface_obj.update_mb_ie(i->iface_obj.ctx, addr, buf, size); +} + +static inline const u8 * fst_iface_get_peer_first(struct fst_iface *i, + struct fst_get_peer_ctx **ctx, + Boolean mb_only) +{ + return i->iface_obj.get_peer_first(i->iface_obj.ctx, ctx, mb_only); +} + +static inline const u8 * fst_iface_get_peer_next(struct fst_iface *i, + struct fst_get_peer_ctx **ctx, + Boolean mb_only) +{ + 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); +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); + +static inline void * fst_iface_get_wpa_obj_ctx(struct fst_iface *i) +{ + return i->iface_obj.ctx; +} + +#endif /* FST_IFACE_H */ diff --git a/contrib/wpa/src/fst/fst_internal.h b/contrib/wpa/src/fst/fst_internal.h new file mode 100644 index 0000000..9fe32b8 --- /dev/null +++ b/contrib/wpa/src/fst/fst_internal.h @@ -0,0 +1,49 @@ +/* + * FST module - auxiliary definitions + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef FST_INTERNAL_H +#define FST_INTERNAL_H + +#include "utils/includes.h" +#include "utils/common.h" +#include "common/defs.h" +#include "common/ieee802_11_defs.h" +#include "fst/fst_iface.h" +#include "fst/fst_group.h" +#include "fst/fst_session.h" + +#define fst_printf(level, format, ...) \ + wpa_printf((level), "FST: " format, ##__VA_ARGS__) + +#define fst_printf_group(group, level, format, ...) \ + wpa_printf((level), "FST: %s: " format, \ + fst_group_get_id(group), ##__VA_ARGS__) + +#define fst_printf_iface(iface, level, format, ...) \ + fst_printf_group(fst_iface_get_group(iface), (level), "%s: " format, \ + fst_iface_get_name(iface), ##__VA_ARGS__) + +enum mb_band_id fst_hw_mode_to_band(enum hostapd_hw_mode mode); + +struct fst_ctrl_handle { + struct fst_ctrl ctrl; + struct dl_list global_ctrls_lentry; +}; + +extern struct dl_list fst_global_ctrls_list; + +#define foreach_fst_ctrl_call(clb, ...) \ + do { \ + struct fst_ctrl_handle *__fst_ctrl_h; \ + dl_list_for_each(__fst_ctrl_h, &fst_global_ctrls_list, \ + struct fst_ctrl_handle, global_ctrls_lentry) \ + if (__fst_ctrl_h->ctrl.clb) \ + __fst_ctrl_h->ctrl.clb(__VA_ARGS__);\ + } while (0) + +#endif /* FST_INTERNAL_H */ diff --git a/contrib/wpa/src/fst/fst_session.c b/contrib/wpa/src/fst/fst_session.c new file mode 100644 index 0000000..55fa694 --- /dev/null +++ b/contrib/wpa/src/fst/fst_session.c @@ -0,0 +1,1620 @@ +/* + * FST module - FST Session implementation + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "common/defs.h" +#include "fst/fst_internal.h" +#include "fst/fst_defs.h" +#include "fst/fst_ctrl_iface.h" +#ifdef CONFIG_FST_TEST +#include "fst/fst_ctrl_defs.h" +#endif /* CONFIG_FST_TEST */ + +#define US_80211_TU 1024 + +#define US_TO_TU(m) ((m) * / US_80211_TU) +#define TU_TO_US(m) ((m) * US_80211_TU) + +#define FST_LLT_SWITCH_IMMEDIATELY 0 + +#define fst_printf_session(s, level, format, ...) \ + fst_printf((level), "%u (0x%08x): [" MACSTR "," MACSTR "] :" format, \ + (s)->id, (s)->data.fsts_id, \ + MAC2STR((s)->data.old_peer_addr), \ + MAC2STR((s)->data.new_peer_addr), \ + ##__VA_ARGS__) + +#define fst_printf_siface(s, iface, level, format, ...) \ + fst_printf_session((s), (level), "%s: " format, \ + fst_iface_get_name(iface), ##__VA_ARGS__) + +#define fst_printf_sframe(s, is_old, level, format, ...) \ + fst_printf_siface((s), \ + (is_old) ? (s)->data.old_iface : (s)->data.new_iface, \ + (level), format, ##__VA_ARGS__) + +#define FST_LLT_MS_DEFAULT 50 +#define FST_ACTION_MAX_SUPPORTED FST_ACTION_ON_CHANNEL_TUNNEL + +const char * const fst_action_names[] = { + [FST_ACTION_SETUP_REQUEST] = "Setup Request", + [FST_ACTION_SETUP_RESPONSE] = "Setup Response", + [FST_ACTION_TEAR_DOWN] = "Tear Down", + [FST_ACTION_ACK_REQUEST] = "Ack Request", + [FST_ACTION_ACK_RESPONSE] = "Ack Response", + [FST_ACTION_ON_CHANNEL_TUNNEL] = "On Channel Tunnel", +}; + +struct fst_session { + struct { + /* Session configuration that can be zeroed on reset */ + u8 old_peer_addr[ETH_ALEN]; + u8 new_peer_addr[ETH_ALEN]; + struct fst_iface *new_iface; + struct fst_iface *old_iface; + u32 llt_ms; + u8 pending_setup_req_dlgt; + u32 fsts_id; /* FSTS ID, see spec, 8.4.2.147 + * Session Transition element */ + } data; + /* Session object internal fields which won't be zeroed on reset */ + struct dl_list global_sessions_lentry; + u32 id; /* Session object ID used to identify + * specific session object */ + struct fst_group *group; + enum fst_session_state state; + Boolean stt_armed; +}; + +static struct dl_list global_sessions_list; +static u32 global_session_id = 0; + +#define foreach_fst_session(s) \ + dl_list_for_each((s), &global_sessions_list, \ + struct fst_session, global_sessions_lentry) + +#define foreach_fst_session_safe(s, temp) \ + dl_list_for_each_safe((s), (temp), &global_sessions_list, \ + struct fst_session, global_sessions_lentry) + + +static void fst_session_global_inc_id(void) +{ + global_session_id++; + if (global_session_id == FST_INVALID_SESSION_ID) + global_session_id++; +} + + +int fst_session_global_init(void) +{ + dl_list_init(&global_sessions_list); + return 0; +} + + +void fst_session_global_deinit(void) +{ + WPA_ASSERT(dl_list_empty(&global_sessions_list)); +} + + +static inline void fst_session_notify_ctrl(struct fst_session *s, + enum fst_event_type event_type, + union fst_event_extra *extra) +{ + foreach_fst_ctrl_call(on_event, event_type, NULL, s, extra); +} + + +static void fst_session_set_state(struct fst_session *s, + enum fst_session_state state, + union fst_session_state_switch_extra *extra) +{ + if (s->state != state) { + union fst_event_extra evext = { + .session_state = { + .old_state = s->state, + .new_state = state, + }, + }; + + if (extra) + evext.session_state.extra = *extra; + fst_session_notify_ctrl(s, EVENT_FST_SESSION_STATE_CHANGED, + &evext); + fst_printf_session(s, MSG_INFO, "State: %s => %s", + fst_session_state_name(s->state), + fst_session_state_name(state)); + s->state = state; + } +} + + +static u32 fst_find_free_session_id(void) +{ + u32 i, id = FST_INVALID_SESSION_ID; + struct fst_session *s; + + for (i = 0; i < (u32) -1; i++) { + Boolean in_use = FALSE; + + foreach_fst_session(s) { + if (s->id == global_session_id) { + fst_session_global_inc_id(); + in_use = TRUE; + break; + } + } + if (!in_use) { + id = global_session_id; + fst_session_global_inc_id(); + break; + } + } + + return id; +} + + +static void fst_session_timeout_handler(void *eloop_data, void *user_ctx) +{ + struct fst_session *s = user_ctx; + union fst_session_state_switch_extra extra = { + .to_initial = { + .reason = REASON_STT, + }, + }; + + fst_printf_session(s, MSG_WARNING, "Session State Timeout"); + fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &extra); +} + + +static void fst_session_stt_arm(struct fst_session *s) +{ + eloop_register_timeout(0, TU_TO_US(FST_DEFAULT_SESSION_TIMEOUT_TU), + fst_session_timeout_handler, NULL, s); + s->stt_armed = TRUE; +} + + +static void fst_session_stt_disarm(struct fst_session *s) +{ + if (s->stt_armed) { + eloop_cancel_timeout(fst_session_timeout_handler, NULL, s); + s->stt_armed = FALSE; + } +} + + +static Boolean fst_session_is_in_transition(struct fst_session *s) +{ + /* See spec, 10.32.2.2 Transitioning between states */ + return s->stt_armed; +} + + +static int fst_session_is_in_progress(struct fst_session *s) +{ + return s->state != FST_SESSION_STATE_INITIAL; +} + + +static int fst_session_is_ready_pending(struct fst_session *s) +{ + return s->state == FST_SESSION_STATE_SETUP_COMPLETION && + fst_session_is_in_transition(s); +} + + +static int fst_session_is_ready(struct fst_session *s) +{ + return s->state == FST_SESSION_STATE_SETUP_COMPLETION && + !fst_session_is_in_transition(s); +} + + +static int fst_session_is_switch_requested(struct fst_session *s) +{ + return s->state == FST_SESSION_STATE_TRANSITION_DONE && + fst_session_is_in_transition(s); +} + + +static struct fst_session * +fst_find_session_in_progress(const u8 *peer_addr, struct fst_group *g) +{ + struct fst_session *s; + + foreach_fst_session(s) { + if (s->group == g && + (os_memcmp(s->data.old_peer_addr, peer_addr, + ETH_ALEN) == 0 || + os_memcmp(s->data.new_peer_addr, peer_addr, + ETH_ALEN) == 0) && + fst_session_is_in_progress(s)) + return s; + } + + return NULL; +} + + +static void fst_session_reset_ex(struct fst_session *s, enum fst_reason reason) +{ + union fst_session_state_switch_extra evext = { + .to_initial = { + .reason = reason, + }, + }; + + if (s->state == FST_SESSION_STATE_SETUP_COMPLETION || + s->state == FST_SESSION_STATE_TRANSITION_DONE) + fst_session_tear_down_setup(s); + fst_session_stt_disarm(s); + os_memset(&s->data, 0, sizeof(s->data)); + fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); +} + + +static int fst_session_send_action(struct fst_session *s, Boolean old_iface, + const void *payload, size_t size, + const struct wpabuf *extra_buf) +{ + size_t len; + int res; + struct wpabuf *buf; + u8 action; + struct fst_iface *iface = + old_iface ? s->data.old_iface : s->data.new_iface; + + WPA_ASSERT(payload != NULL); + WPA_ASSERT(size != 0); + + action = *(const u8 *) payload; + + WPA_ASSERT(action <= FST_ACTION_MAX_SUPPORTED); + + if (!iface) { + fst_printf_session(s, MSG_ERROR, + "no %s interface for FST Action '%s' sending", + old_iface ? "old" : "new", + fst_action_names[action]); + return -1; + } + + len = sizeof(u8) /* category */ + size; + if (extra_buf) + len += wpabuf_size(extra_buf); + + buf = wpabuf_alloc(len); + if (!buf) { + fst_printf_session(s, MSG_ERROR, + "cannot allocate buffer of %zu bytes for FST Action '%s' sending", + len, fst_action_names[action]); + return -1; + } + + wpabuf_put_u8(buf, WLAN_ACTION_FST); + wpabuf_put_data(buf, payload, size); + if (extra_buf) + wpabuf_put_buf(buf, extra_buf); + + res = fst_iface_send_action(iface, + old_iface ? s->data.old_peer_addr : + s->data.new_peer_addr, buf); + if (res < 0) + fst_printf_siface(s, iface, MSG_ERROR, + "failed to send FST Action '%s'", + fst_action_names[action]); + else + fst_printf_siface(s, iface, MSG_DEBUG, "FST Action '%s' sent", + fst_action_names[action]); + wpabuf_free(buf); + + return res; +} + + +static int fst_session_send_tear_down(struct fst_session *s) +{ + struct fst_tear_down td; + int res; + + if (!fst_session_is_in_progress(s)) { + fst_printf_session(s, MSG_ERROR, "No FST setup to tear down"); + return -1; + } + + WPA_ASSERT(s->data.old_iface != NULL); + WPA_ASSERT(s->data.new_iface != NULL); + + os_memset(&td, 0, sizeof(td)); + + td.action = FST_ACTION_TEAR_DOWN; + td.fsts_id = host_to_le32(s->data.fsts_id); + + res = fst_session_send_action(s, TRUE, &td, sizeof(td), NULL); + if (!res) + fst_printf_sframe(s, TRUE, MSG_INFO, "FST TearDown sent"); + else + fst_printf_sframe(s, TRUE, MSG_ERROR, + "failed to send FST TearDown"); + + return res; +} + + +static void fst_session_handle_setup_request(struct fst_iface *iface, + const struct ieee80211_mgmt *mgmt, + size_t frame_len) +{ + struct fst_session *s; + const struct fst_setup_req *req; + 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)) { + fst_printf_iface(iface, MSG_WARNING, + "FST Request dropped: too short (%zu < %zu)", + frame_len, + IEEE80211_HDRLEN + 1 + sizeof(*req)); + return; + } + plen = frame_len - IEEE80211_HDRLEN - 1; + req = (const struct fst_setup_req *) + (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1); + if (req->stie.element_id != WLAN_EID_SESSION_TRANSITION || + req->stie.length < 11) { + fst_printf_iface(iface, MSG_WARNING, + "FST Request dropped: invalid STIE"); + return; + } + + if (req->stie.new_band_id == req->stie.old_band_id) { + fst_printf_iface(iface, MSG_WARNING, + "FST Request dropped: new and old band IDs are the same"); + return; + } + + g = fst_iface_get_group(iface); + + if (plen > sizeof(*req)) { + fst_iface_update_mb_ie(iface, mgmt->sa, (const u8 *) (req + 1), + plen - sizeof(*req)); + fst_printf_iface(iface, MSG_INFO, + "FST Request: MB IEs updated for " MACSTR, + 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)); + } + + if (!new_iface) { + fst_printf_iface(iface, MSG_WARNING, + "FST Request dropped: new iface not found"); + return; + } + + s = fst_find_session_in_progress(mgmt->sa, g); + if (s) { + union fst_session_state_switch_extra evext = { + .to_initial = { + .reason = REASON_SETUP, + }, + }; + + /* + * 10.32.2.2 Transitioning between states: + * Upon receipt of an FST Setup Request frame, the responder + * shall respond with an FST Setup Response frame unless it has + * a pending FST Setup Request frame addressed to the initiator + * and the responder has a numerically larger MAC address than + * 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) { + fst_printf_session(s, MSG_WARNING, + "FST Request dropped due to MAC comparison (our MAC is " + MACSTR ")", + MAC2STR(mgmt->da)); + 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; + } + + + /* + * 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. + * 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. + */ + if (le_to_host32(req->stie.fsts_id) != s->data.fsts_id) + fst_session_send_tear_down(s); + else + fst_printf_session(s, MSG_WARNING, + "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); + if (!s) { + fst_printf(MSG_WARNING, + "FST Request dropped: cannot create session for %s and %s", + fst_iface_get_name(iface), + fst_iface_get_name(new_iface)); + return; + } + + fst_session_set_iface(s, iface, TRUE); + fst_session_set_peer_addr(s, mgmt->sa, TRUE); + fst_session_set_iface(s, new_iface, FALSE); + fst_session_set_peer_addr(s, new_iface_peer_addr, FALSE); + fst_session_set_llt(s, FST_LLT_VAL_TO_MS(le_to_host32(req->llt))); + s->data.pending_setup_req_dlgt = req->dialog_token; + s->data.fsts_id = le_to_host32(req->stie.fsts_id); + + fst_session_stt_arm(s); + + fst_session_notify_ctrl(s, EVENT_FST_SETUP, NULL); + + fst_session_set_state(s, FST_SESSION_STATE_SETUP_COMPLETION, NULL); +} + + +static void fst_session_handle_setup_response(struct fst_session *s, + struct fst_iface *iface, + const struct ieee80211_mgmt *mgmt, + size_t frame_len) +{ + const struct fst_setup_res *res; + size_t plen = frame_len - IEEE80211_HDRLEN - 1; + enum hostapd_hw_mode hw_mode; + u8 channel; + union fst_session_state_switch_extra evext = { + .to_initial = {0}, + }; + + if (iface != s->data.old_iface) { + fst_printf_session(s, MSG_WARNING, + "FST Response dropped: %s is not the old iface", + fst_iface_get_name(iface)); + return; + } + + if (!fst_session_is_ready_pending(s)) { + fst_printf_session(s, MSG_WARNING, + "FST Response dropped due to wrong state: %s", + fst_session_state_name(s->state)); + return; + } + + if (plen < sizeof(*res)) { + fst_printf_session(s, MSG_WARNING, + "Too short FST Response dropped"); + return; + } + res = (const struct fst_setup_res *) + (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1); + if (res->stie.element_id != WLAN_EID_SESSION_TRANSITION || + res->stie.length < 11) { + fst_printf_iface(iface, MSG_WARNING, + "FST Response dropped: invalid STIE"); + return; + } + + if (res->dialog_token != s->data.pending_setup_req_dlgt) { + fst_printf_session(s, MSG_WARNING, + "FST Response dropped due to wrong dialog token (%u != %u)", + s->data.pending_setup_req_dlgt, + res->dialog_token); + return; + } + + if (res->status_code == WLAN_STATUS_SUCCESS && + le_to_host32(res->stie.fsts_id) != s->data.fsts_id) { + fst_printf_session(s, MSG_WARNING, + "FST Response dropped due to wrong FST Session ID (%u)", + le_to_host32(res->stie.fsts_id)); + return; + } + + fst_session_stt_disarm(s); + + if (res->status_code != WLAN_STATUS_SUCCESS) { + /* + * 10.32.2.2 Transitioning between states + * The initiator shall set the STT to the value of the + * FSTSessionTimeOut field at ... and at each ACK frame sent in + * response to a received FST Setup Response with the Status + * Code field equal to PENDING_ADMITTING_FST_SESSION or + * PENDING_GAP_IN_BA_WINDOW. + */ + evext.to_initial.reason = REASON_REJECT; + evext.to_initial.reject_code = res->status_code; + evext.to_initial.initiator = FST_INITIATOR_REMOTE; + fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); + fst_printf_session(s, MSG_WARNING, + "FST Setup rejected by remote side with status %u", + res->status_code); + return; + } + + fst_iface_get_channel_info(s->data.new_iface, &hw_mode, &channel); + + if (fst_hw_mode_to_band(hw_mode) != res->stie.new_band_id) { + evext.to_initial.reason = REASON_ERROR_PARAMS; + fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); + fst_printf_session(s, MSG_WARNING, + "invalid FST Setup parameters"); + fst_session_tear_down_setup(s); + return; + } + + fst_printf_session(s, MSG_INFO, + "%s: FST Setup established for %s (llt=%u)", + fst_iface_get_name(s->data.old_iface), + fst_iface_get_name(s->data.new_iface), + s->data.llt_ms); + + fst_session_notify_ctrl(s, EVENT_FST_ESTABLISHED, NULL); + + if (s->data.llt_ms == FST_LLT_SWITCH_IMMEDIATELY) + fst_session_initiate_switch(s); +} + + +static void fst_session_handle_tear_down(struct fst_session *s, + struct fst_iface *iface, + const struct ieee80211_mgmt *mgmt, + size_t frame_len) +{ + const struct fst_tear_down *td; + size_t plen = frame_len - IEEE80211_HDRLEN - 1; + union fst_session_state_switch_extra evext = { + .to_initial = { + .reason = REASON_TEARDOWN, + .initiator = FST_INITIATOR_REMOTE, + }, + }; + + if (plen < sizeof(*td)) { + fst_printf_session(s, MSG_WARNING, + "Too short FST Tear Down dropped"); + return; + } + td = (const struct fst_tear_down *) + (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1); + + if (le_to_host32(td->fsts_id) != s->data.fsts_id) { + fst_printf_siface(s, iface, MSG_WARNING, + "tear down for wrong FST Setup ID (%u)", + le_to_host32(td->fsts_id)); + return; + } + + fst_session_stt_disarm(s); + + fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); +} + + +static void fst_session_handle_ack_request(struct fst_session *s, + struct fst_iface *iface, + const struct ieee80211_mgmt *mgmt, + size_t frame_len) +{ + const struct fst_ack_req *req; + size_t plen = frame_len - IEEE80211_HDRLEN - 1; + struct fst_ack_res res; + union fst_session_state_switch_extra evext = { + .to_initial = { + .reason = REASON_SWITCH, + .initiator = FST_INITIATOR_REMOTE, + }, + }; + + if (!fst_session_is_ready(s) && !fst_session_is_switch_requested(s)) { + fst_printf_siface(s, iface, MSG_ERROR, + "cannot initiate switch due to wrong session state (%s)", + fst_session_state_name(s->state)); + return; + } + + WPA_ASSERT(s->data.new_iface != NULL); + + if (iface != s->data.new_iface) { + fst_printf_siface(s, iface, MSG_ERROR, + "Ack received on wrong interface"); + return; + } + + if (plen < sizeof(*req)) { + fst_printf_session(s, MSG_WARNING, + "Too short FST Ack Request dropped"); + return; + } + req = (const struct fst_ack_req *) + (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1); + + if (le_to_host32(req->fsts_id) != s->data.fsts_id) { + fst_printf_siface(s, iface, MSG_WARNING, + "Ack for wrong FST Setup ID (%u)", + le_to_host32(req->fsts_id)); + return; + } + + os_memset(&res, 0, sizeof(res)); + + res.action = FST_ACTION_ACK_RESPONSE; + res.dialog_token = req->dialog_token; + res.fsts_id = req->fsts_id; + + if (!fst_session_send_action(s, FALSE, &res, sizeof(res), NULL)) { + fst_printf_sframe(s, FALSE, MSG_INFO, "FST Ack Response sent"); + fst_session_stt_disarm(s); + fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_DONE, + NULL); + fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_CONFIRMED, + NULL); + fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); + } +} + + +static void +fst_session_handle_ack_response(struct fst_session *s, + struct fst_iface *iface, + const struct ieee80211_mgmt *mgmt, + size_t frame_len) +{ + const struct fst_ack_res *res; + size_t plen = frame_len - IEEE80211_HDRLEN - 1; + union fst_session_state_switch_extra evext = { + .to_initial = { + .reason = REASON_SWITCH, + .initiator = FST_INITIATOR_LOCAL, + }, + }; + + if (!fst_session_is_switch_requested(s)) { + fst_printf_siface(s, iface, MSG_ERROR, + "Ack Response in inappropriate session state (%s)", + fst_session_state_name(s->state)); + return; + } + + WPA_ASSERT(s->data.new_iface != NULL); + + if (iface != s->data.new_iface) { + fst_printf_siface(s, iface, MSG_ERROR, + "Ack response received on wrong interface"); + return; + } + + if (plen < sizeof(*res)) { + fst_printf_session(s, MSG_WARNING, + "Too short FST Ack Response dropped"); + return; + } + res = (const struct fst_ack_res *) + (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1); + + if (le_to_host32(res->fsts_id) != s->data.fsts_id) { + fst_printf_siface(s, iface, MSG_ERROR, + "Ack response for wrong FST Setup ID (%u)", + le_to_host32(res->fsts_id)); + return; + } + + fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_CONFIRMED, NULL); + fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); + + fst_session_stt_disarm(s); +} + + +struct fst_session * fst_session_create(struct fst_group *g) +{ + struct fst_session *s; + u32 id; + + WPA_ASSERT(!is_zero_ether_addr(own_addr)); + + id = fst_find_free_session_id(); + if (id == FST_INVALID_SESSION_ID) { + fst_printf(MSG_ERROR, "Cannot assign new session ID"); + return NULL; + } + + s = os_zalloc(sizeof(*s)); + if (!s) { + fst_printf(MSG_ERROR, "Cannot allocate new session object"); + return NULL; + } + + s->id = id; + s->group = g; + s->state = FST_SESSION_STATE_INITIAL; + + s->data.llt_ms = FST_LLT_MS_DEFAULT; + + fst_printf(MSG_INFO, "Session %u created", s->id); + + dl_list_add_tail(&global_sessions_list, &s->global_sessions_lentry); + + foreach_fst_ctrl_call(on_session_added, s); + + return s; +} + + +void fst_session_set_iface(struct fst_session *s, struct fst_iface *iface, + Boolean is_old) +{ + if (is_old) + s->data.old_iface = iface; + else + s->data.new_iface = iface; + +} + + +void fst_session_set_llt(struct fst_session *s, u32 llt) +{ + s->data.llt_ms = llt; +} + + +void fst_session_set_peer_addr(struct fst_session *s, const u8 *addr, + Boolean is_old) +{ + u8 *a = is_old ? s->data.old_peer_addr : s->data.new_peer_addr; + + os_memcpy(a, addr, ETH_ALEN); +} + + +int fst_session_initiate_setup(struct fst_session *s) +{ + struct fst_setup_req req; + int res; + u32 fsts_id; + u8 dialog_token; + struct fst_session *_s; + + if (fst_session_is_in_progress(s)) { + fst_printf_session(s, MSG_ERROR, "Session in progress"); + return -EINVAL; + } + + if (is_zero_ether_addr(s->data.old_peer_addr)) { + fst_printf_session(s, MSG_ERROR, "No old peer MAC address"); + return -EINVAL; + } + + if (is_zero_ether_addr(s->data.new_peer_addr)) { + fst_printf_session(s, MSG_ERROR, "No new peer MAC address"); + return -EINVAL; + } + + if (!s->data.old_iface) { + fst_printf_session(s, MSG_ERROR, "No old interface defined"); + return -EINVAL; + } + + if (!s->data.new_iface) { + fst_printf_session(s, MSG_ERROR, "No new interface defined"); + return -EINVAL; + } + + if (s->data.new_iface == s->data.old_iface) { + fst_printf_session(s, MSG_ERROR, + "Same interface set as old and new"); + return -EINVAL; + } + + if (!fst_iface_is_connected(s->data.old_iface, s->data.old_peer_addr)) { + 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)) { + fst_printf_session(s, MSG_ERROR, + "The preset new peer address is not connected"); + return -EINVAL; + } + + _s = fst_find_session_in_progress(s->data.old_peer_addr, s->group); + if (_s) { + fst_printf_session(s, MSG_ERROR, + "There is another session in progress (old): %u", + _s->id); + return -EINVAL; + } + + _s = fst_find_session_in_progress(s->data.new_peer_addr, s->group); + if (_s) { + fst_printf_session(s, MSG_ERROR, + "There is another session in progress (new): %u", + _s->id); + return -EINVAL; + } + + dialog_token = fst_group_assign_dialog_token(s->group); + fsts_id = fst_group_assign_fsts_id(s->group); + + os_memset(&req, 0, sizeof(req)); + + fst_printf_siface(s, s->data.old_iface, MSG_INFO, + "initiating FST setup for %s (llt=%u ms)", + fst_iface_get_name(s->data.new_iface), s->data.llt_ms); + + req.action = FST_ACTION_SETUP_REQUEST; + req.dialog_token = dialog_token; + req.llt = host_to_le32(FST_LLT_MS_TO_VAL(s->data.llt_ms)); + /* 8.4.2.147 Session Transition element */ + req.stie.element_id = WLAN_EID_SESSION_TRANSITION; + req.stie.length = sizeof(req.stie) - 2; + req.stie.fsts_id = host_to_le32(fsts_id); + req.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0); + + req.stie.new_band_id = fst_iface_get_band_id(s->data.new_iface); + req.stie.new_band_op = 1; + req.stie.new_band_setup = 0; + + req.stie.old_band_id = fst_iface_get_band_id(s->data.old_iface); + req.stie.old_band_op = 1; + req.stie.old_band_setup = 0; + + res = fst_session_send_action(s, TRUE, &req, sizeof(req), + fst_iface_get_mbie(s->data.old_iface)); + if (!res) { + s->data.fsts_id = fsts_id; + s->data.pending_setup_req_dlgt = dialog_token; + fst_printf_sframe(s, TRUE, MSG_INFO, "FST Setup Request sent"); + fst_session_set_state(s, FST_SESSION_STATE_SETUP_COMPLETION, + NULL); + + fst_session_stt_arm(s); + } + + return res; +} + + +int fst_session_respond(struct fst_session *s, u8 status_code) +{ + struct fst_setup_res res; + enum hostapd_hw_mode hw_mode; + u8 channel; + + if (!fst_session_is_ready_pending(s)) { + fst_printf_session(s, MSG_ERROR, "incorrect state: %s", + fst_session_state_name(s->state)); + return -EINVAL; + } + + if (is_zero_ether_addr(s->data.old_peer_addr)) { + fst_printf_session(s, MSG_ERROR, "No peer MAC address"); + return -EINVAL; + } + + if (!s->data.old_iface) { + fst_printf_session(s, MSG_ERROR, "No old interface defined"); + return -EINVAL; + } + + if (!s->data.new_iface) { + fst_printf_session(s, MSG_ERROR, "No new interface defined"); + return -EINVAL; + } + + if (s->data.new_iface == s->data.old_iface) { + fst_printf_session(s, MSG_ERROR, + "Same interface set as old and new"); + return -EINVAL; + } + + if (!fst_iface_is_connected(s->data.old_iface, s->data.old_peer_addr)) { + fst_printf_session(s, MSG_ERROR, + "The preset peer address is not in the peer list"); + return -EINVAL; + } + + fst_session_stt_disarm(s); + + os_memset(&res, 0, sizeof(res)); + + res.action = FST_ACTION_SETUP_RESPONSE; + res.dialog_token = s->data.pending_setup_req_dlgt; + res.status_code = status_code; + + res.stie.element_id = WLAN_EID_SESSION_TRANSITION; + res.stie.length = sizeof(res.stie) - 2; + + if (status_code == WLAN_STATUS_SUCCESS) { + res.stie.fsts_id = 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, + &channel); + res.stie.new_band_id = fst_hw_mode_to_band(hw_mode); + res.stie.new_band_op = 1; + res.stie.new_band_setup = 0; + + fst_iface_get_channel_info(s->data.old_iface, &hw_mode, + &channel); + res.stie.old_band_id = fst_hw_mode_to_band(hw_mode); + res.stie.old_band_op = 1; + res.stie.old_band_setup = 0; + + fst_printf_session(s, MSG_INFO, + "%s: FST Setup Request accepted for %s (llt=%u)", + fst_iface_get_name(s->data.old_iface), + fst_iface_get_name(s->data.new_iface), + s->data.llt_ms); + } else { + fst_printf_session(s, MSG_WARNING, + "%s: FST Setup Request rejected with code %d", + fst_iface_get_name(s->data.old_iface), + status_code); + } + + if (fst_session_send_action(s, TRUE, &res, sizeof(res), + fst_iface_get_mbie(s->data.old_iface))) { + fst_printf_sframe(s, TRUE, MSG_ERROR, + "cannot send FST Setup Response with code %d", + status_code); + return -EINVAL; + } + + fst_printf_sframe(s, TRUE, MSG_INFO, "FST Setup Response sent"); + + if (status_code != WLAN_STATUS_SUCCESS) { + union fst_session_state_switch_extra evext = { + .to_initial = { + .reason = REASON_REJECT, + .reject_code = status_code, + .initiator = FST_INITIATOR_LOCAL, + }, + }; + fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); + } + + return 0; +} + + +int fst_session_initiate_switch(struct fst_session *s) +{ + struct fst_ack_req req; + int res; + u8 dialog_token; + + if (!fst_session_is_ready(s)) { + fst_printf_session(s, MSG_ERROR, + "cannot initiate switch due to wrong setup state (%d)", + s->state); + return -1; + } + + dialog_token = fst_group_assign_dialog_token(s->group); + + WPA_ASSERT(s->data.new_iface != NULL); + WPA_ASSERT(s->data.old_iface != NULL); + + fst_printf_session(s, MSG_INFO, "initiating FST switch: %s => %s", + fst_iface_get_name(s->data.old_iface), + fst_iface_get_name(s->data.new_iface)); + + os_memset(&req, 0, sizeof(req)); + + req.action = FST_ACTION_ACK_REQUEST; + req.dialog_token = dialog_token; + req.fsts_id = host_to_le32(s->data.fsts_id); + + res = fst_session_send_action(s, FALSE, &req, sizeof(req), NULL); + if (!res) { + fst_printf_sframe(s, FALSE, MSG_INFO, "FST Ack Request sent"); + fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_DONE, + NULL); + fst_session_stt_arm(s); + } else { + fst_printf_sframe(s, FALSE, MSG_ERROR, + "Cannot send FST Ack Request"); + } + + return res; +} + + +void fst_session_handle_action(struct fst_session *s, + struct fst_iface *iface, + const struct ieee80211_mgmt *mgmt, + size_t frame_len) +{ + switch (mgmt->u.action.u.fst_action.action) { + case FST_ACTION_SETUP_REQUEST: + WPA_ASSERT(0); + break; + case FST_ACTION_SETUP_RESPONSE: + fst_session_handle_setup_response(s, iface, mgmt, frame_len); + break; + case FST_ACTION_TEAR_DOWN: + fst_session_handle_tear_down(s, iface, mgmt, frame_len); + break; + case FST_ACTION_ACK_REQUEST: + fst_session_handle_ack_request(s, iface, mgmt, frame_len); + break; + case FST_ACTION_ACK_RESPONSE: + fst_session_handle_ack_response(s, iface, mgmt, frame_len); + break; + case FST_ACTION_ON_CHANNEL_TUNNEL: + default: + fst_printf_sframe(s, FALSE, MSG_ERROR, + "Unsupported FST Action frame"); + break; + } +} + + +int fst_session_tear_down_setup(struct fst_session *s) +{ + int res; + union fst_session_state_switch_extra evext = { + .to_initial = { + .reason = REASON_TEARDOWN, + .initiator = FST_INITIATOR_LOCAL, + }, + }; + + res = fst_session_send_tear_down(s); + + fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); + + return res; +} + + +void fst_session_reset(struct fst_session *s) +{ + fst_session_reset_ex(s, REASON_RESET); +} + + +void fst_session_delete(struct fst_session *s) +{ + fst_printf(MSG_INFO, "Session %u deleted", s->id); + dl_list_del(&s->global_sessions_lentry); + foreach_fst_ctrl_call(on_session_removed, s); + os_free(s); +} + + +struct fst_group * fst_session_get_group(struct fst_session *s) +{ + return s->group; +} + + +struct fst_iface * fst_session_get_iface(struct fst_session *s, Boolean is_old) +{ + return is_old ? s->data.old_iface : s->data.new_iface; +} + + +u32 fst_session_get_id(struct fst_session *s) +{ + return s->id; +} + + +const u8 * fst_session_get_peer_addr(struct fst_session *s, Boolean is_old) +{ + return is_old ? s->data.old_peer_addr : s->data.new_peer_addr; +} + + +u32 fst_session_get_llt(struct fst_session *s) +{ + return s->data.llt_ms; +} + + +enum fst_session_state fst_session_get_state(struct fst_session *s) +{ + return s->state; +} + + +struct fst_session * fst_session_get_by_id(u32 id) +{ + struct fst_session *s; + + foreach_fst_session(s) { + if (id == s->id) + return s; + } + + return NULL; +} + + +void fst_session_enum(struct fst_group *g, fst_session_enum_clb clb, void *ctx) +{ + struct fst_session *s; + + foreach_fst_session(s) { + if (!g || s->group == g) + clb(s->group, s, ctx); + } +} + + +void fst_session_on_action_rx(struct fst_iface *iface, + const struct ieee80211_mgmt *mgmt, + size_t len) +{ + struct fst_session *s; + + if (len < IEEE80211_HDRLEN + 2 || + mgmt->u.action.category != WLAN_ACTION_FST) { + fst_printf_iface(iface, MSG_ERROR, + "invalid Action frame received"); + return; + } + + if (mgmt->u.action.u.fst_action.action <= FST_ACTION_MAX_SUPPORTED) { + fst_printf_iface(iface, MSG_DEBUG, + "FST Action '%s' received!", + fst_action_names[mgmt->u.action.u.fst_action.action]); + } else { + fst_printf_iface(iface, MSG_WARNING, + "unknown FST Action (%u) received!", + mgmt->u.action.u.fst_action.action); + return; + } + + if (mgmt->u.action.u.fst_action.action == FST_ACTION_SETUP_REQUEST) { + fst_session_handle_setup_request(iface, mgmt, len); + return; + } + + s = fst_find_session_in_progress(mgmt->sa, fst_iface_get_group(iface)); + if (s) { + fst_session_handle_action(s, iface, mgmt, len); + } else { + fst_printf_iface(iface, MSG_WARNING, + "FST Action '%s' dropped: no session in progress found", + fst_action_names[mgmt->u.action.u.fst_action.action]); + } +} + + +int fst_session_set_str_ifname(struct fst_session *s, const char *ifname, + Boolean is_old) +{ + struct fst_group *g = fst_session_get_group(s); + struct fst_iface *i; + + i = fst_group_get_iface_by_name(g, ifname); + if (!i) { + fst_printf_session(s, MSG_WARNING, + "Cannot set iface %s: no such iface within group '%s'", + ifname, fst_group_get_id(g)); + return -1; + } + + fst_session_set_iface(s, i, is_old); + + return 0; +} + + +int fst_session_set_str_peer_addr(struct fst_session *s, const char *mac, + Boolean is_old) +{ + u8 peer_addr[ETH_ALEN]; + int res = fst_read_peer_addr(mac, peer_addr); + + if (res) + return res; + + fst_session_set_peer_addr(s, peer_addr, is_old); + + return 0; +} + + +int fst_session_set_str_llt(struct fst_session *s, const char *llt_str) +{ + char *endp; + long int llt = strtol(llt_str, &endp, 0); + + if (*endp || llt < 0 || (unsigned long int) llt > FST_MAX_LLT_MS) { + fst_printf_session(s, MSG_WARNING, + "Cannot set llt %s: Invalid llt value (1..%u expected)", + llt_str, FST_MAX_LLT_MS); + return -1; + } + fst_session_set_llt(s, (u32) llt); + + return 0; +} + + +void fst_session_global_on_iface_detached(struct fst_iface *iface) +{ + struct fst_session *s; + + foreach_fst_session(s) { + if (fst_session_is_in_progress(s) && + (s->data.new_iface == iface || + s->data.old_iface == iface)) + fst_session_reset_ex(s, REASON_DETACH_IFACE); + } +} + + +struct fst_session * fst_session_global_get_first_by_group(struct fst_group *g) +{ + struct fst_session *s; + + foreach_fst_session(s) { + if (s->group == g) + return s; + } + + return NULL; +} + + +#ifdef CONFIG_FST_TEST + +static int get_group_fill_session(struct fst_group **g, struct fst_session *s) +{ + const u8 *old_addr, *new_addr; + struct fst_get_peer_ctx *ctx; + + os_memset(s, 0, sizeof(*s)); + foreach_fst_group(*g) { + s->data.new_iface = fst_group_first_iface(*g); + if (s->data.new_iface) + break; + } + if (!s->data.new_iface) + return -EINVAL; + + s->data.old_iface = dl_list_entry(s->data.new_iface->group_lentry.next, + struct fst_iface, group_lentry); + if (!s->data.old_iface) + return -EINVAL; + + old_addr = fst_iface_get_peer_first(s->data.old_iface, &ctx, TRUE); + if (!old_addr) + return -EINVAL; + + new_addr = fst_iface_get_peer_first(s->data.new_iface, &ctx, TRUE); + if (!new_addr) + return -EINVAL; + + os_memcpy(s->data.old_peer_addr, old_addr, ETH_ALEN); + os_memcpy(s->data.new_peer_addr, new_addr, ETH_ALEN); + + return 0; +} + + +#define FST_MAX_COMMAND_WORD_NAME_LENGTH 16 + +int fst_test_req_send_fst_request(const char *params) +{ + int fsts_id; + Boolean is_valid; + char *endp; + struct fst_setup_req req; + struct fst_session s; + struct fst_group *g; + enum hostapd_hw_mode hw_mode; + u8 channel; + char additional_param[FST_MAX_COMMAND_WORD_NAME_LENGTH]; + + if (params[0] != ' ') + return -EINVAL; + params++; + fsts_id = fst_read_next_int_param(params, &is_valid, &endp); + if (!is_valid) + return -EINVAL; + + if (get_group_fill_session(&g, &s)) + return -EINVAL; + + req.action = FST_ACTION_SETUP_REQUEST; + req.dialog_token = g->dialog_token; + req.llt = host_to_le32(FST_LLT_MS_DEFAULT); + /* 8.4.2.147 Session Transition element */ + req.stie.element_id = WLAN_EID_SESSION_TRANSITION; + req.stie.length = sizeof(req.stie) - 2; + req.stie.fsts_id = host_to_le32(fsts_id); + req.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0); + + fst_iface_get_channel_info(s.data.new_iface, &hw_mode, &channel); + req.stie.new_band_id = fst_hw_mode_to_band(hw_mode); + req.stie.new_band_op = 1; + req.stie.new_band_setup = 0; + + fst_iface_get_channel_info(s.data.old_iface, &hw_mode, &channel); + req.stie.old_band_id = fst_hw_mode_to_band(hw_mode); + req.stie.old_band_op = 1; + req.stie.old_band_setup = 0; + + if (!fst_read_next_text_param(endp, additional_param, + sizeof(additional_param), &endp)) { + if (!os_strcasecmp(additional_param, FST_CTR_PVAL_BAD_NEW_BAND)) + req.stie.new_band_id = req.stie.old_band_id; + } + + return fst_session_send_action(&s, TRUE, &req, sizeof(req), + s.data.old_iface->mb_ie); +} + + +int fst_test_req_send_fst_response(const char *params) +{ + int fsts_id; + Boolean is_valid; + char *endp; + struct fst_setup_res res; + struct fst_session s; + struct fst_group *g; + enum hostapd_hw_mode hw_mode; + u8 status_code; + u8 channel; + char response[FST_MAX_COMMAND_WORD_NAME_LENGTH]; + struct fst_session *_s; + + if (params[0] != ' ') + return -EINVAL; + params++; + fsts_id = fst_read_next_int_param(params, &is_valid, &endp); + if (!is_valid) + return -EINVAL; + + if (get_group_fill_session(&g, &s)) + return -EINVAL; + + status_code = WLAN_STATUS_SUCCESS; + if (!fst_read_next_text_param(endp, response, sizeof(response), + &endp)) { + if (!os_strcasecmp(response, FST_CS_PVAL_RESPONSE_REJECT)) + status_code = WLAN_STATUS_PENDING_ADMITTING_FST_SESSION; + } + + os_memset(&res, 0, sizeof(res)); + + res.action = FST_ACTION_SETUP_RESPONSE; + /* + * If some session has just received an FST Setup Request, then + * use the correct dialog token copied from this request. + */ + _s = fst_find_session_in_progress(fst_session_get_peer_addr(&s, TRUE), + g); + res.dialog_token = (_s && fst_session_is_ready_pending(_s)) ? + _s->data.pending_setup_req_dlgt : g->dialog_token; + res.status_code = status_code; + + res.stie.element_id = WLAN_EID_SESSION_TRANSITION; + res.stie.length = sizeof(res.stie) - 2; + + if (res.status_code == WLAN_STATUS_SUCCESS) { + res.stie.fsts_id = fsts_id; + res.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0); + + fst_iface_get_channel_info(s.data.new_iface, &hw_mode, + &channel); + res.stie.new_band_id = fst_hw_mode_to_band(hw_mode); + res.stie.new_band_op = 1; + res.stie.new_band_setup = 0; + + fst_iface_get_channel_info(s.data.old_iface, &hw_mode, + &channel); + res.stie.old_band_id = fst_hw_mode_to_band(hw_mode); + res.stie.old_band_op = 1; + res.stie.old_band_setup = 0; + } + + if (!fst_read_next_text_param(endp, response, sizeof(response), + &endp)) { + if (!os_strcasecmp(response, FST_CTR_PVAL_BAD_NEW_BAND)) + res.stie.new_band_id = res.stie.old_band_id; + } + + return fst_session_send_action(&s, TRUE, &res, sizeof(res), + s.data.old_iface->mb_ie); +} + + +int fst_test_req_send_ack_request(const char *params) +{ + int fsts_id; + Boolean is_valid; + char *endp; + struct fst_ack_req req; + struct fst_session s; + struct fst_group *g; + + if (params[0] != ' ') + return -EINVAL; + params++; + fsts_id = fst_read_next_int_param(params, &is_valid, &endp); + if (!is_valid) + return -EINVAL; + + if (get_group_fill_session(&g, &s)) + return -EINVAL; + + os_memset(&req, 0, sizeof(req)); + req.action = FST_ACTION_ACK_REQUEST; + req.dialog_token = g->dialog_token; + req.fsts_id = fsts_id; + + return fst_session_send_action(&s, FALSE, &req, sizeof(req), NULL); +} + + +int fst_test_req_send_ack_response(const char *params) +{ + int fsts_id; + Boolean is_valid; + char *endp; + struct fst_ack_res res; + struct fst_session s; + struct fst_group *g; + + if (params[0] != ' ') + return -EINVAL; + params++; + fsts_id = fst_read_next_int_param(params, &is_valid, &endp); + if (!is_valid) + return -EINVAL; + + if (get_group_fill_session(&g, &s)) + return -EINVAL; + + os_memset(&res, 0, sizeof(res)); + res.action = FST_ACTION_ACK_RESPONSE; + res.dialog_token = g->dialog_token; + res.fsts_id = fsts_id; + + return fst_session_send_action(&s, FALSE, &res, sizeof(res), NULL); +} + + +int fst_test_req_send_tear_down(const char *params) +{ + int fsts_id; + Boolean is_valid; + char *endp; + struct fst_tear_down td; + struct fst_session s; + struct fst_group *g; + + if (params[0] != ' ') + return -EINVAL; + params++; + fsts_id = fst_read_next_int_param(params, &is_valid, &endp); + if (!is_valid) + return -EINVAL; + + if (get_group_fill_session(&g, &s)) + return -EINVAL; + + os_memset(&td, 0, sizeof(td)); + td.action = FST_ACTION_TEAR_DOWN; + td.fsts_id = fsts_id; + + return fst_session_send_action(&s, TRUE, &td, sizeof(td), NULL); +} + + +u32 fst_test_req_get_fsts_id(const char *params) +{ + int sid; + Boolean is_valid; + char *endp; + struct fst_session *s; + + if (params[0] != ' ') + return FST_FSTS_ID_NOT_FOUND; + params++; + sid = fst_read_next_int_param(params, &is_valid, &endp); + if (!is_valid) + return FST_FSTS_ID_NOT_FOUND; + + s = fst_session_get_by_id(sid); + if (!s) + return FST_FSTS_ID_NOT_FOUND; + + return s->data.fsts_id; +} + + +int fst_test_req_get_local_mbies(const char *request, char *buf, size_t buflen) +{ + char *endp; + char ifname[FST_MAX_COMMAND_WORD_NAME_LENGTH]; + struct fst_group *g; + struct fst_iface *iface; + + if (request[0] != ' ') + return -EINVAL; + request++; + if (fst_read_next_text_param(request, ifname, sizeof(ifname), &endp) || + !*ifname) + goto problem; + g = dl_list_first(&fst_global_groups_list, struct fst_group, + global_groups_lentry); + if (!g) + goto problem; + iface = fst_group_get_iface_by_name(g, ifname); + if (!iface || !iface->mb_ie) + goto problem; + return wpa_snprintf_hex(buf, buflen, wpabuf_head(iface->mb_ie), + wpabuf_len(iface->mb_ie)); + +problem: + return os_snprintf(buf, buflen, "FAIL\n"); +} + +#endif /* CONFIG_FST_TEST */ diff --git a/contrib/wpa/src/fst/fst_session.h b/contrib/wpa/src/fst/fst_session.h new file mode 100644 index 0000000..1162de4 --- /dev/null +++ b/contrib/wpa/src/fst/fst_session.h @@ -0,0 +1,80 @@ +/* + * FST module - FST Session related definitions + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef FST_SESSION_H +#define FST_SESSION_H + +#define FST_DEFAULT_SESSION_TIMEOUT_TU 255 /* u8 */ + +struct fst_iface; +struct fst_group; +struct fst_session; +enum fst_session_state; + +int fst_session_global_init(void); +void fst_session_global_deinit(void); +void fst_session_global_on_iface_detached(struct fst_iface *iface); +struct fst_session * +fst_session_global_get_first_by_group(struct fst_group *g); + +struct fst_session * fst_session_create(struct fst_group *g); +void fst_session_set_iface(struct fst_session *s, struct fst_iface *iface, + Boolean is_old); +void fst_session_set_llt(struct fst_session *s, u32 llt); +void fst_session_set_peer_addr(struct fst_session *s, const u8 *addr, + Boolean is_old); +int fst_session_initiate_setup(struct fst_session *s); +int fst_session_respond(struct fst_session *s, u8 status_code); +int fst_session_initiate_switch(struct fst_session *s); +void fst_session_handle_action(struct fst_session *s, struct fst_iface *iface, + const struct ieee80211_mgmt *mgmt, + size_t frame_len); +int fst_session_tear_down_setup(struct fst_session *s); +void fst_session_reset(struct fst_session *s); +void fst_session_delete(struct fst_session *s); + +struct fst_group * fst_session_get_group(struct fst_session *s); +struct fst_iface * fst_session_get_iface(struct fst_session *s, Boolean is_old); +const u8 * fst_session_get_peer_addr(struct fst_session *s, Boolean is_old); +u32 fst_session_get_id(struct fst_session *s); +u32 fst_session_get_llt(struct fst_session *s); +enum fst_session_state fst_session_get_state(struct fst_session *s); + +struct fst_session *fst_session_get_by_id(u32 id); + +typedef void (*fst_session_enum_clb)(struct fst_group *g, struct fst_session *s, + void *ctx); + +void fst_session_enum(struct fst_group *g, fst_session_enum_clb clb, void *ctx); + +void fst_session_on_action_rx(struct fst_iface *iface, + const struct ieee80211_mgmt *mgmt, size_t len); + + +int fst_session_set_str_ifname(struct fst_session *s, const char *ifname, + Boolean is_old); +int fst_session_set_str_peer_addr(struct fst_session *s, const char *mac, + Boolean is_old); +int fst_session_set_str_llt(struct fst_session *s, const char *llt_str); + +#ifdef CONFIG_FST_TEST + +#define FST_FSTS_ID_NOT_FOUND ((u32) -1) + +int fst_test_req_send_fst_request(const char *params); +int fst_test_req_send_fst_response(const char *params); +int fst_test_req_send_ack_request(const char *params); +int fst_test_req_send_ack_response(const char *params); +int fst_test_req_send_tear_down(const char *params); +u32 fst_test_req_get_fsts_id(const char *params); +int fst_test_req_get_local_mbies(const char *request, char *buf, + size_t buflen); + +#endif /* CONFIG_FST_TEST */ + +#endif /* FST_SESSION_H */ diff --git a/contrib/wpa/src/p2p/p2p.c b/contrib/wpa/src/p2p/p2p.c index 6adb3dc..767706c 100644 --- a/contrib/wpa/src/p2p/p2p.c +++ b/contrib/wpa/src/p2p/p2p.c @@ -48,9 +48,8 @@ static void p2p_scan_timeout(void *eloop_ctx, void *timeout_ctx); #define P2P_PEER_EXPIRATION_AGE 60 #endif /* P2P_PEER_EXPIRATION_AGE */ -#define P2P_PEER_EXPIRATION_INTERVAL (P2P_PEER_EXPIRATION_AGE / 2) -static void p2p_expire_peers(struct p2p_data *p2p) +void p2p_expire_peers(struct p2p_data *p2p) { struct p2p_device *dev, *n; struct os_reltime now; @@ -103,15 +102,6 @@ static void p2p_expire_peers(struct p2p_data *p2p) } -static void p2p_expiration_timeout(void *eloop_ctx, void *timeout_ctx) -{ - struct p2p_data *p2p = eloop_ctx; - p2p_expire_peers(p2p); - eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0, - p2p_expiration_timeout, p2p, NULL); -} - - static const char * p2p_state_txt(int state) { switch (state) { @@ -297,7 +287,7 @@ static void p2p_listen_in_find(struct p2p_data *p2p, int dev_disc) return; } - ies = p2p_build_probe_resp_ies(p2p); + ies = p2p_build_probe_resp_ies(p2p, NULL, 0); if (ies == NULL) return; @@ -346,7 +336,7 @@ int p2p_listen(struct p2p_data *p2p, unsigned int timeout) return 0; } - ies = p2p_build_probe_resp_ies(p2p); + ies = p2p_build_probe_resp_ies(p2p, NULL, 0); if (ies == NULL) return -1; @@ -468,7 +458,8 @@ static void p2p_copy_client_info(struct p2p_device *dev, static int p2p_add_group_clients(struct p2p_data *p2p, const u8 *go_dev_addr, const u8 *go_interface_addr, int freq, - const u8 *gi, size_t gi_len) + const u8 *gi, size_t gi_len, + struct os_reltime *rx_time) { struct p2p_group_info info; size_t c; @@ -536,10 +527,11 @@ static int p2p_add_group_clients(struct p2p_data *p2p, const u8 *go_dev_addr, os_memcpy(dev->interface_addr, cli->p2p_interface_addr, ETH_ALEN); - os_get_reltime(&dev->last_seen); + os_memcpy(&dev->last_seen, rx_time, sizeof(struct os_reltime)); os_memcpy(dev->member_in_go_dev, go_dev_addr, ETH_ALEN); os_memcpy(dev->member_in_go_iface, go_interface_addr, ETH_ALEN); + dev->flags |= P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT; } return 0; @@ -758,26 +750,35 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, /* * Update the device entry only if the new peer - * entry is newer than the one previously stored. + * entry is newer than the one previously stored, or if + * the device was previously seen as a P2P Client in a group + * and the new entry isn't older than a threshold. */ if (dev->last_seen.sec > 0 && - os_reltime_before(rx_time, &dev->last_seen)) { - p2p_dbg(p2p, "Do not update peer entry based on old frame (rx_time=%u.%06u last_seen=%u.%06u)", + os_reltime_before(rx_time, &dev->last_seen) && + (!(dev->flags & P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT) || + os_reltime_expired(&dev->last_seen, rx_time, + P2P_DEV_GROUP_CLIENT_RESP_THRESHOLD))) { + p2p_dbg(p2p, + "Do not update peer entry based on old frame (rx_time=%u.%06u last_seen=%u.%06u flags=0x%x)", (unsigned int) rx_time->sec, (unsigned int) rx_time->usec, (unsigned int) dev->last_seen.sec, - (unsigned int) dev->last_seen.usec); + (unsigned int) dev->last_seen.usec, + dev->flags); p2p_parse_free(&msg); return -1; } os_memcpy(&dev->last_seen, rx_time, sizeof(struct os_reltime)); - dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY); + dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY | + P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT); if (os_memcmp(addr, p2p_dev_addr, ETH_ALEN) != 0) os_memcpy(dev->interface_addr, addr, ETH_ALEN); if (msg.ssid && + msg.ssid[1] <= sizeof(dev->oper_ssid) && (msg.ssid[1] != P2P_WILDCARD_SSID_LEN || os_memcmp(msg.ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) != 0)) { @@ -843,7 +844,8 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, if (scan_res) { p2p_add_group_clients(p2p, p2p_dev_addr, addr, freq, - msg.group_info, msg.group_info_len); + msg.group_info, msg.group_info_len, + rx_time); } p2p_parse_free(&msg); @@ -1127,8 +1129,10 @@ static int p2ps_gen_hash(struct p2p_data *p2p, const char *str, u8 *hash) adv_array = (u8 *) str_buf; adv_len = os_strlen(str); + if (adv_len >= sizeof(str_buf)) + return 0; - for (i = 0; str[i] && i < adv_len; i++) { + for (i = 0; i < adv_len; i++) { if (str[i] >= 'A' && str[i] <= 'Z') str_buf[i] = str[i] - 'A' + 'a'; else @@ -1182,27 +1186,25 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, * An empty seek string means no hash values, but still an ASP * search. */ + p2p_dbg(p2p, "ASP search"); p2p->p2ps_seek_count = 0; p2p->p2ps_seek = 1; } else if (seek && seek_count <= P2P_MAX_QUERY_HASH) { u8 buf[P2PS_HASH_LEN]; - int i; + int i, count = 0; - p2p->p2ps_seek_count = seek_count; for (i = 0; i < seek_count; i++) { if (!p2ps_gen_hash(p2p, seek[i], buf)) continue; - /* If asking for wildcard, don't do others */ - if (os_memcmp(buf, p2p->wild_card_hash, - P2PS_HASH_LEN) == 0) { - p2p->p2ps_seek_count = 0; - break; - } - - os_memcpy(&p2p->query_hash[i * P2PS_HASH_LEN], buf, - P2PS_HASH_LEN); + p2p_dbg(p2p, "Seek service %s hash " MACSTR, + seek[i], MAC2STR(buf)); + os_memcpy(&p2p->p2ps_seek_hash[count * P2PS_HASH_LEN], + buf, P2PS_HASH_LEN); + count++; } + + p2p->p2ps_seek_count = count; p2p->p2ps_seek = 1; } else { p2p->p2ps_seek_count = 0; @@ -1212,7 +1214,8 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, /* Special case to perform wildcard search */ if (p2p->p2ps_seek_count == 0 && p2p->p2ps_seek) { p2p->p2ps_seek_count = 1; - os_memcpy(&p2p->query_hash, p2p->wild_card_hash, P2PS_HASH_LEN); + os_memcpy(&p2p->p2ps_seek_hash, p2p->wild_card_hash, + P2PS_HASH_LEN); } p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING; @@ -1380,7 +1383,7 @@ static int p2p_prepare_channel_pref(struct p2p_data *p2p, static void p2p_prepare_channel_best(struct p2p_data *p2p) { u8 op_class, op_channel; - const int op_classes_5ghz[] = { 124, 115, 0 }; + 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 }; @@ -2147,7 +2150,9 @@ int p2p_match_dev_type(struct p2p_data *p2p, struct wpabuf *wps) } -struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p) +struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p, + const u8 *query_hash, + u8 query_count) { struct wpabuf *buf; u8 *len; @@ -2162,7 +2167,7 @@ struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p) if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P]) extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P]); - if (p2p->query_count) + if (query_count) extra += MAX_SVC_ADV_IE_LEN; buf = wpabuf_alloc(1000 + extra); @@ -2199,9 +2204,8 @@ struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p) p2p_buf_add_device_info(buf, p2p, NULL); p2p_buf_update_ie_hdr(buf, len); - if (p2p->query_count) { - p2p_buf_add_service_instance(buf, p2p, p2p->query_count, - p2p->query_hash, + if (query_count) { + p2p_buf_add_service_instance(buf, p2p, query_count, query_hash, p2p->p2ps_adv_list); } @@ -2212,18 +2216,21 @@ struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p) static int p2p_service_find_asp(struct p2p_data *p2p, const u8 *hash) { struct p2ps_advertisement *adv_data; + int any_wfa; p2p_dbg(p2p, "ASP find - ASP list: %p", p2p->p2ps_adv_list); - /* Wildcard always matches if we have actual services */ - if (os_memcmp(hash, p2p->wild_card_hash, P2PS_HASH_LEN) == 0) - return p2p->p2ps_adv_list != NULL; + /* Wildcard org.wi-fi.wfds matches any WFA spec defined service */ + any_wfa = os_memcmp(hash, p2p->wild_card_hash, P2PS_HASH_LEN) == 0; adv_data = p2p->p2ps_adv_list; while (adv_data) { - p2p_dbg(p2p, "ASP hash: %x =? %x", hash[0], adv_data->hash[0]); if (os_memcmp(hash, adv_data->hash, P2PS_HASH_LEN) == 0) - return 1; + return 1; /* exact hash match */ + if (any_wfa && + os_strncmp(adv_data->svc_name, P2PS_WILD_HASH_STR, + os_strlen(P2PS_WILD_HASH_STR)) == 0) + return 1; /* WFA service match */ adv_data = adv_data->next; } @@ -2233,13 +2240,15 @@ static int p2p_service_find_asp(struct p2p_data *p2p, const u8 *hash) static enum p2p_probe_req_status p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, - const u8 *bssid, const u8 *ie, size_t ie_len) + const u8 *bssid, const u8 *ie, size_t ie_len, + unsigned int rx_freq) { 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) { @@ -2291,53 +2300,42 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, return P2P_PREQ_NOT_P2P; } - p2p->p2ps_svc_found = 0; - if (msg.service_hash && msg.service_hash_count) { const u8 *hash = msg.service_hash; - u8 *dest = p2p->query_hash; u8 i; + int p2ps_svc_found = 0; + + p2p_dbg(p2p, "in_listen=%d drv_in_listen=%d when received P2PS Probe Request at %u MHz; own Listen channel %u, pending listen freq %u MHz", + p2p->in_listen, p2p->drv_in_listen, rx_freq, + p2p->cfg->channel, p2p->pending_listen_freq); + + if (!p2p->in_listen && !p2p->drv_in_listen && + p2p->pending_listen_freq && rx_freq && + rx_freq != p2p->pending_listen_freq) { + p2p_dbg(p2p, "Do not reply to Probe Request frame that was received on %u MHz while waiting to start Listen state on %u MHz", + rx_freq, p2p->pending_listen_freq); + p2p_parse_free(&msg); + return P2P_PREQ_NOT_LISTEN; + } - p2p->query_count = 0; for (i = 0; i < msg.service_hash_count; i++) { if (p2p_service_find_asp(p2p, hash)) { - p2p->p2ps_svc_found = 1; - - if (!os_memcmp(hash, p2p->wild_card_hash, - P2PS_HASH_LEN)) { - /* We found match(es) but wildcard - * will return all */ - p2p->query_count = 1; - os_memcpy(p2p->query_hash, hash, - P2PS_HASH_LEN); - break; - } - - /* Save each matching hash */ - if (p2p->query_count < P2P_MAX_QUERY_HASH) { - os_memcpy(dest, hash, P2PS_HASH_LEN); - dest += P2PS_HASH_LEN; - p2p->query_count++; - } else { - /* We found match(es) but too many to - * return all */ - p2p->query_count = 0; - break; - } + p2p_dbg(p2p, "Service Hash match found: " + MACSTR, MAC2STR(hash)); + p2ps_svc_found = 1; + break; } hash += P2PS_HASH_LEN; } - p2p_dbg(p2p, "ASP adv found: %d", p2p->p2ps_svc_found); - /* Probed hash unknown */ - if (!p2p->p2ps_svc_found) { + if (!p2ps_svc_found) { + p2p_dbg(p2p, "No Service Hash match found"); p2p_parse_free(&msg); return P2P_PREQ_NOT_PROCESSED; } } else { /* This is not a P2PS Probe Request */ - p2p->query_count = 0; p2p_dbg(p2p, "No P2PS Hash in Probe Request"); if (!p2p->in_listen || !p2p->drv_in_listen) { @@ -2366,11 +2364,11 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, p2p_parse_free(&msg); return P2P_PREQ_NOT_PROCESSED; } - p2p_parse_free(&msg); if (!p2p->cfg->send_probe_resp) { /* Response generated elsewhere */ p2p_dbg(p2p, "Probe Resp generated elsewhere - do not generate additional response"); + p2p_parse_free(&msg); return P2P_PREQ_NOT_PROCESSED; } @@ -2382,7 +2380,9 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, * really only used for discovery purposes, not to learn exact BSS * parameters. */ - ies = p2p_build_probe_resp_ies(p2p); + ies = p2p_build_probe_resp_ies(p2p, msg.service_hash, + msg.service_hash_count); + p2p_parse_free(&msg); if (ies == NULL) return P2P_PREQ_NOT_PROCESSED; @@ -2392,8 +2392,8 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, return P2P_PREQ_NOT_PROCESSED; } - resp = NULL; - resp = wpabuf_put(buf, resp->u.probe_resp.variable - (u8 *) resp); + 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)); @@ -2422,32 +2422,50 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, 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)) { + 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, p2p->cfg->channel); + wpabuf_put_u8(buf, channel); wpabuf_put_buf(buf, ies); wpabuf_free(ies); - p2p->cfg->send_probe_resp(p2p->cfg->cb_ctx, buf); + p2p->cfg->send_probe_resp(p2p->cfg->cb_ctx, buf, rx_freq); wpabuf_free(buf); - return P2P_PREQ_NOT_PROCESSED; + return P2P_PREQ_PROCESSED; } 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) + const u8 *bssid, const u8 *ie, size_t ie_len, + unsigned int rx_freq) { enum p2p_probe_req_status res; p2p_add_dev_from_probe_req(p2p, addr, ie, ie_len); - res = p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len); - p2p->query_count = 0; + 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; + /* + * Activate a pending GO Negotiation/Invite flow if a received Probe + * Request frame is from an expected peer. Some devices may share the + * same address for P2P and non-P2P STA running simultaneously. The + * P2P_PREQ_PROCESSED and P2P_PREQ_NOT_PROCESSED p2p_reply_probe() + * return values verified above ensure we are handling a Probe Request + * frame from a P2P peer. + */ if ((p2p->state == P2P_CONNECT || p2p->state == P2P_CONNECT_LISTEN) && p2p->go_neg_peer && os_memcmp(addr, p2p->go_neg_peer->info.p2p_device_addr, ETH_ALEN) @@ -2457,7 +2475,7 @@ p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, p2p_dbg(p2p, "Found GO Negotiation peer - try to start GO negotiation from timeout"); eloop_cancel_timeout(p2p_go_neg_start, p2p, NULL); eloop_register_timeout(0, 0, p2p_go_neg_start, p2p, NULL); - return P2P_PREQ_PROCESSED; + return res; } if ((p2p->state == P2P_INVITE || p2p->state == P2P_INVITE_LISTEN) && @@ -2469,7 +2487,7 @@ p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, p2p_dbg(p2p, "Found Invite peer - try to start Invite from timeout"); eloop_cancel_timeout(p2p_invite_start, p2p, NULL); eloop_register_timeout(0, 0, p2p_invite_start, p2p, NULL); - return P2P_PREQ_PROCESSED; + return res; } return res; @@ -2484,10 +2502,21 @@ static int p2p_assoc_req_ie_wlan_ap(struct p2p_data *p2p, const u8 *bssid, size_t tmplen; int res; u8 group_capab; + struct p2p_message msg; if (p2p_ie == NULL) return 0; /* WLAN AP is not a P2P manager */ + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_p2p_ie(p2p_ie, &msg) < 0) + return 0; + + p2p_dbg(p2p, "BSS P2P manageability %s", + msg.manageability ? "enabled" : "disabled"); + + if (!msg.manageability) + return 0; + /* * (Re)Association Request - P2P IE * P2P Capability attribute (shall be present) @@ -2650,13 +2679,14 @@ int p2p_service_del_asp(struct p2p_data *p2p, u32 adv_id) int p2p_service_add_asp(struct p2p_data *p2p, int auto_accept, u32 adv_id, const char *adv_str, u8 svc_state, u16 config_methods, - const char *svc_info) + const char *svc_info, const u8 *cpt_priority) { struct p2ps_advertisement *adv_data, *tmp, **prev; u8 buf[P2PS_HASH_LEN]; size_t adv_data_len, adv_len, info_len = 0; + int i; - if (!p2p || !adv_str || !adv_str[0]) + if (!p2p || !adv_str || !adv_str[0] || !cpt_priority) return -1; if (!(config_methods & p2p->cfg->config_methods)) { @@ -2685,6 +2715,11 @@ int p2p_service_add_asp(struct p2p_data *p2p, int auto_accept, u32 adv_id, adv_data->auto_accept = (u8) auto_accept; os_memcpy(adv_data->svc_name, adv_str, adv_len); + for (i = 0; cpt_priority[i] && i < P2PS_FEATURE_CAPAB_CPT_MAX; i++) { + adv_data->cpt_priority[i] = cpt_priority[i]; + adv_data->cpt_mask |= cpt_priority[i]; + } + if (svc_info && info_len) { adv_data->svc_info = &adv_data->svc_name[adv_len + 1]; os_memcpy(adv_data->svc_info, svc_info, info_len); @@ -2723,13 +2758,33 @@ int p2p_service_add_asp(struct p2p_data *p2p, int auto_accept, u32 adv_id, inserted: p2p_dbg(p2p, - "Added ASP advertisement adv_id=0x%x config_methods=0x%x svc_state=0x%x adv_str='%s'", - adv_id, adv_data->config_methods, svc_state, adv_str); + "Added ASP advertisement adv_id=0x%x config_methods=0x%x svc_state=0x%x adv_str='%s' cpt_mask=0x%x", + adv_id, adv_data->config_methods, svc_state, adv_str, + adv_data->cpt_mask); return 0; } +void p2p_service_flush_asp(struct p2p_data *p2p) +{ + struct p2ps_advertisement *adv, *prev; + + if (!p2p) + return; + + adv = p2p->p2ps_adv_list; + while (adv) { + prev = adv; + adv = adv->next; + os_free(prev); + } + + p2p->p2ps_adv_list = NULL; + p2p_dbg(p2p, "All ASP advertisements flushed"); +} + + int p2p_parse_dev_addr_in_p2p_ie(struct wpabuf *p2p_ie, u8 *dev_addr) { struct p2p_message msg; @@ -2861,9 +2916,6 @@ struct p2p_data * p2p_init(const struct p2p_config *cfg) dl_list_init(&p2p->devices); - eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0, - p2p_expiration_timeout, p2p, NULL); - p2p->go_timeout = 100; p2p->client_timeout = 20; p2p->num_p2p_sd_queries = 0; @@ -2878,8 +2930,6 @@ struct p2p_data * p2p_init(const struct p2p_config *cfg) void p2p_deinit(struct p2p_data *p2p) { - struct p2ps_advertisement *adv, *prev; - #ifdef CONFIG_WIFI_DISPLAY wpabuf_free(p2p->wfd_ie_beacon); wpabuf_free(p2p->wfd_ie_probe_req); @@ -2894,7 +2944,6 @@ void p2p_deinit(struct p2p_data *p2p) wpabuf_free(p2p->wfd_coupled_sink_info); #endif /* CONFIG_WIFI_DISPLAY */ - eloop_cancel_timeout(p2p_expiration_timeout, p2p, NULL); 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); @@ -2908,18 +2957,12 @@ void p2p_deinit(struct p2p_data *p2p) os_free(p2p->cfg->serial_number); os_free(p2p->cfg->pref_chan); os_free(p2p->groups); - os_free(p2p->p2ps_prov); + 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); - - adv = p2p->p2ps_adv_list; - while (adv) { - prev = adv; - adv = adv->next; - os_free(prev); - } + p2p_service_flush_asp(p2p); os_free(p2p); } @@ -2937,6 +2980,7 @@ void p2p_flush(struct p2p_data *p2p) p2p_free_sd_queries(p2p); os_free(p2p->after_scan_tx); p2p->after_scan_tx = NULL; + p2p->ssid_set = 0; } @@ -4120,7 +4164,7 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info, "country=%c%c\n" "oper_freq=%d\n" "req_config_methods=0x%x\n" - "flags=%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n" + "flags=%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n" "status=%d\n" "invitation_reqs=%u\n", (int) (now.sec - dev->last_seen.sec), @@ -4164,6 +4208,8 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info, "[FORCE_FREQ]" : "", dev->flags & P2P_DEV_PD_FOR_JOIN ? "[PD_FOR_JOIN]" : "", + dev->flags & P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT ? + "[LAST_SEEN_AS_GROUP_CLIENT]" : "", dev->status, dev->invitation_reqs); if (os_snprintf_error(end - pos, res)) @@ -5215,6 +5261,7 @@ int p2p_process_nfc_connection_handover(struct p2p_data *p2p, if (!msg.oob_go_neg_channel) { p2p_dbg(p2p, "OOB GO Negotiation Channel attribute not included"); + p2p_parse_free(&msg); return -1; } @@ -5226,6 +5273,7 @@ int p2p_process_nfc_connection_handover(struct p2p_data *p2p, msg.oob_go_neg_channel[4]); if (freq < 0) { p2p_dbg(p2p, "Unknown peer OOB GO Neg channel"); + p2p_parse_free(&msg); return -1; } role = msg.oob_go_neg_channel[5]; @@ -5246,6 +5294,7 @@ int p2p_process_nfc_connection_handover(struct p2p_data *p2p, p2p->cfg->channel); if (freq < 0) { p2p_dbg(p2p, "Own listen channel not known"); + p2p_parse_free(&msg); return -1; } p2p_dbg(p2p, "Use own Listen channel as OOB GO Neg channel: %u MHz", freq); @@ -5334,3 +5383,20 @@ void p2p_go_neg_wait_timeout(void *eloop_ctx, void *timeout_ctx) "Timeout on waiting peer to become ready for GO Negotiation"); p2p_go_neg_failed(p2p, -1); } + + +void p2p_set_own_pref_freq_list(struct p2p_data *p2p, + const unsigned int *pref_freq_list, + unsigned int size) +{ + unsigned int i; + + if (size > P2P_MAX_PREF_CHANNELS) + size = P2P_MAX_PREF_CHANNELS; + p2p->num_pref_freq = size; + for (i = 0; i < size; i++) { + p2p->pref_freq_list[i] = pref_freq_list[i]; + p2p_dbg(p2p, "Own preferred frequency list[%u]=%u MHz", + i, p2p->pref_freq_list[i]); + } +} diff --git a/contrib/wpa/src/p2p/p2p.h b/contrib/wpa/src/p2p/p2p.h index 2402db6..b4060be 100644 --- a/contrib/wpa/src/p2p/p2p.h +++ b/contrib/wpa/src/p2p/p2p.h @@ -9,7 +9,8 @@ #ifndef P2P_H #define P2P_H -#include "wps/wps_defs.h" +#include "common/ieee802_11_defs.h" +#include "wps/wps.h" /* P2P ASP Setup Capability */ #define P2PS_SETUP_NONE 0 @@ -20,6 +21,12 @@ #define P2PS_WILD_HASH_STR "org.wi-fi.wfds" #define P2PS_HASH_LEN 6 #define P2P_MAX_QUERY_HASH 6 +#define P2PS_FEATURE_CAPAB_CPT_MAX 2 + +/** + * P2P_MAX_PREF_CHANNELS - Maximum number of preferred channels + */ +#define P2P_MAX_PREF_CHANNELS 100 /** * P2P_MAX_REG_CLASSES - Maximum number of regulatory classes @@ -95,7 +102,7 @@ struct p2p_go_neg_results { /** * ssid - SSID of the group */ - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; /** * ssid_len - Length of SSID in octets @@ -155,6 +162,11 @@ struct p2p_go_neg_results { struct p2ps_provision { /** + * pd_seeker - P2PS provision discovery seeker role + */ + unsigned int pd_seeker:1; + + /** * status - Remote returned provisioning status code */ int status; @@ -195,6 +207,23 @@ struct p2ps_provision { u8 adv_mac[ETH_ALEN]; /** + * cpt_mask - Supported Coordination Protocol Transport mask + * + * A bitwise mask of supported ASP Coordination Protocol Transports. + * This property is set together and corresponds with cpt_priority. + */ + u8 cpt_mask; + + /** + * cpt_priority - Coordination Protocol Transport priority list + * + * Priorities of supported ASP Coordination Protocol Transports. + * This property is set together and corresponds with cpt_mask. + * The CPT priority list is 0 terminated. + */ + u8 cpt_priority[P2PS_FEATURE_CAPAB_CPT_MAX + 1]; + + /** * info - Vendor defined extra Provisioning information */ char info[0]; @@ -234,6 +263,23 @@ struct p2ps_advertisement { u8 hash[P2PS_HASH_LEN]; /** + * cpt_mask - supported Coordination Protocol Transport mask + * + * A bitwise mask of supported ASP Coordination Protocol Transports. + * This property is set together and corresponds with cpt_priority. + */ + u8 cpt_mask; + + /** + * cpt_priority - Coordination Protocol Transport priority list + * + * Priorities of supported ASP Coordinatin Protocol Transports. + * This property is set together and corresponds with cpt_mask. + * The CPT priority list is 0 terminated. + */ + u8 cpt_priority[P2PS_FEATURE_CAPAB_CPT_MAX + 1]; + + /** * svc_name - NULL Terminated UTF-8 Service Name, and svc_info storage */ char svc_name[0]; @@ -268,27 +314,27 @@ struct p2p_peer_info { /** * device_name - Device Name (0..32 octets encoded in UTF-8) */ - char device_name[33]; + char device_name[WPS_DEV_NAME_MAX_LEN + 1]; /** * manufacturer - Manufacturer (0..64 octets encoded in UTF-8) */ - char manufacturer[65]; + char manufacturer[WPS_MANUFACTURER_MAX_LEN + 1]; /** * model_name - Model Name (0..32 octets encoded in UTF-8) */ - char model_name[33]; + char model_name[WPS_MODEL_NAME_MAX_LEN + 1]; /** * model_number - Model Number (0..32 octets encoded in UTF-8) */ - char model_number[33]; + char model_number[WPS_MODEL_NUMBER_MAX_LEN + 1]; /** * serial_number - Serial Number (0..32 octets encoded in UTF-8) */ - char serial_number[33]; + char serial_number[WPS_SERIAL_NUMBER_MAX_LEN + 1]; /** * level - Signal level @@ -316,7 +362,7 @@ struct p2p_peer_info { * This list includes from 0 to 16 Secondary Device Types as indicated * by wps_sec_dev_type_list_len (8 * number of types). */ - u8 wps_sec_dev_type_list[128]; + u8 wps_sec_dev_type_list[WPS_SEC_DEV_TYPE_MAX_LEN]; /** * wps_sec_dev_type_list_len - Length of secondary device type list @@ -495,7 +541,7 @@ struct p2p_config { * This data will be added to the end of the SSID after the * DIRECT-<random two octets> prefix. */ - u8 ssid_postfix[32 - 9]; + u8 ssid_postfix[SSID_MAX_LEN - 9]; /** * ssid_postfix_len - Length of the ssid_postfix data @@ -569,12 +615,14 @@ struct p2p_config { * send_probe_resp - Transmit a Probe Response frame * @ctx: Callback context from cb_ctx * @buf: Probe Response frame (including the header and body) + * @freq: Forced frequency (in MHz) to use or 0. * Returns: 0 on success, -1 on failure * * This function is used to reply to Probe Request frames that were * indicated with a call to p2p_probe_req_rx(). The response is to be - * sent on the same channel or to be dropped if the driver is not - * anymore listening to Probe Request frames. + * sent on the same channel, unless otherwise specified, or to be + * dropped if the driver is not listening to Probe Request frames + * anymore. * * Alternatively, the responsibility for building the Probe Response * frames in Listen state may be in another system component in which @@ -585,7 +633,8 @@ struct p2p_config { * Request frames must be indicated by calling p2p_probe_req_rx() even * if this send_probe_resp() is not used. */ - int (*send_probe_resp)(void *ctx, const struct wpabuf *buf); + int (*send_probe_resp)(void *ctx, const struct wpabuf *buf, + unsigned int freq); /** * send_action - Transmit an Action frame @@ -704,6 +753,7 @@ struct p2p_config { * @ctx: Callback context from cb_ctx * @src: Source address of the message triggering this notification * @dev_passwd_id: WPS Device Password ID + * @go_intent: Peer's GO Intent * * This callback is used to notify that a P2P Device is requesting * group owner negotiation with us, but we do not have all the @@ -712,7 +762,8 @@ struct p2p_config { * PIN or PBC button press. This information can be provided with a * call to p2p_connect(). */ - void (*go_neg_req_rx)(void *ctx, const u8 *src, u16 dev_passwd_id); + void (*go_neg_req_rx)(void *ctx, const u8 *src, u16 dev_passwd_id, + u8 go_intent); /** * go_neg_completed - Notification of GO Negotiation results @@ -949,18 +1000,21 @@ struct p2p_config { /** * Determine if we have a persistent group we share with remote peer + * and allocate interface for this group if needed * @ctx: Callback context from cb_ctx * @addr: Peer device address to search for * @ssid: Persistent group SSID or %NULL if any * @ssid_len: Length of @ssid - * @go_dev_addr: Buffer for returning intended GO P2P Device Address + * @go_dev_addr: Buffer for returning GO P2P Device Address * @ret_ssid: Buffer for returning group SSID * @ret_ssid_len: Buffer for returning length of @ssid + * @intended_iface_addr: Buffer for returning intended iface address * Returns: 1 if a matching persistent group was found, 0 otherwise */ int (*get_persistent_group)(void *ctx, const u8 *addr, const u8 *ssid, size_t ssid_len, u8 *go_dev_addr, - u8 *ret_ssid, size_t *ret_ssid_len); + u8 *ret_ssid, size_t *ret_ssid_len, + u8 *intended_iface_addr); /** * Get information about a possible local GO role @@ -1001,7 +1055,8 @@ struct p2p_config { u8 conncap, int passwd_id, const u8 *persist_ssid, size_t persist_ssid_size, int response_done, - int prov_start, const char *session_info); + int prov_start, const char *session_info, + const u8 *feat_cap, size_t feat_cap_len); /** * prov_disc_resp_cb - Callback for indicating completion of PD Response @@ -1023,6 +1078,20 @@ struct p2p_config { * P2PS_SETUP_* bitmap is used as the parameters and return value. */ u8 (*p2ps_group_capability)(void *ctx, u8 incoming, u8 role); + + /** + * get_pref_freq_list - Get preferred frequency list for an interface + * @ctx: Callback context from cb_ctx + * @go: Whether the use if for GO role + * @len: Length of freq_list in entries (both IN and OUT) + * @freq_list: Buffer for returning the preferred frequencies (MHz) + * Returns: 0 on success, -1 on failure + * + * This function can be used to query the preferred frequency list from + * the driver specific to a particular interface type. + */ + int (*get_pref_freq_list)(void *ctx, int go, + unsigned int *len, unsigned int *freq_list); }; @@ -1460,11 +1529,13 @@ enum p2p_probe_req_status { * @bssid: BSSID if available or %NULL * @ie: Information elements from the Probe Request frame body * @ie_len: Length of ie buffer in octets + * @rx_freq: Probe Request frame RX frequency * 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); + const u8 *bssid, const u8 *ie, size_t ie_len, + unsigned int rx_freq); /** * p2p_rx_action - Report received Action frame @@ -1607,7 +1678,7 @@ struct p2p_group_config { /** * ssid - Group SSID */ - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; /** * ssid_len - Length of SSID @@ -2214,7 +2285,7 @@ struct p2p_nfc_params { size_t oob_dev_pw_len; int go_freq; u8 go_dev_addr[ETH_ALEN]; - u8 go_ssid[32]; + u8 go_ssid[SSID_MAX_LEN]; size_t go_ssid_len; }; @@ -2240,8 +2311,33 @@ struct p2ps_advertisement * p2p_service_p2ps_id(struct p2p_data *p2p, u32 adv_id); int p2p_service_add_asp(struct p2p_data *p2p, int auto_accept, u32 adv_id, const char *adv_str, u8 svc_state, - u16 config_methods, const char *svc_info); + u16 config_methods, const char *svc_info, + const u8 *cpt_priority); int p2p_service_del_asp(struct p2p_data *p2p, u32 adv_id); +void p2p_service_flush_asp(struct p2p_data *p2p); struct p2ps_advertisement * p2p_get_p2ps_adv_list(struct p2p_data *p2p); +/** + * p2p_expire_peers - Periodic cleanup function to expire peers + * @p2p: P2P module context from p2p_init() + * + * This is a cleanup function that the entity calling p2p_init() is + * expected to call periodically to clean up expired peer entries. + */ +void p2p_expire_peers(struct p2p_data *p2p); + +void p2p_set_own_pref_freq_list(struct p2p_data *p2p, + const unsigned int *pref_freq_list, + unsigned int size); + +/** + * p2p_group_get_common_freqs - Get the group common frequencies + * @group: P2P group context from p2p_group_init() + * @common_freqs: On return will hold the group common frequencies + * @num: On return will hold the number of group common frequencies + * Returns: 0 on success, -1 otherwise + */ +int p2p_group_get_common_freqs(struct p2p_group *group, int *common_freqs, + unsigned int *num); + #endif /* P2P_H */ diff --git a/contrib/wpa/src/p2p/p2p_build.c b/contrib/wpa/src/p2p/p2p_build.c index 92c92066..793d28b 100644 --- a/contrib/wpa/src/p2p/p2p_build.c +++ b/contrib/wpa/src/p2p/p2p_build.c @@ -10,6 +10,7 @@ #include "common.h" #include "common/ieee802_11_defs.h" +#include "common/qca-vendor.h" #include "wps/wps_i.h" #include "p2p_i.h" @@ -109,6 +110,44 @@ void p2p_buf_add_operating_channel(struct wpabuf *buf, const char *country, } +void p2p_buf_add_pref_channel_list(struct wpabuf *buf, + const u32 *preferred_freq_list, + unsigned int size) +{ + unsigned int i, count = 0; + u8 op_class, op_channel; + + if (!size) + return; + + /* + * First, determine the number of P2P supported channels in the + * pref_freq_list returned from driver. This is needed for calculations + * of the vendor IE size. + */ + for (i = 0; i < size; i++) { + if (p2p_freq_to_channel(preferred_freq_list[i], &op_class, + &op_channel) == 0) + count++; + } + + wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(buf, 4 + count * sizeof(u16)); + wpabuf_put_be24(buf, OUI_QCA); + wpabuf_put_u8(buf, QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST); + for (i = 0; i < size; i++) { + if (p2p_freq_to_channel(preferred_freq_list[i], &op_class, + &op_channel) < 0) { + wpa_printf(MSG_DEBUG, "Unsupported frequency %u MHz", + preferred_freq_list[i]); + continue; + } + wpabuf_put_u8(buf, op_class); + wpabuf_put_u8(buf, op_channel); + } +} + + void p2p_buf_add_channel_list(struct wpabuf *buf, const char *country, struct p2p_channels *chan) { @@ -353,10 +392,10 @@ void p2p_buf_add_service_hash(struct wpabuf *buf, struct p2p_data *p2p) /* Service Hash */ wpabuf_put_u8(buf, P2P_ATTR_SERVICE_HASH); wpabuf_put_le16(buf, p2p->p2ps_seek_count * P2PS_HASH_LEN); - wpabuf_put_data(buf, p2p->query_hash, + wpabuf_put_data(buf, p2p->p2ps_seek_hash, p2p->p2ps_seek_count * P2PS_HASH_LEN); wpa_hexdump(MSG_DEBUG, "P2P: * Service Hash", - p2p->query_hash, p2p->p2ps_seek_count * P2PS_HASH_LEN); + p2p->p2ps_seek_hash, p2p->p2ps_seek_count * P2PS_HASH_LEN); } @@ -404,152 +443,221 @@ void p2p_buf_add_advertisement_id(struct wpabuf *buf, u32 id, const u8 *mac) } -void p2p_buf_add_service_instance(struct wpabuf *buf, struct p2p_data *p2p, - u8 hash_count, const u8 *hash, - struct p2ps_advertisement *adv_list) +static int p2ps_wildcard_hash(struct p2p_data *p2p, + const u8 *hash, u8 hash_count) +{ + u8 i; + const u8 *test = hash; + + for (i = 0; i < hash_count; i++) { + if (os_memcmp(test, p2p->wild_card_hash, P2PS_HASH_LEN) == 0) + return 1; + test += P2PS_HASH_LEN; + } + + return 0; +} + + +static int p2p_wfa_service_adv(struct p2p_data *p2p) { struct p2ps_advertisement *adv; - struct wpabuf *tmp_buf; - u8 *tag_len = NULL, *ie_len = NULL; - size_t svc_len = 0, remaining = 0, total_len = 0; - if (!adv_list || !hash) - return; + for (adv = p2p->p2ps_adv_list; adv; adv = adv->next) { + if (os_strncmp(adv->svc_name, P2PS_WILD_HASH_STR, + os_strlen(P2PS_WILD_HASH_STR)) == 0) + return 1; + } - /* Allocate temp buffer, allowing for overflow of 1 instance */ - tmp_buf = wpabuf_alloc(MAX_SVC_ADV_IE_LEN + 256 + P2PS_HASH_LEN); - if (!tmp_buf) - return; + return 0; +} - for (adv = adv_list; adv && total_len <= MAX_SVC_ADV_LEN; - adv = adv->next) { - u8 count = hash_count; - const u8 *test = hash; - while (count--) { - /* Check for wildcard */ - if (os_memcmp(test, p2p->wild_card_hash, - P2PS_HASH_LEN) == 0) { - total_len = MAX_SVC_ADV_LEN + 1; - goto wild_hash; - } +static int p2p_buf_add_service_info(struct wpabuf *buf, struct p2p_data *p2p, + u32 adv_id, u16 config_methods, + const char *svc_name, u8 **ie_len, u8 **pos, + size_t *total_len, u8 *attr_len) +{ + size_t svc_len; + size_t remaining; + size_t info_len; - if (os_memcmp(test, adv->hash, P2PS_HASH_LEN) == 0) - goto hash_match; + p2p_dbg(p2p, "Add service info for %s (adv_id=%u)", svc_name, adv_id); + svc_len = os_strlen(svc_name); + info_len = sizeof(adv_id) + sizeof(config_methods) + sizeof(u8) + + svc_len; - test += P2PS_HASH_LEN; - } + if (info_len + *total_len > MAX_SVC_ADV_LEN) { + p2p_dbg(p2p, + "Unsufficient buffer, failed to add advertised service info"); + return -1; + } - /* No matches found - Skip this Adv Instance */ - continue; - -hash_match: - if (!tag_len) { - tag_len = p2p_buf_add_ie_hdr(tmp_buf); - remaining = 255 - 4; - if (!ie_len) { - wpabuf_put_u8(tmp_buf, - P2P_ATTR_ADVERTISED_SERVICE); - ie_len = wpabuf_put(tmp_buf, sizeof(u16)); - remaining -= (sizeof(u8) + sizeof(u16)); - } - } + if (svc_len > 255) { + p2p_dbg(p2p, + "Invalid service name length (%u bytes), failed to add advertised service info", + (unsigned int) svc_len); + return -1; + } - svc_len = os_strlen(adv->svc_name); + if (*ie_len) { + int ie_data_len = (*pos - *ie_len) - 1; - if (7 + svc_len + total_len > MAX_SVC_ADV_LEN) { - /* Can't fit... return wildcard */ - total_len = MAX_SVC_ADV_LEN + 1; - break; + if (ie_data_len < 0 || ie_data_len > 255) { + p2p_dbg(p2p, + "Invalid IE length, failed to add advertised service info"); + return -1; } + remaining = 255 - ie_data_len; + } else { + /* + * Adding new P2P IE header takes 6 extra bytes: + * - 2 byte IE header (1 byte IE id and 1 byte length) + * - 4 bytes of IE_VENDOR_TYPE are reduced from 255 below + */ + *ie_len = p2p_buf_add_ie_hdr(buf); + remaining = 255 - 4; + } - if (remaining <= (sizeof(adv->id) + - sizeof(adv->config_methods))) { - size_t front = remaining; - size_t back = (sizeof(adv->id) + - sizeof(adv->config_methods)) - front; - u8 holder[sizeof(adv->id) + - sizeof(adv->config_methods)]; - - /* This works even if front or back == 0 */ - WPA_PUT_LE32(holder, adv->id); - WPA_PUT_BE16(&holder[sizeof(adv->id)], - adv->config_methods); - wpabuf_put_data(tmp_buf, holder, front); - p2p_buf_update_ie_hdr(tmp_buf, tag_len); - tag_len = p2p_buf_add_ie_hdr(tmp_buf); - wpabuf_put_data(tmp_buf, &holder[front], back); - remaining = 255 - (sizeof(adv->id) + - sizeof(adv->config_methods)) - back; - } else { - wpabuf_put_le32(tmp_buf, adv->id); - wpabuf_put_be16(tmp_buf, adv->config_methods); - remaining -= (sizeof(adv->id) + - sizeof(adv->config_methods)); - } + if (remaining < sizeof(u32) + sizeof(u16) + sizeof(u8)) { + /* + * Split adv_id, config_methods, and svc_name_len between two + * IEs. + */ + size_t front = remaining; + size_t back = sizeof(u32) + sizeof(u16) + sizeof(u8) - front; + u8 holder[sizeof(u32) + sizeof(u16) + sizeof(u8)]; - /* We are guaranteed at least one byte for svc_len */ - wpabuf_put_u8(tmp_buf, svc_len); - remaining -= sizeof(u8); - - if (remaining < svc_len) { - size_t front = remaining; - size_t back = svc_len - front; - - wpabuf_put_data(tmp_buf, adv->svc_name, front); - p2p_buf_update_ie_hdr(tmp_buf, tag_len); - tag_len = p2p_buf_add_ie_hdr(tmp_buf); - - /* In rare cases, we must split across 3 attributes */ - if (back > 255 - 4) { - wpabuf_put_data(tmp_buf, - &adv->svc_name[front], 255 - 4); - back -= 255 - 4; - front += 255 - 4; - p2p_buf_update_ie_hdr(tmp_buf, tag_len); - tag_len = p2p_buf_add_ie_hdr(tmp_buf); - } - - wpabuf_put_data(tmp_buf, &adv->svc_name[front], back); - remaining = 255 - 4 - back; - } else { - wpabuf_put_data(tmp_buf, adv->svc_name, svc_len); - remaining -= svc_len; - } + WPA_PUT_LE32(holder, adv_id); + WPA_PUT_BE16(&holder[sizeof(u32)], config_methods); + holder[sizeof(u32) + sizeof(u16)] = svc_len; + + if (front) + wpabuf_put_data(buf, holder, front); + + p2p_buf_update_ie_hdr(buf, *ie_len); + *ie_len = p2p_buf_add_ie_hdr(buf); - /* adv_id config_methods svc_string */ - total_len += sizeof(u32) + sizeof(u16) + sizeof(u8) + svc_len; + wpabuf_put_data(buf, &holder[front], back); + remaining = 255 - 4 - (sizeof(u32) + sizeof(u16) + sizeof(u8)) - + back; + } else { + wpabuf_put_le32(buf, adv_id); + wpabuf_put_be16(buf, config_methods); + wpabuf_put_u8(buf, svc_len); + remaining -= sizeof(adv_id) + sizeof(config_methods) + + sizeof(u8); } - if (tag_len) - p2p_buf_update_ie_hdr(tmp_buf, tag_len); + if (remaining < svc_len) { + /* split svc_name between two or three IEs */ + size_t front = remaining; + size_t back = svc_len - front; - if (ie_len) - WPA_PUT_LE16(ie_len, (u16) total_len); + if (front) + wpabuf_put_data(buf, svc_name, front); -wild_hash: - /* If all fit, return matching instances, otherwise the wildcard */ - if (total_len <= MAX_SVC_ADV_LEN) { - wpabuf_put_buf(buf, tmp_buf); + p2p_buf_update_ie_hdr(buf, *ie_len); + *ie_len = p2p_buf_add_ie_hdr(buf); + + /* In rare cases, we must split across 3 attributes */ + if (back > 255 - 4) { + wpabuf_put_data(buf, &svc_name[front], 255 - 4); + back -= 255 - 4; + front += 255 - 4; + p2p_buf_update_ie_hdr(buf, *ie_len); + *ie_len = p2p_buf_add_ie_hdr(buf); + } + + wpabuf_put_data(buf, &svc_name[front], back); + remaining = 255 - 4 - back; } else { - char *wild_card = P2PS_WILD_HASH_STR; - u8 wild_len; - - /* Insert wildcard instance */ - tag_len = p2p_buf_add_ie_hdr(buf); - wpabuf_put_u8(buf, P2P_ATTR_ADVERTISED_SERVICE); - ie_len = wpabuf_put(buf, sizeof(u16)); - - wild_len = (u8) os_strlen(wild_card); - wpabuf_put_le32(buf, 0); - wpabuf_put_be16(buf, 0); - wpabuf_put_u8(buf, wild_len); - wpabuf_put_data(buf, wild_card, wild_len); - - WPA_PUT_LE16(ie_len, 4 + 2 + 1 + wild_len); - p2p_buf_update_ie_hdr(buf, tag_len); + wpabuf_put_data(buf, svc_name, svc_len); + remaining -= svc_len; } + p2p_buf_update_ie_hdr(buf, *ie_len); + + /* set *ie_len to NULL if a new IE has to be added on the next call */ + if (!remaining) + *ie_len = NULL; + + /* set *pos to point to the next byte to update */ + *pos = wpabuf_put(buf, 0); + + *total_len += info_len; + WPA_PUT_LE16(attr_len, (u16) *total_len); + return 0; +} + + +void p2p_buf_add_service_instance(struct wpabuf *buf, struct p2p_data *p2p, + u8 hash_count, const u8 *hash, + struct p2ps_advertisement *adv_list) +{ + struct p2ps_advertisement *adv; + int p2ps_wildcard; + size_t total_len; + struct wpabuf *tmp_buf = NULL; + u8 *pos, *attr_len, *ie_len = NULL; + + if (!adv_list || !hash || !hash_count) + return; + + wpa_hexdump(MSG_DEBUG, "P2PS: Probe Request service hash values", + hash, hash_count * P2PS_HASH_LEN); + p2ps_wildcard = p2ps_wildcard_hash(p2p, hash, hash_count) && + p2p_wfa_service_adv(p2p); + + /* Allocate temp buffer, allowing for overflow of 1 instance */ + tmp_buf = wpabuf_alloc(MAX_SVC_ADV_IE_LEN + 256 + P2PS_HASH_LEN); + if (!tmp_buf) + return; + + /* + * Attribute data can be split into a number of IEs. Start with the + * first IE and the attribute headers here. + */ + ie_len = p2p_buf_add_ie_hdr(tmp_buf); + + total_len = 0; + + wpabuf_put_u8(tmp_buf, P2P_ATTR_ADVERTISED_SERVICE); + attr_len = wpabuf_put(tmp_buf, sizeof(u16)); + WPA_PUT_LE16(attr_len, (u16) total_len); + p2p_buf_update_ie_hdr(tmp_buf, ie_len); + pos = wpabuf_put(tmp_buf, 0); + + if (p2ps_wildcard) { + /* org.wi-fi.wfds match found */ + p2p_buf_add_service_info(tmp_buf, p2p, 0, 0, P2PS_WILD_HASH_STR, + &ie_len, &pos, &total_len, attr_len); + } + + /* add advertised service info of matching services */ + for (adv = adv_list; adv && total_len <= MAX_SVC_ADV_LEN; + adv = adv->next) { + const u8 *test = hash; + u8 i; + + for (i = 0; i < hash_count; i++) { + /* exact name hash match */ + if (os_memcmp(test, adv->hash, P2PS_HASH_LEN) == 0 && + p2p_buf_add_service_info(tmp_buf, p2p, + adv->id, + adv->config_methods, + adv->svc_name, + &ie_len, &pos, + &total_len, + attr_len)) + break; + + test += P2PS_HASH_LEN; + } + } + + if (total_len) + wpabuf_put_buf(buf, tmp_buf); wpabuf_free(tmp_buf); } diff --git a/contrib/wpa/src/p2p/p2p_dev_disc.c b/contrib/wpa/src/p2p/p2p_dev_disc.c index 86bae1a..98805fe 100644 --- a/contrib/wpa/src/p2p/p2p_dev_disc.c +++ b/contrib/wpa/src/p2p/p2p_dev_disc.c @@ -314,7 +314,7 @@ void p2p_process_go_disc_req(struct p2p_data *p2p, const u8 *da, const u8 *sa, p2p_dbg(p2p, "Received GO Discoverability Request - remain awake for 100 TU"); - ies = p2p_build_probe_resp_ies(p2p); + ies = p2p_build_probe_resp_ies(p2p, NULL, 0); if (ies == NULL) return; diff --git a/contrib/wpa/src/p2p/p2p_go_neg.c b/contrib/wpa/src/p2p/p2p_go_neg.c index 98abf9d..83b4356 100644 --- a/contrib/wpa/src/p2p/p2p_go_neg.c +++ b/contrib/wpa/src/p2p/p2p_go_neg.c @@ -185,6 +185,9 @@ static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p, p2p->op_reg_class, p2p->op_channel); p2p_buf_update_ie_hdr(buf, len); + p2p_buf_add_pref_channel_list(buf, p2p->pref_freq_list, + p2p->num_pref_freq); + /* WPS IE with Device Password ID attribute */ pw_id = p2p_wps_method_pw_id(peer->wps_method); if (peer->oob_pw_id) @@ -312,7 +315,7 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p, group_capab); p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | tie_breaker); p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout); - if (peer && peer->go_state == REMOTE_GO) { + if (peer && peer->go_state == REMOTE_GO && !p2p->num_pref_freq) { p2p_dbg(p2p, "Omit Operating Channel attribute"); } else { p2p_buf_add_operating_channel(buf, p2p->cfg->country, @@ -379,7 +382,7 @@ void p2p_reselect_channel(struct p2p_data *p2p, int freq; u8 op_reg_class, op_channel; unsigned int i; - const int op_classes_5ghz[] = { 124, 115, 0 }; + 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 }; @@ -542,6 +545,195 @@ int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev, } +static void p2p_check_pref_chan_no_recv(struct p2p_data *p2p, int go, + struct p2p_device *dev, + struct p2p_message *msg, + unsigned freq_list[], unsigned int size) +{ + u8 op_class, op_channel; + unsigned int oper_freq = 0, i, j; + int found = 0; + + p2p_dbg(p2p, + "Peer didn't provide a preferred frequency list, see if any of our preferred channels are supported by peer device"); + + /* + * Search for a common channel in our preferred frequency list which is + * also supported by the peer device. + */ + for (i = 0; i < size && !found; i++) { + /* + * Make sure that the common frequency is: + * 1. Supported by peer + * 2. Allowed for P2P use. + */ + oper_freq = freq_list[i]; + if (p2p_freq_to_channel(oper_freq, &op_class, + &op_channel) < 0) { + p2p_dbg(p2p, "Unsupported frequency %u MHz", oper_freq); + continue; + } + if (!p2p_channels_includes(&p2p->cfg->channels, + op_class, op_channel) && + (go || !p2p_channels_includes(&p2p->cfg->cli_channels, + op_class, op_channel))) { + p2p_dbg(p2p, + "Freq %u MHz (oper_class %u channel %u) not allowed for P2P", + oper_freq, op_class, op_channel); + break; + } + for (j = 0; j < msg->channel_list_len; j++) { + + if (op_channel != msg->channel_list[j]) + continue; + + p2p->op_reg_class = op_class; + p2p->op_channel = op_channel; + os_memcpy(&p2p->channels, &p2p->cfg->channels, + sizeof(struct p2p_channels)); + found = 1; + break; + } + } + + if (found) { + p2p_dbg(p2p, + "Freq %d MHz is a preferred channel and is also supported by peer, use it as the operating channel", + oper_freq); + } else { + p2p_dbg(p2p, + "None of our preferred channels are supported by peer!. Use: %d MHz for oper_channel", + dev->oper_freq); + } +} + + +static void p2p_check_pref_chan_recv(struct p2p_data *p2p, int go, + struct p2p_device *dev, + struct p2p_message *msg, + unsigned freq_list[], unsigned int size) +{ + u8 op_class, op_channel; + unsigned int oper_freq = 0, i, j; + int found = 0; + + /* + * Peer device supports a Preferred Frequency List. + * Search for a common channel in the preferred frequency lists + * of both peer and local devices. + */ + for (i = 0; i < size && !found; i++) { + for (j = 2; j < (msg->pref_freq_list_len / 2); j++) { + oper_freq = p2p_channel_to_freq( + msg->pref_freq_list[2 * j], + msg->pref_freq_list[2 * j + 1]); + if (freq_list[i] != oper_freq) + continue; + + /* + * Make sure that the found frequency is: + * 1. Supported + * 2. Allowed for P2P use. + */ + if (p2p_freq_to_channel(oper_freq, &op_class, + &op_channel) < 0) { + p2p_dbg(p2p, "Unsupported frequency %u MHz", + oper_freq); + continue; + } + + if (!p2p_channels_includes(&p2p->cfg->channels, + op_class, op_channel) && + (go || + !p2p_channels_includes(&p2p->cfg->cli_channels, + op_class, op_channel))) { + p2p_dbg(p2p, + "Freq %u MHz (oper_class %u channel %u) not allowed for P2P", + oper_freq, op_class, op_channel); + break; + } + p2p->op_reg_class = op_class; + p2p->op_channel = op_channel; + os_memcpy(&p2p->channels, &p2p->cfg->channels, + sizeof(struct p2p_channels)); + found = 1; + break; + } + } + + if (found) { + p2p_dbg(p2p, + "Freq %d MHz is a common preferred channel for both peer and local, use it as operating channel", + oper_freq); + } else { + p2p_dbg(p2p, + "No common preferred channels found! Use: %d MHz for oper_channel", + dev->oper_freq); + } +} + + +void p2p_check_pref_chan(struct p2p_data *p2p, int go, + struct p2p_device *dev, struct p2p_message *msg) +{ + unsigned int freq_list[P2P_MAX_PREF_CHANNELS], size; + unsigned int i; + u8 op_class, op_channel; + + /* + * Use the preferred channel list from the driver only if there is no + * forced_freq, e.g., P2P_CONNECT freq=..., and no preferred operating + * channel hardcoded in the configuration file. + */ + if (!p2p->cfg->get_pref_freq_list || p2p->cfg->num_pref_chan || + (dev->flags & P2P_DEV_FORCE_FREQ) || p2p->cfg->cfg_op_channel) + return; + + /* Obtain our preferred frequency list from driver based on P2P role. */ + size = P2P_MAX_PREF_CHANNELS; + if (p2p->cfg->get_pref_freq_list(p2p->cfg->cb_ctx, go, &size, + freq_list)) + return; + + /* + * Check if peer's preference of operating channel is in + * our preferred channel list. + */ + for (i = 0; i < size; i++) { + if (freq_list[i] == (unsigned int) dev->oper_freq) + break; + } + if (i != size) { + /* Peer operating channel preference matches our preference */ + if (p2p_freq_to_channel(freq_list[i], &op_class, &op_channel) < + 0) { + p2p_dbg(p2p, + "Peer operating channel preference is unsupported frequency %u MHz", + freq_list[i]); + } else { + p2p->op_reg_class = op_class; + p2p->op_channel = op_channel; + os_memcpy(&p2p->channels, &p2p->cfg->channels, + sizeof(struct p2p_channels)); + return; + } + } + + p2p_dbg(p2p, + "Peer operating channel preference: %d MHz is not in our preferred channel list", + dev->oper_freq); + + /* + Check if peer's preferred channel list is + * _not_ included in the GO Negotiation Request or Invitation Request. + */ + if (msg->pref_freq_list_len == 0) + p2p_check_pref_chan_no_recv(p2p, go, dev, msg, freq_list, size); + else + p2p_check_pref_chan_recv(p2p, go, dev, msg, freq_list, size); +} + + void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq) { @@ -668,7 +860,9 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, MAC2STR(sa)); status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; p2p->cfg->go_neg_req_rx(p2p->cfg->cb_ctx, sa, - msg.dev_password_id); + msg.dev_password_id, + msg.go_intent ? (*msg.go_intent >> 1) : + 0); } else if (p2p->go_neg_peer && p2p->go_neg_peer != dev) { p2p_dbg(p2p, "Already in Group Formation with another peer"); status = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; @@ -797,6 +991,12 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, p2p_dbg(p2p, "Peer operating channel preference: %d MHz", dev->oper_freq); + /* + * Use the driver preferred frequency list extension if + * supported. + */ + p2p_check_pref_chan(p2p, go, dev, &msg); + if (msg.config_timeout) { dev->go_timeout = msg.config_timeout[0]; dev->client_timeout = msg.config_timeout[1]; @@ -1148,6 +1348,13 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, if (go && p2p_go_select_channel(p2p, dev, &status) < 0) goto fail; + /* + * Use the driver preferred frequency list extension if local device is + * GO. + */ + if (go) + p2p_check_pref_chan(p2p, go, dev, &msg); + p2p_set_state(p2p, P2P_GO_NEG); p2p_clear_timeout(p2p); diff --git a/contrib/wpa/src/p2p/p2p_group.c b/contrib/wpa/src/p2p/p2p_group.c index 41ca99f..0d66993 100644 --- a/contrib/wpa/src/p2p/p2p_group.c +++ b/contrib/wpa/src/p2p/p2p_group.c @@ -1071,3 +1071,43 @@ void p2p_loop_on_all_groups(struct p2p_data *p2p, break; } } + + +int p2p_group_get_common_freqs(struct p2p_group *group, int *common_freqs, + unsigned int *num) + +{ + struct p2p_channels intersect, res; + struct p2p_group_member *m; + + if (!group || !common_freqs || !num) + return -1; + + os_memset(&intersect, 0, sizeof(intersect)); + os_memset(&res, 0, sizeof(res)); + + p2p_channels_union(&intersect, &group->p2p->cfg->channels, + &intersect); + + p2p_channels_dump(group->p2p, + "Group common freqs before iterating members", + &intersect); + + for (m = group->members; m; m = m->next) { + struct p2p_device *dev; + + dev = p2p_get_device(group->p2p, m->dev_addr); + if (!dev) + continue; + + p2p_channels_intersect(&intersect, &dev->channels, &res); + intersect = res; + } + + p2p_channels_dump(group->p2p, "Group common channels", &intersect); + + os_memset(common_freqs, 0, *num * sizeof(int)); + *num = p2p_channels_to_freqs(&intersect, common_freqs, *num); + + return 0; +} diff --git a/contrib/wpa/src/p2p/p2p_i.h b/contrib/wpa/src/p2p/p2p_i.h index 6af19ce..0ce4058 100644 --- a/contrib/wpa/src/p2p/p2p_i.h +++ b/contrib/wpa/src/p2p/p2p_i.h @@ -14,6 +14,12 @@ #define P2P_GO_NEG_CNF_MAX_RETRY_COUNT 1 +/* + * A threshold (in seconds) to prefer a direct Probe Response frame from a P2P + * Device over the P2P Client Info received from a GO. + */ +#define P2P_DEV_GROUP_CLIENT_RESP_THRESHOLD 1 + enum p2p_role_indication; /* @@ -47,6 +53,9 @@ struct p2p_device { * from Beacon/Probe Response), the interface address is stored here. * p2p_device_addr must still be set in such a case to the unique * identifier for the P2P Device. + * + * This field is also used during P2PS PD to store the intended GO + * address of the peer. */ u8 interface_addr[ETH_ALEN]; @@ -71,7 +80,7 @@ struct p2p_device { char country[3]; struct p2p_channels channels; int oper_freq; - u8 oper_ssid[32]; + u8 oper_ssid[SSID_MAX_LEN]; size_t oper_ssid_len; /** @@ -107,6 +116,8 @@ struct p2p_device { #define P2P_DEV_WAIT_INV_REQ_ACK BIT(19) #define P2P_DEV_P2PS_REPORTED BIT(20) #define P2P_DEV_PD_PEER_P2PS BIT(21) +#define P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT BIT(22) + unsigned int flags; int status; /* enum p2p_status_code */ @@ -322,7 +333,7 @@ struct p2p_data { /** * ssid - Selected SSID for GO Negotiation (if local end will be GO) */ - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; /** * ssid_len - ssid length in octets @@ -403,7 +414,7 @@ struct p2p_data { enum p2p_invite_role inv_role; u8 inv_bssid[ETH_ALEN]; int inv_bssid_set; - u8 inv_ssid[32]; + u8 inv_ssid[SSID_MAX_LEN]; size_t inv_ssid_len; u8 inv_sa[ETH_ALEN]; u8 inv_group_bssid[ETH_ALEN]; @@ -506,11 +517,9 @@ struct p2p_data { struct p2ps_advertisement *p2ps_adv_list; struct p2ps_provision *p2ps_prov; u8 wild_card_hash[P2PS_HASH_LEN]; - u8 query_hash[P2P_MAX_QUERY_HASH * P2PS_HASH_LEN]; - u8 query_count; u8 p2ps_seek; + u8 p2ps_seek_hash[P2P_MAX_QUERY_HASH * P2PS_HASH_LEN]; u8 p2ps_seek_count; - u8 p2ps_svc_found; #ifdef CONFIG_WIFI_DISPLAY struct wpabuf *wfd_ie_beacon; @@ -529,6 +538,9 @@ struct p2p_data { u16 authorized_oob_dev_pw_id; struct wpabuf **vendor_elem; + + unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS]; + unsigned int num_pref_freq; }; /** @@ -578,7 +590,7 @@ struct p2p_message { const u8 *p2p_device_addr; const u8 *pri_dev_type; u8 num_sec_dev_types; - char device_name[33]; + char device_name[WPS_DEV_NAME_MAX_LEN + 1]; u16 config_methods; /* WPS IE */ @@ -631,6 +643,9 @@ struct p2p_message { const u8 *persistent_dev; const u8 *persistent_ssid; size_t persistent_ssid_len; + + const u8 *pref_freq_list; + size_t pref_freq_list_len; }; @@ -757,6 +772,8 @@ void p2p_buf_add_persistent_group_info(struct wpabuf *buf, const u8 *dev_addr, const u8 *ssid, size_t ssid_len); int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id, int all_attr); +void p2p_buf_add_pref_channel_list(struct wpabuf *buf, + const u32 *preferred_freq_list, u32 size); /* p2p_sd.c */ struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p, @@ -786,6 +803,8 @@ int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev); u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method); void p2p_reselect_channel(struct p2p_data *p2p, struct p2p_channels *intersection); +void p2p_check_pref_chan(struct p2p_data *p2p, int go, + struct p2p_device *dev, struct p2p_message *msg); /* p2p_pd.c */ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, @@ -795,6 +814,7 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev, int join, int force_freq); void p2p_reset_pending_pd(struct p2p_data *p2p); +void p2ps_prov_free(struct p2p_data *p2p); /* p2p_invitation.c */ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, @@ -840,7 +860,9 @@ void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer); int p2p_match_dev_type(struct p2p_data *p2p, struct wpabuf *wps); int dev_type_list_match(const u8 *dev_type, const u8 *req_dev_type[], size_t num_req_dev_type); -struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p); +struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p, + const u8 *query_hash, + u8 query_count); void p2p_build_ssid(struct p2p_data *p2p, u8 *ssid, size_t *ssid_len); int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *buf, diff --git a/contrib/wpa/src/p2p/p2p_invitation.c b/contrib/wpa/src/p2p/p2p_invitation.c index 558c6dd..108e5b7 100644 --- a/contrib/wpa/src/p2p/p2p_invitation.c +++ b/contrib/wpa/src/p2p/p2p_invitation.c @@ -85,6 +85,9 @@ static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p, p2p_buf_add_device_info(buf, p2p, peer); p2p_buf_update_ie_hdr(buf, len); + p2p_buf_add_pref_channel_list(buf, p2p->pref_freq_list, + p2p->num_pref_freq); + #ifdef CONFIG_WIFI_DISPLAY if (wfd_ie) wpabuf_put_buf(buf, wfd_ie); @@ -134,6 +137,9 @@ static struct wpabuf * p2p_build_invitation_resp(struct p2p_data *p2p, extra = wpabuf_len(wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ + if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_INV_RESP]) + extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_INV_RESP]); + buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; @@ -158,6 +164,9 @@ static struct wpabuf * p2p_build_invitation_resp(struct p2p_data *p2p, wpabuf_put_buf(buf, wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ + if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_INV_RESP]) + wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_INV_RESP]); + return buf; } @@ -337,6 +346,12 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, p2p_reselect_channel(p2p, &intersection); } + /* + * Use the driver preferred frequency list extension if + * supported. + */ + p2p_check_pref_chan(p2p, go, dev, &msg); + op_freq = p2p_channel_to_freq(p2p->op_reg_class, p2p->op_channel); if (op_freq < 0) { @@ -387,7 +402,7 @@ fail: } else p2p->inv_group_bssid_ptr = NULL; if (msg.group_id) { - if (msg.group_id_len - ETH_ALEN <= 32) { + if (msg.group_id_len - ETH_ALEN <= SSID_MAX_LEN) { os_memcpy(p2p->inv_ssid, msg.group_id + ETH_ALEN, msg.group_id_len - ETH_ALEN); p2p->inv_ssid_len = msg.group_id_len - ETH_ALEN; @@ -528,6 +543,12 @@ void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa, peer_oper_freq = 0; } + /* + * Use the driver preferred frequency list extension if + * supported. + */ + p2p_check_pref_chan(p2p, 0, dev, &msg); + p2p->cfg->invitation_result(p2p->cfg->cb_ctx, *msg.status, msg.group_bssid, channels, sa, freq, peer_oper_freq); diff --git a/contrib/wpa/src/p2p/p2p_parse.c b/contrib/wpa/src/p2p/p2p_parse.c index fd6a461..bd1e68b 100644 --- a/contrib/wpa/src/p2p/p2p_parse.c +++ b/contrib/wpa/src/p2p/p2p_parse.c @@ -149,7 +149,8 @@ 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 > 32) { + if (data + len - pos < (int) nlen || + nlen > WPS_DEV_NAME_MAX_LEN) { wpa_printf(MSG_DEBUG, "P2P: Invalid Device Name " "length %d (buf len %d)", (int) nlen, (int) (data + len - pos)); @@ -160,8 +161,7 @@ static int p2p_parse_attribute(u8 id, const u8 *data, u16 len, for (i = 0; i < nlen; i++) { if (msg->device_name[i] == '\0') break; - if (msg->device_name[i] > 0 && - msg->device_name[i] < 32) + if (is_ctrl_char(msg->device_name[i])) msg->device_name[i] = '_'; } wpa_printf(MSG_DEBUG, "P2P: * Device Info: addr " MACSTR @@ -203,7 +203,7 @@ static int p2p_parse_attribute(u8 id, const u8 *data, u16 len, MAC2STR(msg->group_bssid)); break; case P2P_ATTR_GROUP_ID: - if (len < ETH_ALEN || len > ETH_ALEN + 32) { + if (len < ETH_ALEN || len > ETH_ALEN + SSID_MAX_LEN) { wpa_printf(MSG_DEBUG, "P2P: Invalid P2P Group ID " "attribute length %d", len); return -1; @@ -371,9 +371,9 @@ static int p2p_parse_attribute(u8 id, const u8 *data, u16 len, break; case P2P_ATTR_PERSISTENT_GROUP: { - if (len < ETH_ALEN) { + if (len < ETH_ALEN || len > ETH_ALEN + SSID_MAX_LEN) { wpa_printf(MSG_DEBUG, - "P2P: Too short Persistent Group Info (length %u)", + "P2P: Invalid Persistent Group Info (length %u)", len); return -1; } @@ -516,7 +516,7 @@ int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg) struct ieee802_11_elems elems; ieee802_11_parse_elems(data, len, &elems, 0); - if (elems.ds_params && elems.ds_params_len >= 1) + if (elems.ds_params) msg->ds_params = elems.ds_params; if (elems.ssid) msg->ssid = elems.ssid - 2; @@ -548,6 +548,9 @@ int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg) } #endif /* CONFIG_WIFI_DISPLAY */ + msg->pref_freq_list = elems.pref_freq_list; + msg->pref_freq_list_len = elems.pref_freq_list_len; + return 0; } @@ -674,8 +677,8 @@ int p2p_group_info_parse(const u8 *gi, size_t gi_len, t += 2; if (count > cend - t) return -1; /* invalid Device Name TLV */ - if (count >= 32) - count = 32; + if (count >= WPS_DEV_NAME_MAX_LEN) + count = WPS_DEV_NAME_MAX_LEN; cli->dev_name = (const char *) t; cli->dev_name_len = count; @@ -703,7 +706,7 @@ static int p2p_group_info_text(const u8 *gi, size_t gi_len, char *buf, for (i = 0; i < info.num_clients; i++) { struct p2p_client_info *cli; - char name[33]; + char name[WPS_DEV_NAME_MAX_LEN + 1]; char devtype[WPS_DEV_TYPE_BUFSIZE]; u8 s; int count; @@ -742,7 +745,7 @@ static int p2p_group_info_text(const u8 *gi, size_t gi_len, char *buf, name[cli->dev_name_len] = '\0'; count = (int) cli->dev_name_len - 1; while (count >= 0) { - if (name[count] > 0 && name[count] < 32) + if (is_ctrl_char(name[count])) name[count] = '_'; count--; } diff --git a/contrib/wpa/src/p2p/p2p_pd.c b/contrib/wpa/src/p2p/p2p_pd.c index 328b1e0..8900945 100644 --- a/contrib/wpa/src/p2p/p2p_pd.c +++ b/contrib/wpa/src/p2p/p2p_pd.c @@ -44,7 +44,7 @@ static void p2ps_add_new_group_info(struct p2p_data *p2p, struct wpabuf *buf) { int found; u8 intended_addr[ETH_ALEN]; - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; size_t ssid_len; int group_iface; @@ -57,7 +57,11 @@ static void p2ps_add_new_group_info(struct p2p_data *p2p, struct wpabuf *buf) if (found) { p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, ssid, ssid_len); - p2p_buf_add_intended_addr(buf, intended_addr); + + if (group_iface) + p2p_buf_add_intended_addr(buf, p2p->intended_addr); + else + p2p_buf_add_intended_addr(buf, intended_addr); } else { if (!p2p->ssid_set) { p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len); @@ -82,11 +86,12 @@ static void p2ps_add_pd_req_attrs(struct p2p_data *p2p, struct p2p_device *dev, struct wpabuf *buf, u16 config_methods) { struct p2ps_provision *prov = p2p->p2ps_prov; - u8 feat_cap_mask[] = { 1, 0 }; + struct p2ps_feature_capab fcap = { prov->cpt_mask, 0 }; int shared_group = 0; - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; size_t ssid_len; u8 go_dev_addr[ETH_ALEN]; + u8 intended_addr[ETH_ALEN]; /* If we might be explicite group owner, add GO details */ if (prov->conncap & (P2PS_SETUP_GROUP_OWNER | @@ -101,7 +106,7 @@ static void p2ps_add_pd_req_attrs(struct p2p_data *p2p, struct p2p_device *dev, 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); + go_dev_addr, ssid, &ssid_len, intended_addr); } /* Add Operating Channel if conncap includes GO */ @@ -146,12 +151,17 @@ static void p2ps_add_pd_req_attrs(struct p2p_data *p2p, struct p2p_device *dev, p2p_buf_add_session_id(buf, prov->session_id, prov->session_mac); - p2p_buf_add_feature_capability(buf, sizeof(feat_cap_mask), - feat_cap_mask); + p2p_buf_add_feature_capability(buf, sizeof(fcap), (const u8 *) &fcap); - if (shared_group) + if (shared_group) { p2p_buf_add_persistent_group_info(buf, go_dev_addr, ssid, ssid_len); + /* Add intended interface address if it is not added yet */ + if ((prov->conncap == P2PS_SETUP_NONE || + prov->conncap == P2PS_SETUP_CLIENT) && + !is_zero_ether_addr(intended_addr)) + p2p_buf_add_intended_addr(buf, intended_addr); + } } @@ -232,7 +242,9 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, const u8 *group_id, size_t group_id_len, const u8 *persist_ssid, - size_t persist_ssid_len) + size_t persist_ssid_len, + const u8 *fcap, + u16 fcap_len) { struct wpabuf *buf; size_t extra = 0; @@ -270,7 +282,6 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, /* Add P2P IE for P2PS */ if (p2p->p2ps_prov && p2p->p2ps_prov->adv_id == adv_id) { - u8 feat_cap_mask[] = { 1, 0 }; u8 *len = p2p_buf_add_ie_hdr(buf); struct p2ps_provision *prov = p2p->p2ps_prov; u8 group_capab; @@ -293,18 +304,23 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, if (persist_ssid && p2p->cfg->get_persistent_group && (status == P2P_SC_SUCCESS || status == P2P_SC_SUCCESS_DEFERRED)) { - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; size_t ssid_len; u8 go_dev_addr[ETH_ALEN]; + u8 intended_addr[ETH_ALEN]; persist = p2p->cfg->get_persistent_group( p2p->cfg->cb_ctx, dev->info.p2p_device_addr, persist_ssid, persist_ssid_len, go_dev_addr, - ssid, &ssid_len); - if (persist) + ssid, &ssid_len, intended_addr); + if (persist) { p2p_buf_add_persistent_group_info( buf, go_dev_addr, ssid, ssid_len); + if (!is_zero_ether_addr(intended_addr)) + p2p_buf_add_intended_addr( + buf, intended_addr); + } } if (!persist && (prov->conncap & P2PS_SETUP_GROUP_OWNER)) @@ -344,8 +360,7 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, p2p_buf_add_session_id(buf, prov->session_id, prov->session_mac); - p2p_buf_add_feature_capability(buf, sizeof(feat_cap_mask), - feat_cap_mask); + p2p_buf_add_feature_capability(buf, fcap_len, fcap); p2p_buf_update_ie_hdr(buf, len); } else if (status != P2P_SC_SUCCESS || adv_id) { u8 *len = p2p_buf_add_ie_hdr(buf); @@ -400,6 +415,18 @@ static int p2ps_setup_p2ps_prov(struct p2p_data *p2p, u32 adv_id, } +static u8 p2ps_own_preferred_cpt(const u8 *cpt_priority, u8 req_cpt_mask) +{ + int i; + + for (i = 0; cpt_priority[i]; i++) + if (req_cpt_mask & cpt_priority[i]) + return cpt_priority[i]; + + return 0; +} + + void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq) { @@ -415,9 +442,12 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, u32 session_id = 0; u8 session_mac[ETH_ALEN]; u8 adv_mac[ETH_ALEN]; - u8 group_mac[ETH_ALEN]; + 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; if (p2p_parse(data, len, &msg)) return; @@ -425,6 +455,7 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, p2p_dbg(p2p, "Received Provision Discovery Request from " MACSTR " with config methods 0x%x (freq=%d)", MAC2STR(sa), msg.wps_config_methods, rx_freq); + group_mac = msg.intended_addr; dev = p2p_get_device(p2p, sa); if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { @@ -441,9 +472,12 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); } - if (!(msg.wps_config_methods & - (WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD | - WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_P2PS))) { + if (msg.adv_id) + allowed_config_methods |= WPS_CONFIG_P2PS; + else + 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; } @@ -501,14 +535,23 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, os_memset(session_mac, 0, ETH_ALEN); os_memset(adv_mac, 0, ETH_ALEN); - os_memset(group_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; - if (msg.intended_addr) - os_memcpy(group_mac, msg.intended_addr, ETH_ALEN); + req_fcap = (struct p2ps_feature_capab *) msg.feature_cap; os_memcpy(session_mac, msg.session_mac, ETH_ALEN); os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN); @@ -533,9 +576,22 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, p2p_dbg(p2p, "Conncap: local:%d remote:%d result:%d", auto_accept, remote_conncap, conncap); - if (p2ps_adv->config_methods && - !(msg.wps_config_methods & - p2ps_adv->config_methods)) { + 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, @@ -624,6 +680,15 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, 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 @@ -634,14 +699,22 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, else if (method & WPS_CONFIG_KEYPAD) method = WPS_CONFIG_DISPLAY; - /* Reject this "Deferred Accept* if incompatible - * conncap or method */ if (!conncap || - !(msg.wps_config_methods & method)) + !(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 + } else { reject = P2P_SC_SUCCESS; + } p2p->p2ps_prov->status = reject; p2p->p2ps_prov->conncap = conncap; @@ -659,7 +732,9 @@ out: config_methods, adv_id, msg.group_id, msg.group_id_len, msg.persistent_ssid, - msg.persistent_ssid_len); + msg.persistent_ssid_len, + (const u8 *) &resp_fcap, + sizeof(resp_fcap)); if (resp == NULL) { p2p_parse_free(&msg); return; @@ -696,7 +771,7 @@ out: NULL, adv_id, session_id, 0, 0, msg.persistent_ssid, msg.persistent_ssid_len, - 0, 0, NULL); + 0, 0, NULL, NULL, 0); } else if (msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED && p2p->p2ps_prov) { p2p->p2ps_prov->status = reject; @@ -709,7 +784,7 @@ out: session_id, conncap, 0, msg.persistent_ssid, msg.persistent_ssid_len, 0, - 0, NULL); + 0, NULL, NULL, 0); else p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, *msg.status, @@ -719,7 +794,9 @@ out: passwd_id, msg.persistent_ssid, msg.persistent_ssid_len, 0, - 0, NULL); + 0, NULL, + (const u8 *) &resp_fcap, + sizeof(resp_fcap)); } 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, @@ -728,7 +805,9 @@ out: passwd_id, msg.persistent_ssid, msg.persistent_ssid_len, - 0, 0, NULL); + 0, 0, NULL, + (const u8 *) &resp_fcap, + sizeof(resp_fcap)); } else if (msg.status) { } else if (auto_accept && reject == P2P_SC_SUCCESS) { p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, P2P_SC_SUCCESS, @@ -737,7 +816,9 @@ out: conncap, passwd_id, msg.persistent_ssid, msg.persistent_ssid_len, - 0, 0, NULL); + 0, 0, NULL, + (const u8 *) &resp_fcap, + sizeof(resp_fcap)); } else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE && (!msg.session_info || !msg.session_info_len)) { p2p->p2ps_prov->method = msg.wps_config_methods; @@ -748,7 +829,9 @@ out: conncap, passwd_id, msg.persistent_ssid, msg.persistent_ssid_len, - 0, 1, NULL); + 0, 1, NULL, + (const u8 *) &resp_fcap, + sizeof(resp_fcap)); } 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); @@ -764,14 +847,45 @@ out: adv_mac, session_mac, group_mac, adv_id, session_id, conncap, passwd_id, msg.persistent_ssid, msg.persistent_ssid_len, - 0, 1, buf); + 0, 1, buf, + (const u8 *) &resp_fcap, sizeof(resp_fcap)); os_free(buf); } } - if (reject == P2P_SC_SUCCESS && p2p->cfg->prov_disc_req) { + /* + * prov_disc_req callback is used to generate P2P-PROV-DISC-ENTER-PIN, + * P2P-PROV-DISC-SHOW-PIN, and P2P-PROV-DISC-PBC-REQ events. + * Call it either on legacy P2P PD or on P2PS PD only if we need to + * enter/show PIN. + * + * The callback is called in the following cases: + * 1. Legacy P2P PD request, response status SUCCESS + * 2. P2PS advertiser, method: DISPLAY, autoaccept: TRUE, + * response status: SUCCESS + * 3. P2PS advertiser, method DISPLAY, autoaccept: FALSE, + * response status: INFO_CURRENTLY_UNAVAILABLE + * 4. P2PS advertiser, method: KEYPAD, autoaccept==any, + * response status: INFO_CURRENTLY_UNAVAILABLE + * 5. P2PS follow-on with SUCCESS_DEFERRED, + * advertiser role: DISPLAY, autoaccept: FALSE, + * seeker: KEYPAD, response status: SUCCESS + */ + if (p2p->cfg->prov_disc_req && + ((reject == P2P_SC_SUCCESS && !msg.adv_id) || + (!msg.status && + (reject == P2P_SC_SUCCESS || + reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) && + passwd_id == DEV_PW_USER_SPECIFIED) || + (!msg.status && + reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE && + passwd_id == DEV_PW_REGISTRAR_SPECIFIED) || + (reject == P2P_SC_SUCCESS && + msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED && + passwd_id == DEV_PW_REGISTRAR_SPECIFIED))) { const u8 *dev_addr = sa; + if (msg.p2p_device_addr) dev_addr = msg.p2p_device_addr; p2p->cfg->prov_disc_req(p2p->cfg->cb_ctx, sa, @@ -783,10 +897,133 @@ out: 0, msg.group_id, msg.group_id_len); } + + if (dev && reject == P2P_SC_SUCCESS) { + switch (config_methods) { + case WPS_CONFIG_DISPLAY: + dev->wps_prov_info = WPS_CONFIG_KEYPAD; + break; + case WPS_CONFIG_KEYPAD: + dev->wps_prov_info = WPS_CONFIG_DISPLAY; + break; + case WPS_CONFIG_PUSHBUTTON: + dev->wps_prov_info = WPS_CONFIG_PUSHBUTTON; + break; + case WPS_CONFIG_P2PS: + dev->wps_prov_info = WPS_CONFIG_P2PS; + break; + default: + dev->wps_prov_info = 0; + break; + } + + if (msg.intended_addr) + os_memcpy(dev->interface_addr, msg.intended_addr, + ETH_ALEN); + } p2p_parse_free(&msg); } +static int p2p_validate_p2ps_pd_resp(struct p2p_data *p2p, + struct p2p_message *msg) +{ + u8 conn_cap_go = 0; + u8 conn_cap_cli = 0; + u32 session_id; + u32 adv_id; + +#define P2PS_PD_RESP_CHECK(_val, _attr) \ + do { \ + if ((_val) && !msg->_attr) { \ + p2p_dbg(p2p, "P2PS PD Response missing " #_attr); \ + return -1; \ + } \ + } while (0) + + P2PS_PD_RESP_CHECK(1, status); + P2PS_PD_RESP_CHECK(1, adv_id); + P2PS_PD_RESP_CHECK(1, adv_mac); + P2PS_PD_RESP_CHECK(1, capability); + P2PS_PD_RESP_CHECK(1, p2p_device_info); + P2PS_PD_RESP_CHECK(1, session_id); + P2PS_PD_RESP_CHECK(1, session_mac); + P2PS_PD_RESP_CHECK(1, feature_cap); + + session_id = WPA_GET_LE32(msg->session_id); + adv_id = WPA_GET_LE32(msg->adv_id); + + if (p2p->p2ps_prov->session_id != session_id) { + p2p_dbg(p2p, + "Ignore PD Response with unexpected Session ID"); + return -1; + } + + if (os_memcmp(p2p->p2ps_prov->session_mac, msg->session_mac, + ETH_ALEN)) { + p2p_dbg(p2p, + "Ignore PD Response with unexpected Session MAC"); + return -1; + } + + if (p2p->p2ps_prov->adv_id != adv_id) { + p2p_dbg(p2p, + "Ignore PD Response with unexpected Advertisement ID"); + return -1; + } + + if (os_memcmp(p2p->p2ps_prov->adv_mac, msg->adv_mac, ETH_ALEN) != 0) { + p2p_dbg(p2p, + "Ignore PD Response with unexpected Advertisement MAC"); + return -1; + } + + if (msg->listen_channel) { + p2p_dbg(p2p, + "Ignore malformed PD Response - unexpected Listen Channel"); + return -1; + } + + if (*msg->status == P2P_SC_SUCCESS && + !(!!msg->conn_cap ^ !!msg->persistent_dev)) { + p2p_dbg(p2p, + "Ignore malformed PD Response - either conn_cap or persistent group should be present"); + return -1; + } + + if (msg->persistent_dev && *msg->status != P2P_SC_SUCCESS) { + p2p_dbg(p2p, + "Ignore malformed PD Response - persistent group is present, but the status isn't success"); + return -1; + } + + if (msg->conn_cap) { + conn_cap_go = *msg->conn_cap == P2PS_SETUP_GROUP_OWNER; + conn_cap_cli = *msg->conn_cap == P2PS_SETUP_CLIENT; + } + + P2PS_PD_RESP_CHECK(msg->persistent_dev || conn_cap_go || conn_cap_cli, + channel_list); + P2PS_PD_RESP_CHECK(msg->persistent_dev || conn_cap_go || conn_cap_cli, + config_timeout); + + P2PS_PD_RESP_CHECK(conn_cap_go, group_id); + P2PS_PD_RESP_CHECK(conn_cap_go, intended_addr); + P2PS_PD_RESP_CHECK(conn_cap_go, operating_channel); + /* + * TODO: Also validate that operating channel is present if the device + * is a GO in a persistent group. We can't do it here since we don't + * know what is the role of the peer. It should be probably done in + * p2ps_prov_complete callback, but currently operating channel isn't + * passed to it. + */ + +#undef P2PS_PD_RESP_CHECK + + return 0; +} + + void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len) { @@ -794,24 +1031,26 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, struct p2p_device *dev; u16 report_config_methods = 0, req_config_methods; u8 status = P2P_SC_SUCCESS; - int success = 0; u32 adv_id = 0; u8 conncap = P2PS_SETUP_NEW; u8 adv_mac[ETH_ALEN]; - u8 group_mac[ETH_ALEN]; + const u8 *group_mac; int passwd_id = DEV_PW_DEFAULT; + int p2ps_seeker; if (p2p_parse(data, len, &msg)) return; + if (p2p->p2ps_prov && p2p_validate_p2ps_pd_resp(p2p, &msg)) { + p2p_parse_free(&msg); + return; + } + /* Parse the P2PS members present */ if (msg.status) status = *msg.status; - if (msg.intended_addr) - os_memcpy(group_mac, msg.intended_addr, ETH_ALEN); - else - os_memset(group_mac, 0, ETH_ALEN); + group_mac = msg.intended_addr; if (msg.adv_mac) os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN); @@ -859,6 +1098,8 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, p2p->pending_action_state = P2P_NO_PENDING_ACTION; } + p2ps_seeker = p2p->p2ps_prov && p2p->p2ps_prov->pd_seeker; + /* * Use a local copy of the requested config methods since * p2p_reset_pending_pd() can clear this in the peer entry. @@ -881,8 +1122,7 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, P2P_PROV_DISC_REJECTED, adv_id, adv_mac, NULL); p2p_parse_free(&msg); - os_free(p2p->p2ps_prov); - p2p->p2ps_prov = NULL; + p2ps_prov_free(p2p); goto out; } @@ -909,7 +1149,6 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, } if ((msg.conn_cap || msg.persistent_dev) && - msg.adv_id && (status == P2P_SC_SUCCESS || status == P2P_SC_SUCCESS_DEFERRED) && p2p->p2ps_prov) { if (p2p->cfg->p2ps_prov_complete) { @@ -918,23 +1157,20 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, 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.persistent_ssid_len, 1, 0, NULL, + msg.feature_cap, msg.feature_cap_len); } - os_free(p2p->p2ps_prov); - p2p->p2ps_prov = NULL; - } - - if (status != P2P_SC_SUCCESS && - status != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE && - status != P2P_SC_SUCCESS_DEFERRED && p2p->p2ps_prov) { + p2ps_prov_free(p2p); + } else if (status != P2P_SC_SUCCESS && + status != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE && + status != P2P_SC_SUCCESS_DEFERRED && p2p->p2ps_prov) { if (p2p->cfg->p2ps_prov_complete) 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, - 0, 0, NULL, 0, 1, 0, NULL); - os_free(p2p->p2ps_prov); - p2p->p2ps_prov = NULL; + 0, 0, NULL, 0, 1, 0, NULL, NULL, 0); + p2ps_prov_free(p2p); } if (status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) { @@ -950,8 +1186,7 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, if (!deferred_sess_resp) { p2p_parse_free(&msg); - os_free(p2p->p2ps_prov); - p2p->p2ps_prov = NULL; + p2ps_prov_free(p2p); goto out; } utf8_escape((char *) msg.session_info, info_len, @@ -970,24 +1205,23 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, p2p->cfg->cb_ctx, sa, P2P_PROV_DISC_INFO_UNAVAILABLE, adv_id, adv_mac, NULL); - } else if (msg.wps_config_methods != dev->req_config_methods || - status != P2P_SC_SUCCESS) { + } else if (status != P2P_SC_SUCCESS) { p2p_dbg(p2p, "Peer rejected our Provision Discovery Request"); if (p2p->cfg->prov_disc_fail) p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa, - P2P_PROV_DISC_REJECTED, 0, - NULL, NULL); + P2P_PROV_DISC_REJECTED, + adv_id, adv_mac, NULL); p2p_parse_free(&msg); - os_free(p2p->p2ps_prov); - p2p->p2ps_prov = NULL; + p2ps_prov_free(p2p); goto out; } /* Store the provisioning info */ dev->wps_prov_info = msg.wps_config_methods; + if (msg.intended_addr) + os_memcpy(dev->interface_addr, msg.intended_addr, ETH_ALEN); p2p_parse_free(&msg); - success = 1; out: dev->req_config_methods = 0; @@ -999,7 +1233,28 @@ out: p2p_connect_send(p2p, dev); return; } - if (success && p2p->cfg->prov_disc_resp) + + /* + * prov_disc_resp callback is used to generate P2P-PROV-DISC-ENTER-PIN, + * P2P-PROV-DISC-SHOW-PIN, and P2P-PROV-DISC-PBC-REQ events. + * Call it only for a legacy P2P PD or for P2PS PD scenarios where + * show/enter PIN events are needed. + * + * The callback is called in the following cases: + * 1. Legacy P2P PD response with a status SUCCESS + * 2. P2PS, advertiser method: DISPLAY, autoaccept: true, + * response status: SUCCESS, local method KEYPAD + * 3. P2PS, advertiser method: KEYPAD,Seeker side, + * response status: INFO_CURRENTLY_UNAVAILABLE, + * local method: DISPLAY + */ + if (p2p->cfg->prov_disc_resp && + ((status == P2P_SC_SUCCESS && !adv_id) || + (p2ps_seeker && status == P2P_SC_SUCCESS && + passwd_id == DEV_PW_REGISTRAR_SPECIFIED) || + (p2ps_seeker && + status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE && + passwd_id == DEV_PW_USER_SPECIFIED))) p2p->cfg->prov_disc_resp(p2p->cfg->cb_ctx, sa, report_config_methods); @@ -1120,7 +1375,7 @@ int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr, /* Reset provisioning info */ dev->wps_prov_info = 0; - os_free(p2p->p2ps_prov); + p2ps_prov_free(p2p); p2p->p2ps_prov = p2ps_prov; dev->req_config_methods = config_methods; @@ -1176,3 +1431,10 @@ void p2p_reset_pending_pd(struct p2p_data *p2p) p2p->pd_retries = 0; p2p->pd_force_freq = 0; } + + +void p2ps_prov_free(struct p2p_data *p2p) +{ + os_free(p2p->p2ps_prov); + p2p->p2ps_prov = NULL; +} diff --git a/contrib/wpa/src/p2p/p2p_utils.c b/contrib/wpa/src/p2p/p2p_utils.c index f32751d..2e2aa8a 100644 --- a/contrib/wpa/src/p2p/p2p_utils.c +++ b/contrib/wpa/src/p2p/p2p_utils.c @@ -9,6 +9,7 @@ #include "includes.h" #include "common.h" +#include "common/defs.h" #include "common/ieee802_11_common.h" #include "p2p_i.h" @@ -67,50 +68,11 @@ int p2p_channel_to_freq(int op_class, int channel) */ int p2p_freq_to_channel(unsigned int freq, u8 *op_class, u8 *channel) { - /* TODO: more operating classes */ - if (freq >= 2412 && freq <= 2472) { - if ((freq - 2407) % 5) - return -1; - - *op_class = 81; /* 2.407 GHz, channels 1..13 */ - *channel = (freq - 2407) / 5; - return 0; - } - - if (freq == 2484) { - *op_class = 82; /* channel 14 */ - *channel = 14; - return 0; - } - - if (freq >= 5180 && freq <= 5240) { - if ((freq - 5000) % 5) - return -1; - - *op_class = 115; /* 5 GHz, channels 36..48 */ - *channel = (freq - 5000) / 5; - return 0; - } - - if (freq >= 5745 && freq <= 5805) { - if ((freq - 5000) % 5) - return -1; - - *op_class = 124; /* 5 GHz, channels 149..161 */ - *channel = (freq - 5000) / 5; - return 0; - } - - if (freq >= 58320 && freq <= 64800) { - if ((freq - 58320) % 2160) - return -1; - - *op_class = 180; /* 60 GHz, channels 1..4 */ - *channel = (freq - 56160) / 2160; - return 0; - } + if (ieee80211_freq_to_channel_ext(freq, 0, 0, op_class, channel) == + NUM_HOSTAPD_MODES) + return -1; - return -1; + return 0; } @@ -497,12 +459,22 @@ int p2p_channels_to_freqs(const struct p2p_channels *channels, int *freq_list, break; for (j = 0; j < c->channels; j++) { int freq; + unsigned int k; + if (idx + 1 == max_len) break; freq = p2p_channel_to_freq(c->reg_class, c->channel[j]); if (freq < 0) continue; + + for (k = 0; k < idx; k++) { + if (freq_list[k] == freq) + break; + } + + if (k < idx) + continue; freq_list[idx++] = freq; } } diff --git a/contrib/wpa/src/radius/radius.c b/contrib/wpa/src/radius/radius.c index 8d878a4..1ebfd11 100644 --- a/contrib/wpa/src/radius/radius.c +++ b/contrib/wpa/src/radius/radius.c @@ -167,7 +167,7 @@ struct radius_attr_type { } data_type; }; -static struct radius_attr_type radius_attrs[] = +static const struct radius_attr_type radius_attrs[] = { { RADIUS_ATTR_USER_NAME, "User-Name", RADIUS_ATTR_TEXT }, { RADIUS_ATTR_USER_PASSWORD, "User-Password", RADIUS_ATTR_UNDIST }, @@ -259,7 +259,7 @@ static struct radius_attr_type radius_attrs[] = #define RADIUS_ATTRS ARRAY_SIZE(radius_attrs) -static struct radius_attr_type *radius_get_attr_type(u8 type) +static const struct radius_attr_type *radius_get_attr_type(u8 type) { size_t i; @@ -274,7 +274,7 @@ static struct radius_attr_type *radius_get_attr_type(u8 type) static void radius_msg_dump_attr(struct radius_attr_hdr *hdr) { - struct radius_attr_type *attr; + const struct radius_attr_type *attr; int len; unsigned char *pos; char buf[1000]; @@ -1225,7 +1225,7 @@ int radius_msg_add_mppe_keys(struct radius_msg *msg, } /* MS-MPPE-Recv-Key */ - buf = os_malloc(hlen + send_key_len + 16); + buf = os_malloc(hlen + recv_key_len + 16); if (buf == NULL) { return 0; } @@ -1425,7 +1425,7 @@ struct radius_tunnel_attrs { /** * radius_msg_get_vlanid - Parse RADIUS attributes for VLAN tunnel information * @msg: RADIUS message - * Returns: VLAN ID for the first tunnel configuration of -1 if none is found + * Returns: VLAN ID for the first tunnel configuration or 0 if none is found */ int radius_msg_get_vlanid(struct radius_msg *msg) { @@ -1488,7 +1488,7 @@ int radius_msg_get_vlanid(struct radius_msg *msg) return tun->vlanid; } - return -1; + return 0; } diff --git a/contrib/wpa/src/radius/radius_das.c b/contrib/wpa/src/radius/radius_das.c index 39ceea8..b7d991b 100644 --- a/contrib/wpa/src/radius/radius_das.c +++ b/contrib/wpa/src/radius/radius_das.c @@ -245,7 +245,7 @@ static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx) (u8 *) &val, 4); if (res == 4) { u32 timestamp = ntohl(val); - if ((unsigned int) abs(now.sec - timestamp) > + if ((unsigned int) abs((int) (now.sec - timestamp)) > das->time_window) { wpa_printf(MSG_DEBUG, "DAS: Unacceptable " "Event-Timestamp (%u; local time %u) in " diff --git a/contrib/wpa/src/radius/radius_server.c b/contrib/wpa/src/radius/radius_server.c index 85a485e..744283c 100644 --- a/contrib/wpa/src/radius/radius_server.c +++ b/contrib/wpa/src/radius/radius_server.c @@ -35,7 +35,7 @@ */ #define RADIUS_MAX_MSG_LEN 3000 -static struct eapol_callbacks radius_server_eapol_cb; +static const struct eapol_callbacks radius_server_eapol_cb; struct radius_client; struct radius_server_data; @@ -265,6 +265,8 @@ struct radius_server_data { struct dl_list erp_keys; /* struct eap_server_erp_key */ + unsigned int tls_session_lifetime; + /** * wps - Wi-Fi Protected Setup context * @@ -688,6 +690,7 @@ radius_server_get_new_session(struct radius_server_data *data, eap_conf.server_id = (const u8 *) data->server_id; eap_conf.server_id_len = os_strlen(data->server_id); eap_conf.erp = data->erp; + eap_conf.tls_session_lifetime = data->tls_session_lifetime; radius_server_testing_options(sess, &eap_conf); sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb, &eap_conf); @@ -1711,8 +1714,10 @@ radius_server_init(struct radius_server_conf *conf) data->ipv6 = conf->ipv6; if (conf->pac_opaque_encr_key) { data->pac_opaque_encr_key = os_malloc(16); - os_memcpy(data->pac_opaque_encr_key, conf->pac_opaque_encr_key, - 16); + if (data->pac_opaque_encr_key) { + os_memcpy(data->pac_opaque_encr_key, + conf->pac_opaque_encr_key, 16); + } } if (conf->eap_fast_a_id) { data->eap_fast_a_id = os_malloc(conf->eap_fast_a_id_len); @@ -1743,6 +1748,7 @@ radius_server_init(struct radius_server_conf *conf) } data->erp = conf->erp; data->erp_domain = conf->erp_domain; + data->tls_session_lifetime = conf->tls_session_lifetime; if (conf->subscr_remediation_url) { data->subscr_remediation_url = @@ -2035,6 +2041,12 @@ static int radius_server_get_eap_user(void *ctx, const u8 *identity, sess->remediation = user->remediation; sess->macacl = user->macacl; } + + if (ret) { + RADIUS_DEBUG("%s: User-Name not found from user database", + __func__); + } + return ret; } @@ -2095,7 +2107,7 @@ static int radius_server_erp_add_key(void *ctx, struct eap_server_erp_key *erp) #endif /* CONFIG_ERP */ -static struct eapol_callbacks radius_server_eapol_cb = +static const struct eapol_callbacks radius_server_eapol_cb = { .get_eap_user = radius_server_get_eap_user, .get_eap_req_id_text = radius_server_get_eap_req_id_text, diff --git a/contrib/wpa/src/radius/radius_server.h b/contrib/wpa/src/radius/radius_server.h index ca4e38c..7a25802 100644 --- a/contrib/wpa/src/radius/radius_server.h +++ b/contrib/wpa/src/radius/radius_server.h @@ -170,6 +170,8 @@ struct radius_server_conf { const char *erp_domain; + unsigned int tls_session_lifetime; + /** * wps - Wi-Fi Protected Setup context * diff --git a/contrib/wpa/src/rsn_supp/tdls.c b/contrib/wpa/src/rsn_supp/tdls.c index c1d7749..722c20a 100644 --- a/contrib/wpa/src/rsn_supp/tdls.c +++ b/contrib/wpa/src/rsn_supp/tdls.c @@ -12,6 +12,7 @@ #include "utils/eloop.h" #include "utils/os.h" #include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" #include "crypto/sha256.h" #include "crypto/crypto.h" #include "crypto/aes_wrap.h" @@ -1577,9 +1578,7 @@ static int copy_supp_rates(const struct wpa_eapol_ie_parse *kde, static int copy_peer_ht_capab(const struct wpa_eapol_ie_parse *kde, struct wpa_tdls_peer *peer) { - if (!kde->ht_capabilities || - kde->ht_capabilities_len < - sizeof(struct ieee80211_ht_capabilities) ) { + if (!kde->ht_capabilities) { wpa_printf(MSG_DEBUG, "TDLS: No supported ht capabilities " "received"); return 0; @@ -1605,9 +1604,7 @@ static int copy_peer_ht_capab(const struct wpa_eapol_ie_parse *kde, static int copy_peer_vht_capab(const struct wpa_eapol_ie_parse *kde, struct wpa_tdls_peer *peer) { - if (!kde->vht_capabilities || - kde->vht_capabilities_len < - sizeof(struct ieee80211_vht_capabilities) ) { + if (!kde->vht_capabilities) { wpa_printf(MSG_DEBUG, "TDLS: No supported vht capabilities " "received"); return 0; @@ -2863,14 +2860,14 @@ void wpa_tdls_disassoc(struct wpa_sm *sm) } -static int wpa_tdls_prohibited(struct wpa_eapol_ie_parse *elems) +static int wpa_tdls_prohibited(struct ieee802_11_elems *elems) { /* bit 38 - TDLS Prohibited */ return !!(elems->ext_capab[2 + 4] & 0x40); } -static int wpa_tdls_chan_switch_prohibited(struct wpa_eapol_ie_parse *elems) +static int wpa_tdls_chan_switch_prohibited(struct ieee802_11_elems *elems) { /* bit 39 - TDLS Channel Switch Prohibited */ return !!(elems->ext_capab[2 + 4] & 0x80); @@ -2879,12 +2876,13 @@ static int wpa_tdls_chan_switch_prohibited(struct wpa_eapol_ie_parse *elems) void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len) { - struct wpa_eapol_ie_parse elems; + struct ieee802_11_elems elems; sm->tdls_prohibited = 0; sm->tdls_chan_switch_prohibited = 0; - if (ies == NULL || wpa_supplicant_parse_ies(ies, len, &elems) < 0 || + if (ies == NULL || + ieee802_11_parse_elems(ies, len, &elems, 0) == ParseFailed || elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5) return; @@ -2900,9 +2898,10 @@ void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len) void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len) { - struct wpa_eapol_ie_parse elems; + struct ieee802_11_elems elems; - if (ies == NULL || wpa_supplicant_parse_ies(ies, len, &elems) < 0 || + if (ies == NULL || + ieee802_11_parse_elems(ies, len, &elems, 0) == ParseFailed || elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5) return; diff --git a/contrib/wpa/src/rsn_supp/wpa.c b/contrib/wpa/src/rsn_supp/wpa.c index 127e246..d397ff1 100644 --- a/contrib/wpa/src/rsn_supp/wpa.c +++ b/contrib/wpa/src/rsn_supp/wpa.c @@ -249,6 +249,17 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, "RSN: the new PMK matches with the " "PMKID"); abort_cached = 0; + } else if (sa && !sm->cur_pmksa && pmkid) { + /* + * It looks like the authentication server + * derived mismatching MSK. This should not + * really happen, but bugs happen.. There is not + * much we can do here without knowing what + * exactly caused the server to misbehave. + */ + wpa_dbg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: PMKID mismatch - authentication server may have derived different MSK?!"); + return -1; } if (!sm->cur_pmksa) @@ -1281,8 +1292,8 @@ static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm, &gd->key_rsc_len, &gd->alg)) return -1; - wpa_hexdump(MSG_DEBUG, "RSN: received GTK in group key handshake", - ie.gtk, ie.gtk_len); + wpa_hexdump_key(MSG_DEBUG, "RSN: received GTK in group key handshake", + ie.gtk, ie.gtk_len); gd->keyidx = ie.gtk[0] & 0x3; gd->tx = wpa_supplicant_gtk_tx_bit_workaround(sm, !!(ie.gtk[0] & BIT(2))); @@ -1333,6 +1344,11 @@ static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm, gd->keyidx = (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >> WPA_KEY_INFO_KEY_INDEX_SHIFT; if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && sm->ptk.kek_len == 16) { +#ifdef CONFIG_NO_RC4 + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: RC4 not supported in the build"); + return -1; +#else /* CONFIG_NO_RC4 */ u8 ek[32]; if (key_data_len > sizeof(gd->gtk)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, @@ -1350,6 +1366,7 @@ static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm, return -1; } os_memset(ek, 0, sizeof(ek)); +#endif /* CONFIG_NO_RC4 */ } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { if (maxkeylen % 8) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, @@ -1564,6 +1581,11 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, /* Decrypt key data here so that this operation does not need * to be implemented separately for each message type. */ if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && sm->ptk.kek_len == 16) { +#ifdef CONFIG_NO_RC4 + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: RC4 not supported in the build"); + return -1; +#else /* CONFIG_NO_RC4 */ u8 ek[32]; os_memcpy(ek, key->key_iv, 16); os_memcpy(ek + 16, sm->ptk.kek, sm->ptk.kek_len); @@ -1574,6 +1596,7 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, return -1; } os_memset(ek, 0, sizeof(ek)); +#endif /* CONFIG_NO_RC4 */ } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || ver == WPA_KEY_INFO_TYPE_AES_128_CMAC || sm->key_mgmt == WPA_KEY_MGMT_OSEN || diff --git a/contrib/wpa/src/rsn_supp/wpa_ft.c b/contrib/wpa/src/rsn_supp/wpa_ft.c index 06dea05..205793e 100644 --- a/contrib/wpa/src/rsn_supp/wpa_ft.c +++ b/contrib/wpa/src/rsn_supp/wpa_ft.c @@ -168,9 +168,7 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, pos = (u8 *) (rsnie + 1); /* Group Suite Selector */ - if (sm->group_cipher != WPA_CIPHER_CCMP && - sm->group_cipher != WPA_CIPHER_GCMP && - sm->group_cipher != WPA_CIPHER_TKIP) { + if (!wpa_cipher_valid_group(sm->group_cipher)) { wpa_printf(MSG_WARNING, "FT: Invalid group cipher (%d)", sm->group_cipher); os_free(buf); diff --git a/contrib/wpa/src/rsn_supp/wpa_ie.c b/contrib/wpa/src/rsn_supp/wpa_ie.c index cb334df..0c37b35 100644 --- a/contrib/wpa/src/rsn_supp/wpa_ie.c +++ b/contrib/wpa/src/rsn_supp/wpa_ie.c @@ -30,6 +30,9 @@ int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len, { if (wpa_ie_len >= 1 && wpa_ie[0] == WLAN_EID_RSN) return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data); + if (wpa_ie_len >= 6 && wpa_ie[0] == WLAN_EID_VENDOR_SPECIFIC && + wpa_ie[1] >= 4 && WPA_GET_BE32(&wpa_ie[2]) == OSEN_IE_VENDOR_TYPE) + return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data); else return wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, data); } @@ -508,12 +511,14 @@ int wpa_supplicant_parse_ies(const u8 *buf, size_t len, ie->rsn_ie_len = pos[1] + 2; wpa_hexdump(MSG_DEBUG, "WPA: RSN IE in EAPOL-Key", ie->rsn_ie, ie->rsn_ie_len); - } else if (*pos == WLAN_EID_MOBILITY_DOMAIN) { + } else if (*pos == WLAN_EID_MOBILITY_DOMAIN && + pos[1] >= sizeof(struct rsn_mdie)) { ie->mdie = pos; ie->mdie_len = pos[1] + 2; wpa_hexdump(MSG_DEBUG, "WPA: MDIE in EAPOL-Key", ie->mdie, ie->mdie_len); - } else if (*pos == WLAN_EID_FAST_BSS_TRANSITION) { + } else if (*pos == WLAN_EID_FAST_BSS_TRANSITION && + pos[1] >= sizeof(struct rsn_ftie)) { ie->ftie = pos; ie->ftie_len = pos[1] + 2; wpa_hexdump(MSG_DEBUG, "WPA: FTIE in EAPOL-Key", @@ -548,15 +553,16 @@ int wpa_supplicant_parse_ies(const u8 *buf, size_t len, } else if (*pos == WLAN_EID_EXT_SUPP_RATES) { ie->ext_supp_rates = pos; ie->ext_supp_rates_len = pos[1] + 2; - } else if (*pos == WLAN_EID_HT_CAP) { + } else if (*pos == WLAN_EID_HT_CAP && + pos[1] >= sizeof(struct ieee80211_ht_capabilities)) { ie->ht_capabilities = pos + 2; - ie->ht_capabilities_len = pos[1]; } else if (*pos == WLAN_EID_VHT_AID) { if (pos[1] >= 2) ie->aid = WPA_GET_LE16(pos + 2) & 0x3fff; - } else if (*pos == WLAN_EID_VHT_CAP) { + } else if (*pos == WLAN_EID_VHT_CAP && + pos[1] >= sizeof(struct ieee80211_vht_capabilities)) + { ie->vht_capabilities = pos + 2; - ie->vht_capabilities_len = pos[1]; } else if (*pos == WLAN_EID_QOS && pos[1] >= 1) { ie->qosinfo = pos[2]; } else if (*pos == WLAN_EID_SUPPORTED_CHANNELS) { diff --git a/contrib/wpa/src/rsn_supp/wpa_ie.h b/contrib/wpa/src/rsn_supp/wpa_ie.h index 0fc42cc..fe95af0 100644 --- a/contrib/wpa/src/rsn_supp/wpa_ie.h +++ b/contrib/wpa/src/rsn_supp/wpa_ie.h @@ -50,9 +50,7 @@ struct wpa_eapol_ie_parse { const u8 *ext_supp_rates; size_t ext_supp_rates_len; const u8 *ht_capabilities; - size_t ht_capabilities_len; const u8 *vht_capabilities; - size_t vht_capabilities_len; const u8 *supp_channels; size_t supp_channels_len; const u8 *supp_oper_classes; diff --git a/contrib/wpa/src/tls/libtommath.c b/contrib/wpa/src/tls/libtommath.c index 3fb8fbe..8bc824f 100644 --- a/contrib/wpa/src/tls/libtommath.c +++ b/contrib/wpa/src/tls/libtommath.c @@ -1472,8 +1472,7 @@ static int mp_init_multi(mp_int *mp, ...) cur_arg = va_arg(clean_args, mp_int*); } va_end(clean_args); - res = MP_MEM; - break; + return MP_MEM; } n++; cur_arg = va_arg(args, mp_int*); @@ -1631,7 +1630,7 @@ static int mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d) } /* init our temps */ - if ((res = mp_init_multi(&ta, &tb, &tq, &q, NULL) != MP_OKAY)) { + if ((res = mp_init_multi(&ta, &tb, &tq, &q, NULL)) != MP_OKAY) { return res; } diff --git a/contrib/wpa/src/tls/tlsv1_client.c b/contrib/wpa/src/tls/tlsv1_client.c index facdd65..a6f0587 100644 --- a/contrib/wpa/src/tls/tlsv1_client.c +++ b/contrib/wpa/src/tls/tlsv1_client.c @@ -714,12 +714,12 @@ int tlsv1_client_hello_ext(struct tlsv1_client *conn, int ext_type, /** - * tlsv1_client_get_keys - Get master key and random data from TLS connection + * tlsv1_client_get_random - Get random data from TLS connection * @conn: TLSv1 client connection data from tlsv1_client_init() - * @keys: Structure of key/random data (filled on success) + * @keys: Structure of random data (filled on success) * Returns: 0 on success, -1 on failure */ -int tlsv1_client_get_keys(struct tlsv1_client *conn, struct tls_keys *keys) +int tlsv1_client_get_random(struct tlsv1_client *conn, struct tls_random *keys) { os_memset(keys, 0, sizeof(*keys)); if (conn->state == CLIENT_HELLO) @@ -731,8 +731,6 @@ int tlsv1_client_get_keys(struct tlsv1_client *conn, struct tls_keys *keys) if (conn->state != SERVER_HELLO) { keys->server_random = conn->server_random; keys->server_random_len = TLS_RANDOM_LEN; - keys->master_key = conn->master_secret; - keys->master_key_len = TLS_MASTER_SECRET_LEN; } return 0; diff --git a/contrib/wpa/src/tls/tlsv1_client.h b/contrib/wpa/src/tls/tlsv1_client.h index 8ec85f1..a4e25e9 100644 --- a/contrib/wpa/src/tls/tlsv1_client.h +++ b/contrib/wpa/src/tls/tlsv1_client.h @@ -36,7 +36,7 @@ int tlsv1_client_shutdown(struct tlsv1_client *conn); int tlsv1_client_resumed(struct tlsv1_client *conn); int tlsv1_client_hello_ext(struct tlsv1_client *conn, int ext_type, const u8 *data, size_t data_len); -int tlsv1_client_get_keys(struct tlsv1_client *conn, struct tls_keys *keys); +int tlsv1_client_get_random(struct tlsv1_client *conn, struct tls_random *data); 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, diff --git a/contrib/wpa/src/tls/tlsv1_server.c b/contrib/wpa/src/tls/tlsv1_server.c index 93ae488..ba47337 100644 --- a/contrib/wpa/src/tls/tlsv1_server.c +++ b/contrib/wpa/src/tls/tlsv1_server.c @@ -610,12 +610,12 @@ int tlsv1_server_resumed(struct tlsv1_server *conn) /** - * tlsv1_server_get_keys - Get master key and random data from TLS connection + * tlsv1_server_get_random - Get random data from TLS connection * @conn: TLSv1 server connection data from tlsv1_server_init() - * @keys: Structure of key/random data (filled on success) + * @keys: Structure of random data (filled on success) * Returns: 0 on success, -1 on failure */ -int tlsv1_server_get_keys(struct tlsv1_server *conn, struct tls_keys *keys) +int tlsv1_server_get_random(struct tlsv1_server *conn, struct tls_random *keys) { os_memset(keys, 0, sizeof(*keys)); if (conn->state == CLIENT_HELLO) @@ -627,8 +627,6 @@ int tlsv1_server_get_keys(struct tlsv1_server *conn, struct tls_keys *keys) if (conn->state != SERVER_HELLO) { keys->server_random = conn->server_random; keys->server_random_len = TLS_RANDOM_LEN; - keys->master_key = conn->master_secret; - keys->master_key_len = TLS_MASTER_SECRET_LEN; } return 0; diff --git a/contrib/wpa/src/tls/tlsv1_server.h b/contrib/wpa/src/tls/tlsv1_server.h index b2b28d1..10e7699 100644 --- a/contrib/wpa/src/tls/tlsv1_server.h +++ b/contrib/wpa/src/tls/tlsv1_server.h @@ -32,7 +32,7 @@ int tlsv1_server_get_cipher(struct tlsv1_server *conn, char *buf, size_t buflen); int tlsv1_server_shutdown(struct tlsv1_server *conn); int tlsv1_server_resumed(struct tlsv1_server *conn); -int tlsv1_server_get_keys(struct tlsv1_server *conn, struct tls_keys *keys); +int tlsv1_server_get_random(struct tlsv1_server *conn, struct tls_random *data); int tlsv1_server_get_keyblock_size(struct tlsv1_server *conn); int tlsv1_server_set_cipher_list(struct tlsv1_server *conn, u8 *ciphers); int tlsv1_server_set_verify(struct tlsv1_server *conn, int verify_peer); diff --git a/contrib/wpa/src/tls/x509v3.c b/contrib/wpa/src/tls/x509v3.c index 742af32..b51dfcd 100644 --- a/contrib/wpa/src/tls/x509v3.c +++ b/contrib/wpa/src/tls/x509v3.c @@ -1511,7 +1511,7 @@ struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len) if (pos + hdr.length < end) { wpa_hexdump(MSG_MSGDUMP, "X509: Ignoring extra data after DER " "encoded certificate", - pos + hdr.length, end - pos + hdr.length); + pos + hdr.length, end - (pos + hdr.length)); end = pos + hdr.length; } diff --git a/contrib/wpa/src/utils/browser-wpadebug.c b/contrib/wpa/src/utils/browser-wpadebug.c index 5fc40fa..59ba4d1 100644 --- a/contrib/wpa/src/utils/browser-wpadebug.c +++ b/contrib/wpa/src/utils/browser-wpadebug.c @@ -96,7 +96,7 @@ int hs20_web_browser(const char *url) if (pid == 0) { /* run the external command in the child process */ - char *argv[12]; + char *argv[14]; argv[0] = "browser-wpadebug"; argv[1] = "start"; @@ -109,7 +109,9 @@ int hs20_web_browser(const char *url) argv[8] = "-e"; argv[9] = "w1.fi.wpadebug.URL"; argv[10] = (void *) url; - argv[11] = NULL; + argv[11] = "--user"; + argv[12] = "-3"; /* USER_CURRENT_OR_SELF */ + argv[13] = 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 5fd795f..660e9fc 100644 --- a/contrib/wpa/src/utils/common.c +++ b/contrib/wpa/src/utils/common.c @@ -8,6 +8,7 @@ #include "includes.h" +#include "common/ieee802_11_defs.h" #include "common.h" @@ -277,6 +278,31 @@ int wpa_scnprintf(char *buf, size_t size, const char *fmt, ...) return ret; } + +int wpa_snprintf_hex_sep(char *buf, size_t buf_size, const u8 *data, size_t len, + char sep) +{ + size_t i; + char *pos = buf, *end = buf + buf_size; + int ret; + + if (buf_size == 0) + return 0; + + for (i = 0; i < len; i++) { + ret = os_snprintf(pos, end - pos, "%02x%c", + data[i], sep); + if (os_snprintf_error(end - pos, ret)) { + end[-1] = '\0'; + return pos - buf; + } + pos += ret; + } + pos[-1] = '\0'; + return pos - buf; +} + + static inline int _wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len, int uppercase) { @@ -584,7 +610,7 @@ size_t printf_decode(u8 *buf, size_t maxlen, const char *str) */ const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len) { - static char ssid_txt[32 * 4 + 1]; + static char ssid_txt[SSID_MAX_LEN * 4 + 1]; if (ssid == NULL) { ssid_txt[0] = '\0'; @@ -947,6 +973,48 @@ int random_mac_addr_keep_oui(u8 *addr) /** + * cstr_token - Get next token from const char string + * @str: a constant string to tokenize + * @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. + * Returns: a pointer to token position in str or NULL + * + * This function is similar to str_token, but it can be used with both + * char and const char strings. Differences: + * - The str buffer remains unmodified + * - The returned token is not a NULL terminated string, but a token + * position in str buffer. If a return value is not NULL a size + * of the returned token could be calculated as (last - token). + */ +const char * cstr_token(const char *str, const char *delim, const char **last) +{ + const char *end, *token = str; + + if (!str || !delim || !last) + return NULL; + + if (*last) + token = *last; + + while (*token && os_strchr(delim, *token)) + token++; + + if (!*token) + return NULL; + + end = token + 1; + + while (*end && !os_strchr(delim, *end)) + end++; + + *last = end; + return token; +} + + +/** * str_token - Get next token from a string * @buf: String to tokenize. Note that the string might be modified. * @delim: String of delimiters @@ -956,25 +1024,12 @@ int random_mac_addr_keep_oui(u8 *addr) */ char * str_token(char *str, const char *delim, char **context) { - char *end, *pos = str; - - if (*context) - pos = *context; - - while (*pos && os_strchr(delim, *pos)) - pos++; - if (!*pos) - return NULL; - - end = pos + 1; - while (*end && !os_strchr(delim, *end)) - end++; + char *token = (char *) cstr_token(str, delim, (const char **) context); - if (*end) - *end++ = '\0'; + if (token && **context) + *(*context)++ = '\0'; - *context = end; - return pos; + return token; } @@ -1062,3 +1117,9 @@ size_t utf8_escape(const char *inp, size_t in_size, return res_size; } + + +int is_ctrl_char(char c) +{ + return c > 0 && c < 32; +} diff --git a/contrib/wpa/src/utils/common.h b/contrib/wpa/src/utils/common.h index 576e8e7..0b9cc3d 100644 --- a/contrib/wpa/src/utils/common.h +++ b/contrib/wpa/src/utils/common.h @@ -53,16 +53,6 @@ static inline unsigned int bswap_32(unsigned int v) } #endif /* __APPLE__ */ -#ifdef CONFIG_TI_COMPILER -#define __BIG_ENDIAN 4321 -#define __LITTLE_ENDIAN 1234 -#ifdef __big_endian__ -#define __BYTE_ORDER __BIG_ENDIAN -#else -#define __BYTE_ORDER __LITTLE_ENDIAN -#endif -#endif /* CONFIG_TI_COMPILER */ - #ifdef CONFIG_NATIVE_WINDOWS #include <winsock.h> @@ -110,22 +100,6 @@ typedef INT8 s8; #define WPA_TYPES_DEFINED #endif /* __vxworks */ -#ifdef CONFIG_TI_COMPILER -#ifdef _LLONG_AVAILABLE -typedef unsigned long long u64; -#else -/* - * TODO: 64-bit variable not available. Using long as a workaround to test the - * build, but this will likely not work for all operations. - */ -typedef unsigned long u64; -#endif -typedef unsigned int u32; -typedef unsigned short u16; -typedef unsigned char u8; -#define WPA_TYPES_DEFINED -#endif /* CONFIG_TI_COMPILER */ - #ifndef WPA_TYPES_DEFINED #ifdef CONFIG_USE_INTTYPES_H #include <inttypes.h> @@ -262,7 +236,7 @@ static inline void WPA_PUT_BE24(u8 *a, u32 val) static inline u32 WPA_GET_BE32(const u8 *a) { - return (a[0] << 24) | (a[1] << 16) | (a[2] << 8) | a[3]; + return ((u32) a[0] << 24) | (a[1] << 16) | (a[2] << 8) | a[3]; } static inline void WPA_PUT_BE32(u8 *a, u32 val) @@ -275,7 +249,7 @@ static inline void WPA_PUT_BE32(u8 *a, u32 val) static inline u32 WPA_GET_LE32(const u8 *a) { - return (a[3] << 24) | (a[2] << 16) | (a[1] << 8) | a[0]; + return ((u32) a[3] << 24) | (a[2] << 16) | (a[1] << 8) | a[0]; } static inline void WPA_PUT_LE32(u8 *a, u32 val) @@ -433,7 +407,7 @@ void perror(const char *s); #endif #ifndef BIT -#define BIT(x) (1 << (x)) +#define BIT(x) (1U << (x)) #endif /* @@ -480,6 +454,8 @@ int hexstr2bin(const char *hex, u8 *buf, size_t len); void inc_byte_array(u8 *counter, size_t len); void wpa_get_ntp_timestamp(u8 *buf); int wpa_scnprintf(char *buf, size_t size, const char *fmt, ...); +int wpa_snprintf_hex_sep(char *buf, size_t buf_size, const u8 *data, size_t len, + char sep); int wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len); int wpa_snprintf_hex_uppercase(char *buf, size_t buf_size, const u8 *data, size_t len); @@ -516,6 +492,11 @@ static inline int is_broadcast_ether_addr(const u8 *a) return (a[0] & a[1] & a[2] & a[3] & a[4] & a[5]) == 0xff; } +static inline int is_multicast_ether_addr(const u8 *a) +{ + return a[0] & 0x01; +} + #define broadcast_ether_addr (const u8 *) "\xff\xff\xff\xff\xff\xff" #include "wpa_debug.h" @@ -547,11 +528,13 @@ void bin_clear_free(void *bin, size_t len); int random_mac_addr(u8 *addr); int random_mac_addr_keep_oui(u8 *addr); +const char * cstr_token(const char *str, const char *delim, const char **last); char * str_token(char *str, const char *delim, char **context); size_t utf8_escape(const char *inp, size_t in_size, char *outp, size_t out_size); size_t utf8_unescape(const char *inp, size_t in_size, char *outp, size_t out_size); +int is_ctrl_char(char c); /* diff --git a/contrib/wpa/src/utils/eloop.c b/contrib/wpa/src/utils/eloop.c index 4a565eb..8647229 100644 --- a/contrib/wpa/src/utils/eloop.c +++ b/contrib/wpa/src/utils/eloop.c @@ -61,11 +61,8 @@ struct eloop_signal { struct eloop_sock_table { int count; struct eloop_sock *table; -#ifdef CONFIG_ELOOP_EPOLL eloop_event_type type; -#else /* CONFIG_ELOOP_EPOLL */ int changed; -#endif /* CONFIG_ELOOP_EPOLL */ }; struct eloop_data { @@ -256,9 +253,7 @@ static int eloop_sock_table_add_sock(struct eloop_sock_table *table, table->table = tmp; eloop.max_sock = new_max_sock; eloop.count++; -#ifndef CONFIG_ELOOP_EPOLL table->changed = 1; -#endif /* CONFIG_ELOOP_EPOLL */ eloop_trace_sock_add_ref(table); #ifdef CONFIG_ELOOP_EPOLL @@ -314,9 +309,7 @@ static void eloop_sock_table_remove_sock(struct eloop_sock_table *table, } table->count--; eloop.count--; -#ifndef CONFIG_ELOOP_EPOLL table->changed = 1; -#endif /* CONFIG_ELOOP_EPOLL */ eloop_trace_sock_add_ref(table); #ifdef CONFIG_ELOOP_EPOLL if (epoll_ctl(eloop.epollfd, EPOLL_CTL_DEL, sock, NULL) < 0) { @@ -523,6 +516,10 @@ static void eloop_sock_table_dispatch(struct epoll_event *events, int nfds) continue; table->handler(table->sock, table->eloop_data, table->user_data); + if (eloop.readers.changed || + eloop.writers.changed || + eloop.exceptions.changed) + break; } } #endif /* CONFIG_ELOOP_EPOLL */ @@ -923,6 +920,20 @@ void eloop_run(void) (!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 || eloop.writers.count > 0 || eloop.exceptions.count > 0)) { struct eloop_timeout *timeout; + + if (eloop.pending_terminate) { + /* + * This may happen in some corner cases where a signal + * is received during a blocking operation. We need to + * process the pending signals and exit if requested to + * avoid hitting the SIGALRM limit if the blocking + * operation took more than two seconds. + */ + eloop_process_pending_signals(); + if (eloop.terminate) + break; + } + timeout = dl_list_first(&eloop.timeout, struct eloop_timeout, list); if (timeout) { @@ -977,6 +988,11 @@ void eloop_run(void) , strerror(errno)); goto out; } + + eloop.readers.changed = 0; + eloop.writers.changed = 0; + eloop.exceptions.changed = 0; + eloop_process_pending_signals(); /* check if some registered timeouts have occurred */ @@ -998,6 +1014,19 @@ void eloop_run(void) if (res <= 0) continue; + if (eloop.readers.changed || + eloop.writers.changed || + eloop.exceptions.changed) { + /* + * Sockets may have been closed and reopened with the + * same FD in the signal or timeout handlers, so we + * must skip the previous results and check again + * whether any of the currently registered sockets have + * events. + */ + continue; + } + #ifdef CONFIG_ELOOP_POLL eloop_sock_table_dispatch(&eloop.readers, &eloop.writers, &eloop.exceptions, eloop.pollfds_map, @@ -1073,7 +1102,7 @@ void eloop_destroy(void) int eloop_terminated(void) { - return eloop.terminate; + return eloop.terminate || eloop.pending_terminate; } diff --git a/contrib/wpa/src/utils/http_curl.c b/contrib/wpa/src/utils/http_curl.c index b38cf79..653eb54 100644 --- a/contrib/wpa/src/utils/http_curl.c +++ b/contrib/wpa/src/utils/http_curl.c @@ -855,8 +855,10 @@ static int validate_server_cert(struct http_ctx *ctx, X509 *cert) struct http_cert hcert; int ret; - if (ctx->cert_cb == NULL) + if (ctx->cert_cb == NULL) { + wpa_printf(MSG_DEBUG, "%s: no cert_cb configured", __func__); return 0; + } if (0) { BIO *out; @@ -950,7 +952,8 @@ static int curl_cb_ssl_verify(int preverify_ok, X509_STORE_CTX *x509_ctx) ssl_ctx = ssl->ctx; ctx = SSL_CTX_get_app_data(ssl_ctx); - wpa_printf(MSG_DEBUG, "curl_cb_ssl_verify"); + wpa_printf(MSG_DEBUG, "curl_cb_ssl_verify, preverify_ok: %d", + preverify_ok); err = X509_STORE_CTX_get_error(x509_ctx); err_str = X509_verify_cert_error_string(err); @@ -1249,9 +1252,14 @@ static CURL * setup_curl_post(struct http_ctx *ctx, const char *address, const char *client_key) { CURL *curl; +#ifdef EAP_TLS_OPENSSL + const char *extra = " tls=openssl"; +#else /* EAP_TLS_OPENSSL */ + const char *extra = ""; +#endif /* EAP_TLS_OPENSSL */ wpa_printf(MSG_DEBUG, "Start HTTP client: address=%s ca_fname=%s " - "username=%s", address, ca_fname, username); + "username=%s%s", address, ca_fname, username, extra); curl = curl_easy_init(); if (curl == NULL) diff --git a/contrib/wpa/src/utils/includes.h b/contrib/wpa/src/utils/includes.h index 6c6ec87..75513fc 100644 --- a/contrib/wpa/src/utils/includes.h +++ b/contrib/wpa/src/utils/includes.h @@ -17,26 +17,22 @@ #include "build_config.h" #include <stdlib.h> +#include <stddef.h> #include <stdio.h> #include <stdarg.h> #include <string.h> #ifndef _WIN32_WCE -#ifndef CONFIG_TI_COMPILER #include <signal.h> #include <sys/types.h> -#endif /* CONFIG_TI_COMPILER */ #include <errno.h> #endif /* _WIN32_WCE */ #include <ctype.h> -#ifndef CONFIG_TI_COMPILER #ifndef _MSC_VER #include <unistd.h> #endif /* _MSC_VER */ -#endif /* CONFIG_TI_COMPILER */ #ifndef CONFIG_NATIVE_WINDOWS -#ifndef CONFIG_TI_COMPILER #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> @@ -44,7 +40,6 @@ #include <sys/uio.h> #include <sys/time.h> #endif /* __vxworks */ -#endif /* CONFIG_TI_COMPILER */ #endif /* CONFIG_NATIVE_WINDOWS */ #endif /* INCLUDES_H */ diff --git a/contrib/wpa/src/utils/os.h b/contrib/wpa/src/utils/os.h index 77250d6..9e496fb 100644 --- a/contrib/wpa/src/utils/os.h +++ b/contrib/wpa/src/utils/os.h @@ -247,6 +247,13 @@ char * os_readfile(const char *name, size_t *len); int os_file_exists(const char *fname); /** + * os_fdatasync - Sync a file's (for a given stream) state with storage device + * @stream: the stream to be flushed + * Returns: 0 if the operation succeeded or -1 on failure + */ +int os_fdatasync(FILE *stream); + +/** * os_zalloc - Allocate and zero memory * @size: Number of bytes to allocate * Returns: Pointer to allocated and zeroed memory or %NULL on failure @@ -646,4 +653,12 @@ int os_exec(const char *program, const char *arg, int wait_completion); #define strcpy OS_DO_NOT_USE_strcpy #endif /* OS_REJECT_C_LIB_FUNCTIONS */ + +#if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS) +#define TEST_FAIL() testing_test_fail() +int testing_test_fail(void); +#else +#define TEST_FAIL() 0 +#endif + #endif /* OS_H */ diff --git a/contrib/wpa/src/utils/os_internal.c b/contrib/wpa/src/utils/os_internal.c index 77733ad..ed6eb3c 100644 --- a/contrib/wpa/src/utils/os_internal.c +++ b/contrib/wpa/src/utils/os_internal.c @@ -243,6 +243,12 @@ char * os_readfile(const char *name, size_t *len) } +int os_fdatasync(FILE *stream) +{ + return 0; +} + + void * os_zalloc(size_t size) { void *n = os_malloc(size); diff --git a/contrib/wpa/src/utils/os_none.c b/contrib/wpa/src/utils/os_none.c index 83fe025..0c3214d 100644 --- a/contrib/wpa/src/utils/os_none.c +++ b/contrib/wpa/src/utils/os_none.c @@ -102,6 +102,12 @@ char * os_readfile(const char *name, size_t *len) } +int os_fdatasync(FILE *stream) +{ + return 0; +} + + void * os_zalloc(size_t size) { return NULL; diff --git a/contrib/wpa/src/utils/os_unix.c b/contrib/wpa/src/utils/os_unix.c index 34cb87a..488995c 100644 --- a/contrib/wpa/src/utils/os_unix.c +++ b/contrib/wpa/src/utils/os_unix.c @@ -17,6 +17,12 @@ #include <private/android_filesystem_config.h> #endif /* ANDROID */ +#ifdef __MACH__ +#include <CoreServices/CoreServices.h> +#include <mach/mach.h> +#include <mach/mach_time.h> +#endif /* __MACH__ */ + #include "os.h" #include "common.h" @@ -36,7 +42,7 @@ struct os_alloc_trace { struct dl_list list; size_t len; WPA_TRACE_INFO -}; +} __attribute__((aligned(16))); #endif /* WPA_TRACE */ @@ -63,6 +69,7 @@ int os_get_time(struct os_time *t) int os_get_reltime(struct os_reltime *t) { +#ifndef __MACH__ #if defined(CLOCK_BOOTTIME) static clockid_t clock_id = CLOCK_BOOTTIME; #elif defined(CLOCK_MONOTONIC) @@ -95,6 +102,23 @@ int os_get_reltime(struct os_reltime *t) return -1; } } +#else /* __MACH__ */ + uint64_t abstime, nano; + static mach_timebase_info_data_t info = { 0, 0 }; + + if (!info.denom) { + if (mach_timebase_info(&info) != KERN_SUCCESS) + return -1; + } + + abstime = mach_absolute_time(); + nano = (abstime * info.numer) / info.denom; + + t->sec = nano / NSEC_PER_SEC; + t->usec = (nano - (((uint64_t) t->sec) * NSEC_PER_SEC)) / NSEC_PER_USEC; + + return 0; +#endif /* __MACH__ */ } @@ -253,6 +277,9 @@ int os_get_random(unsigned char *buf, size_t len) FILE *f; size_t rc; + if (TEST_FAIL()) + return -1; + f = fopen("/dev/urandom", "rb"); if (f == NULL) { printf("Could not open /dev/urandom.\n"); @@ -442,6 +469,25 @@ int os_file_exists(const char *fname) } +int os_fdatasync(FILE *stream) +{ + if (!fflush(stream)) { +#ifdef __linux__ + return fdatasync(fileno(stream)); +#else /* !__linux__ */ +#ifdef F_FULLFSYNC + /* OS X does not implement fdatasync(). */ + return fcntl(fileno(stream), F_FULLFSYNC); +#else /* F_FULLFSYNC */ + return fsync(fileno(stream)); +#endif /* F_FULLFSYNC */ +#endif /* __linux__ */ + } + + return -1; +} + + #ifndef WPA_TRACE void * os_zalloc(size_t size) { @@ -575,6 +621,78 @@ static int testing_fail_alloc(void) return 0; } + +char wpa_trace_test_fail_func[256] = { 0 }; +unsigned int wpa_trace_test_fail_after; + +int testing_test_fail(void) +{ + const char *func[WPA_TRACE_LEN]; + size_t i, res, len; + char *pos, *next; + int match; + + if (!wpa_trace_test_fail_after) + return 0; + + res = wpa_trace_calling_func(func, WPA_TRACE_LEN); + i = 0; + if (i < res && os_strcmp(func[i], __func__) == 0) + i++; + + pos = wpa_trace_test_fail_func; + + match = 0; + while (i < res) { + int allow_skip = 1; + int maybe = 0; + + if (*pos == '=') { + allow_skip = 0; + pos++; + } else if (*pos == '?') { + maybe = 1; + pos++; + } + next = os_strchr(pos, ';'); + if (next) + len = next - pos; + else + len = os_strlen(pos); + if (os_memcmp(pos, func[i], len) != 0) { + if (maybe && next) { + pos = next + 1; + continue; + } + if (allow_skip) { + i++; + continue; + } + return 0; + } + if (!next) { + match = 1; + break; + } + pos = next + 1; + i++; + } + if (!match) + return 0; + + wpa_trace_test_fail_after--; + if (wpa_trace_test_fail_after == 0) { + wpa_printf(MSG_INFO, "TESTING: fail at %s", + wpa_trace_test_fail_func); + for (i = 0; i < res; i++) + wpa_printf(MSG_INFO, "backtrace[%d] = %s", + (int) i, func[i]); + return 1; + } + + return 0; +} + #else static inline int testing_fail_alloc(void) diff --git a/contrib/wpa/src/utils/os_win32.c b/contrib/wpa/src/utils/os_win32.c index 296ea13..dea27b9 100644 --- a/contrib/wpa/src/utils/os_win32.c +++ b/contrib/wpa/src/utils/os_win32.c @@ -216,6 +216,24 @@ char * os_readfile(const char *name, size_t *len) } +int os_fdatasync(FILE *stream) +{ + HANDLE h; + + if (stream == NULL) + return -1; + + h = (HANDLE) _get_osfhandle(_fileno(stream)); + if (h == INVALID_HANDLE_VALUE) + return -1; + + if (!FlushFileBuffers(h)) + return -1; + + return 0; +} + + void * os_zalloc(size_t size) { return calloc(1, size); diff --git a/contrib/wpa/src/utils/radiotap.c b/contrib/wpa/src/utils/radiotap.c index f8f815a..c9a5023 100644 --- a/contrib/wpa/src/utils/radiotap.c +++ b/contrib/wpa/src/utils/radiotap.c @@ -123,13 +123,13 @@ int ieee80211_radiotap_iterator_init( /* find payload start allowing for extended bitmap(s) */ - if (iterator->_bitmap_shifter & (1<<IEEE80211_RADIOTAP_EXT)) { + if (iterator->_bitmap_shifter & BIT(IEEE80211_RADIOTAP_EXT)) { if ((unsigned long)iterator->_arg - (unsigned long)iterator->_rtheader + sizeof(uint32_t) > (unsigned long)iterator->_max_length) return -EINVAL; while (get_unaligned_le32(iterator->_arg) & - (1 << IEEE80211_RADIOTAP_EXT)) { + BIT(IEEE80211_RADIOTAP_EXT)) { iterator->_arg += sizeof(uint32_t); /* diff --git a/contrib/wpa/src/utils/utils_module_tests.c b/contrib/wpa/src/utils/utils_module_tests.c index 4b97dad..41511b9 100644 --- a/contrib/wpa/src/utils/utils_module_tests.c +++ b/contrib/wpa/src/utils/utils_module_tests.c @@ -9,10 +9,13 @@ #include "utils/includes.h" #include "utils/common.h" +#include "common/ieee802_11_defs.h" #include "utils/bitfield.h" #include "utils/ext_password.h" #include "utils/trace.h" #include "utils/base64.h" +#include "utils/ip_addr.h" +#include "utils/eloop.h" struct printf_test_data { @@ -43,6 +46,7 @@ static int printf_encode_decode_tests(void) char buf[100]; u8 bin[100]; int errors = 0; + int array[10]; wpa_printf(MSG_INFO, "printf encode/decode tests"); @@ -91,9 +95,24 @@ static int printf_encode_decode_tests(void) if (printf_decode(bin, 3, "\\xa") != 1 || bin[0] != 10) errors++; + if (printf_decode(bin, 3, "\\xq") != 1 || bin[0] != 'q') + errors++; + if (printf_decode(bin, 3, "\\a") != 1 || bin[0] != 'a') errors++; + array[0] = 10; + array[1] = 10; + array[2] = 5; + array[3] = 10; + array[4] = 5; + array[5] = 0; + if (int_array_len(array) != 5) + errors++; + int_array_sort_unique(array); + if (int_array_len(array) != 2) + errors++; + if (errors) { wpa_printf(MSG_ERROR, "%d printf test(s) failed", errors); return -1; @@ -335,11 +354,14 @@ static int base64_tests(void) static int common_tests(void) { - char buf[3]; + char buf[3], longbuf[100]; u8 addr[ETH_ALEN] = { 1, 2, 3, 4, 5, 6 }; u8 bin[3]; int errors = 0; struct wpa_freq_range_list ranges; + size_t len; + const char *txt; + u8 ssid[255]; wpa_printf(MSG_INFO, "common tests"); @@ -395,6 +417,21 @@ static int common_tests(void) if (utf8_escape("a", 0, buf, sizeof(buf)) != 1 || buf[0] != 'a') errors++; + os_memset(ssid, 0, sizeof(ssid)); + txt = wpa_ssid_txt(ssid, sizeof(ssid)); + len = os_strlen(txt); + /* Verify that SSID_MAX_LEN * 4 buffer limit is enforced. */ + if (len != SSID_MAX_LEN * 4) { + wpa_printf(MSG_ERROR, + "Unexpected wpa_ssid_txt() result with too long SSID"); + errors++; + } + + if (wpa_snprintf_hex_sep(longbuf, 0, addr, ETH_ALEN, '-') != 0 || + wpa_snprintf_hex_sep(longbuf, 5, addr, ETH_ALEN, '-') != 3 || + os_strcmp(longbuf, "01-0") != 0) + errors++; + if (errors) { wpa_printf(MSG_ERROR, "%d common test(s) failed", errors); return -1; @@ -404,6 +441,403 @@ static int common_tests(void) } +static int os_tests(void) +{ + int errors = 0; + void *ptr; + os_time_t t; + + wpa_printf(MSG_INFO, "os tests"); + + ptr = os_calloc((size_t) -1, (size_t) -1); + if (ptr) { + errors++; + os_free(ptr); + } + ptr = os_calloc((size_t) 2, (size_t) -1); + if (ptr) { + errors++; + os_free(ptr); + } + ptr = os_calloc((size_t) -1, (size_t) 2); + if (ptr) { + errors++; + os_free(ptr); + } + + ptr = os_realloc_array(NULL, (size_t) -1, (size_t) -1); + if (ptr) { + errors++; + os_free(ptr); + } + + os_sleep(1, 1); + + if (os_mktime(1969, 1, 1, 1, 1, 1, &t) == 0 || + os_mktime(1971, 0, 1, 1, 1, 1, &t) == 0 || + os_mktime(1971, 13, 1, 1, 1, 1, &t) == 0 || + os_mktime(1971, 1, 0, 1, 1, 1, &t) == 0 || + os_mktime(1971, 1, 32, 1, 1, 1, &t) == 0 || + os_mktime(1971, 1, 1, -1, 1, 1, &t) == 0 || + os_mktime(1971, 1, 1, 24, 1, 1, &t) == 0 || + os_mktime(1971, 1, 1, 1, -1, 1, &t) == 0 || + os_mktime(1971, 1, 1, 1, 60, 1, &t) == 0 || + os_mktime(1971, 1, 1, 1, 1, -1, &t) == 0 || + os_mktime(1971, 1, 1, 1, 1, 61, &t) == 0 || + os_mktime(1971, 1, 1, 1, 1, 1, &t) != 0 || + os_mktime(2020, 1, 2, 3, 4, 5, &t) != 0 || + os_mktime(2015, 12, 31, 23, 59, 59, &t) != 0) + errors++; + + if (os_setenv("hwsim_test_env", "test value", 0) != 0 || + os_setenv("hwsim_test_env", "test value 2", 1) != 0 || + os_unsetenv("hwsim_test_env") != 0) + errors++; + + if (os_file_exists("/this-file-does-not-exists-hwsim") != 0) + errors++; + + if (errors) { + wpa_printf(MSG_ERROR, "%d os test(s) failed", errors); + return -1; + } + + return 0; +} + + +static int wpabuf_tests(void) +{ + int errors = 0; + void *ptr; + struct wpabuf *buf; + + wpa_printf(MSG_INFO, "wpabuf tests"); + + ptr = os_malloc(100); + if (ptr) { + buf = wpabuf_alloc_ext_data(ptr, 100); + if (buf) { + if (wpabuf_resize(&buf, 100) < 0) + errors++; + else + wpabuf_put(buf, 100); + wpabuf_free(buf); + } else { + errors++; + os_free(ptr); + } + } else { + errors++; + } + + buf = wpabuf_alloc(100); + if (buf) { + struct wpabuf *buf2; + + wpabuf_put(buf, 100); + if (wpabuf_resize(&buf, 100) < 0) + errors++; + else + wpabuf_put(buf, 100); + buf2 = wpabuf_concat(buf, NULL); + if (buf2 != buf) + errors++; + wpabuf_free(buf2); + } else { + errors++; + } + + buf = NULL; + buf = wpabuf_zeropad(buf, 10); + if (buf != NULL) + errors++; + + if (errors) { + wpa_printf(MSG_ERROR, "%d wpabuf test(s) failed", errors); + return -1; + } + + return 0; +} + + +static int ip_addr_tests(void) +{ + int errors = 0; + struct hostapd_ip_addr addr; + char buf[100]; + + wpa_printf(MSG_INFO, "ip_addr tests"); + + if (hostapd_parse_ip_addr("1.2.3.4", &addr) != 0 || + addr.af != AF_INET || + hostapd_ip_txt(NULL, buf, sizeof(buf)) != NULL || + hostapd_ip_txt(&addr, buf, 1) != buf || buf[0] != '\0' || + hostapd_ip_txt(&addr, buf, 0) != NULL || + hostapd_ip_txt(&addr, buf, sizeof(buf)) != buf) + errors++; + + if (hostapd_parse_ip_addr("::", &addr) != 0 || + addr.af != AF_INET6 || + hostapd_ip_txt(&addr, buf, 1) != buf || buf[0] != '\0' || + hostapd_ip_txt(&addr, buf, sizeof(buf)) != buf) + errors++; + + if (errors) { + wpa_printf(MSG_ERROR, "%d ip_addr test(s) failed", errors); + return -1; + } + + return 0; +} + + +struct test_eloop { + unsigned int magic; + int close_in_timeout; + int pipefd1[2]; + int pipefd2[2]; +}; + + +static void eloop_tests_start(int close_in_timeout); + + +static void eloop_test_read_2(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct test_eloop *t = eloop_ctx; + ssize_t res; + char buf[10]; + + wpa_printf(MSG_INFO, "%s: sock=%d", __func__, sock); + + if (t->magic != 0x12345678) { + wpa_printf(MSG_INFO, "%s: unexpected magic 0x%x", + __func__, t->magic); + } + + if (t->pipefd2[0] != sock) { + wpa_printf(MSG_INFO, "%s: unexpected sock %d != %d", + __func__, sock, t->pipefd2[0]); + } + + res = read(sock, buf, sizeof(buf)); + wpa_printf(MSG_INFO, "%s: sock=%d --> res=%d", + __func__, sock, (int) res); +} + + +static void eloop_test_read_2_wrong(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct test_eloop *t = eloop_ctx; + + wpa_printf(MSG_INFO, "%s: sock=%d", __func__, sock); + + if (t->magic != 0x12345678) { + wpa_printf(MSG_INFO, "%s: unexpected magic 0x%x", + __func__, t->magic); + } + + if (t->pipefd2[0] != sock) { + wpa_printf(MSG_INFO, "%s: unexpected sock %d != %d", + __func__, sock, t->pipefd2[0]); + } + + /* + * This is expected to block due to the original socket with data having + * been closed and no new data having been written to the new socket + * with the same fd. To avoid blocking the process during test, skip the + * read here. + */ + wpa_printf(MSG_ERROR, "%s: FAIL - should not have called this function", + __func__); +} + + +static void reopen_pipefd2(struct test_eloop *t) +{ + if (t->pipefd2[0] < 0) { + wpa_printf(MSG_INFO, "pipefd2 had been closed"); + } else { + int res; + + wpa_printf(MSG_INFO, "close pipefd2"); + eloop_unregister_read_sock(t->pipefd2[0]); + close(t->pipefd2[0]); + t->pipefd2[0] = -1; + close(t->pipefd2[1]); + t->pipefd2[1] = -1; + + res = pipe(t->pipefd2); + if (res < 0) { + wpa_printf(MSG_INFO, "pipe: %s", strerror(errno)); + t->pipefd2[0] = -1; + t->pipefd2[1] = -1; + return; + } + + wpa_printf(MSG_INFO, + "re-register pipefd2 with new sockets %d,%d", + t->pipefd2[0], t->pipefd2[1]); + eloop_register_read_sock(t->pipefd2[0], eloop_test_read_2_wrong, + t, NULL); + } +} + + +static void eloop_test_read_1(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct test_eloop *t = eloop_ctx; + ssize_t res; + char buf[10]; + + wpa_printf(MSG_INFO, "%s: sock=%d", __func__, sock); + + if (t->magic != 0x12345678) { + wpa_printf(MSG_INFO, "%s: unexpected magic 0x%x", + __func__, t->magic); + } + + if (t->pipefd1[0] != sock) { + wpa_printf(MSG_INFO, "%s: unexpected sock %d != %d", + __func__, sock, t->pipefd1[0]); + } + + res = read(sock, buf, sizeof(buf)); + wpa_printf(MSG_INFO, "%s: sock=%d --> res=%d", + __func__, sock, (int) res); + + if (!t->close_in_timeout) + reopen_pipefd2(t); +} + + +static void eloop_test_cb(void *eloop_data, void *user_ctx) +{ + struct test_eloop *t = eloop_data; + + wpa_printf(MSG_INFO, "%s", __func__); + + if (t->magic != 0x12345678) { + wpa_printf(MSG_INFO, "%s: unexpected magic 0x%x", + __func__, t->magic); + } + + if (t->close_in_timeout) + reopen_pipefd2(t); +} + + +static void eloop_test_timeout(void *eloop_data, void *user_ctx) +{ + struct test_eloop *t = eloop_data; + int next_run = 0; + + wpa_printf(MSG_INFO, "%s", __func__); + + if (t->magic != 0x12345678) { + wpa_printf(MSG_INFO, "%s: unexpected magic 0x%x", + __func__, t->magic); + } + + if (t->pipefd1[0] >= 0) { + wpa_printf(MSG_INFO, "pipefd1 had not been closed"); + eloop_unregister_read_sock(t->pipefd1[0]); + close(t->pipefd1[0]); + t->pipefd1[0] = -1; + close(t->pipefd1[1]); + t->pipefd1[1] = -1; + } + + if (t->pipefd2[0] >= 0) { + wpa_printf(MSG_INFO, "pipefd2 had not been closed"); + eloop_unregister_read_sock(t->pipefd2[0]); + close(t->pipefd2[0]); + t->pipefd2[0] = -1; + close(t->pipefd2[1]); + t->pipefd2[1] = -1; + } + + next_run = t->close_in_timeout; + t->magic = 0; + wpa_printf(MSG_INFO, "%s - free(%p)", __func__, t); + os_free(t); + + if (next_run) + eloop_tests_start(0); +} + + +static void eloop_tests_start(int close_in_timeout) +{ + struct test_eloop *t; + int res; + + t = os_zalloc(sizeof(*t)); + if (!t) + return; + t->magic = 0x12345678; + t->close_in_timeout = close_in_timeout; + + wpa_printf(MSG_INFO, "starting eloop tests (%p) (close_in_timeout=%d)", + t, close_in_timeout); + + res = pipe(t->pipefd1); + if (res < 0) { + wpa_printf(MSG_INFO, "pipe: %s", strerror(errno)); + os_free(t); + return; + } + + res = pipe(t->pipefd2); + if (res < 0) { + wpa_printf(MSG_INFO, "pipe: %s", strerror(errno)); + close(t->pipefd1[0]); + close(t->pipefd1[1]); + os_free(t); + return; + } + + wpa_printf(MSG_INFO, "pipe fds: %d,%d %d,%d", + t->pipefd1[0], t->pipefd1[1], + t->pipefd2[0], t->pipefd2[1]); + + eloop_register_read_sock(t->pipefd1[0], eloop_test_read_1, t, NULL); + eloop_register_read_sock(t->pipefd2[0], eloop_test_read_2, t, NULL); + eloop_register_timeout(0, 0, eloop_test_cb, t, NULL); + eloop_register_timeout(0, 200000, eloop_test_timeout, t, NULL); + + if (write(t->pipefd1[1], "HELLO", 5) < 0) + wpa_printf(MSG_INFO, "write: %s", strerror(errno)); + if (write(t->pipefd2[1], "TEST", 4) < 0) + wpa_printf(MSG_INFO, "write: %s", strerror(errno)); + os_sleep(0, 50000); + wpa_printf(MSG_INFO, "waiting for eloop callbacks"); +} + + +static void eloop_tests_run(void *eloop_data, void *user_ctx) +{ + eloop_tests_start(1); +} + + +static int eloop_tests(void) +{ + wpa_printf(MSG_INFO, "schedule eloop tests to be run"); + + /* + * Cannot return error from these without a significant design change, + * so for now, run the tests from a scheduled timeout and require + * separate verification of the results from the debug log. + */ + eloop_register_timeout(0, 0, eloop_tests_run, NULL, NULL); + + return 0; +} + + int utils_module_tests(void) { int ret = 0; @@ -416,6 +850,10 @@ int utils_module_tests(void) bitfield_tests() < 0 || base64_tests() < 0 || common_tests() < 0 || + os_tests() < 0 || + wpabuf_tests() < 0 || + ip_addr_tests() < 0 || + eloop_tests() < 0 || int_array_tests() < 0) ret = -1; diff --git a/contrib/wpa/src/utils/wpa_debug.c b/contrib/wpa/src/utils/wpa_debug.c index 0d11905..61c0d5c 100644 --- a/contrib/wpa/src/utils/wpa_debug.c +++ b/contrib/wpa/src/utils/wpa_debug.c @@ -307,7 +307,7 @@ static void _wpa_hexdump(int level, const char *title, const u8 *buf, "%s - hexdump(len=%lu):%s%s", title, (long unsigned int) len, display, len > slen ? " ..." : ""); - os_free(strbuf); + bin_clear_free(strbuf, 1 + 3 * slen); return; } #else /* CONFIG_ANDROID_LOG */ @@ -339,7 +339,7 @@ static void _wpa_hexdump(int level, const char *title, const u8 *buf, syslog(syslog_priority(level), "%s - hexdump(len=%lu):%s", title, (unsigned long) len, display); - os_free(strbuf); + bin_clear_free(strbuf, 1 + 3 * len); return; } #endif /* CONFIG_DEBUG_SYSLOG */ @@ -635,8 +635,8 @@ void wpa_msg(void *ctx, int level, const char *fmt, ...) va_end(ap); wpa_printf(level, "%s%s", prefix, buf); if (wpa_msg_cb) - wpa_msg_cb(ctx, level, 0, buf, len); - os_free(buf); + wpa_msg_cb(ctx, level, WPA_MSG_PER_INTERFACE, buf, len); + bin_clear_free(buf, buflen); } @@ -663,8 +663,8 @@ void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...) va_start(ap, fmt); len = vsnprintf(buf, buflen, fmt, ap); va_end(ap); - wpa_msg_cb(ctx, level, 0, buf, len); - os_free(buf); + wpa_msg_cb(ctx, level, WPA_MSG_PER_INTERFACE, buf, len); + bin_clear_free(buf, buflen); } @@ -690,8 +690,8 @@ void wpa_msg_global(void *ctx, int level, const char *fmt, ...) va_end(ap); wpa_printf(level, "%s", buf); if (wpa_msg_cb) - wpa_msg_cb(ctx, level, 1, buf, len); - os_free(buf); + wpa_msg_cb(ctx, level, WPA_MSG_GLOBAL, buf, len); + bin_clear_free(buf, buflen); } @@ -718,8 +718,8 @@ void wpa_msg_global_ctrl(void *ctx, int level, const char *fmt, ...) va_start(ap, fmt); len = vsnprintf(buf, buflen, fmt, ap); va_end(ap); - wpa_msg_cb(ctx, level, 1, buf, len); - os_free(buf); + wpa_msg_cb(ctx, level, WPA_MSG_GLOBAL, buf, len); + bin_clear_free(buf, buflen); } @@ -745,7 +745,34 @@ void wpa_msg_no_global(void *ctx, int level, const char *fmt, ...) va_end(ap); wpa_printf(level, "%s", buf); if (wpa_msg_cb) - wpa_msg_cb(ctx, level, 2, buf, len); + wpa_msg_cb(ctx, level, WPA_MSG_NO_GLOBAL, buf, len); + bin_clear_free(buf, buflen); +} + + +void wpa_msg_global_only(void *ctx, int level, const char *fmt, ...) +{ + va_list ap; + char *buf; + int buflen; + int len; + + va_start(ap, fmt); + buflen = vsnprintf(NULL, 0, fmt, ap) + 1; + va_end(ap); + + buf = os_malloc(buflen); + if (buf == NULL) { + wpa_printf(MSG_ERROR, "%s: Failed to allocate message buffer", + __func__); + return; + } + va_start(ap, fmt); + len = vsnprintf(buf, buflen, fmt, ap); + va_end(ap); + wpa_printf(level, "%s", buf); + if (wpa_msg_cb) + wpa_msg_cb(ctx, level, WPA_MSG_ONLY_GLOBAL, buf, len); os_free(buf); } @@ -789,6 +816,45 @@ void hostapd_logger(void *ctx, const u8 *addr, unsigned int module, int level, MAC2STR(addr), buf); else wpa_printf(MSG_DEBUG, "hostapd_logger: %s", buf); - os_free(buf); + bin_clear_free(buf, buflen); } #endif /* CONFIG_NO_HOSTAPD_LOGGER */ + + +const char * debug_level_str(int level) +{ + switch (level) { + case MSG_EXCESSIVE: + return "EXCESSIVE"; + case MSG_MSGDUMP: + return "MSGDUMP"; + case MSG_DEBUG: + return "DEBUG"; + case MSG_INFO: + return "INFO"; + case MSG_WARNING: + return "WARNING"; + case MSG_ERROR: + return "ERROR"; + default: + return "?"; + } +} + + +int str_to_debug_level(const char *s) +{ + if (os_strcasecmp(s, "EXCESSIVE") == 0) + return MSG_EXCESSIVE; + if (os_strcasecmp(s, "MSGDUMP") == 0) + return MSG_MSGDUMP; + if (os_strcasecmp(s, "DEBUG") == 0) + return MSG_DEBUG; + if (os_strcasecmp(s, "INFO") == 0) + return MSG_INFO; + if (os_strcasecmp(s, "WARNING") == 0) + return MSG_WARNING; + if (os_strcasecmp(s, "ERROR") == 0) + return MSG_ERROR; + return -1; +} diff --git a/contrib/wpa/src/utils/wpa_debug.h b/contrib/wpa/src/utils/wpa_debug.h index 400bea9..17d8f96 100644 --- a/contrib/wpa/src/utils/wpa_debug.h +++ b/contrib/wpa/src/utils/wpa_debug.h @@ -164,6 +164,7 @@ void wpa_hexdump_ascii_key(int level, const char *title, const void *buf, #define wpa_msg_global(args...) do { } while (0) #define wpa_msg_global_ctrl(args...) do { } while (0) #define wpa_msg_no_global(args...) do { } while (0) +#define wpa_msg_global_only(args...) do { } while (0) #define wpa_msg_register_cb(f) do { } while (0) #define wpa_msg_register_ifname_cb(f) do { } while (0) #else /* CONFIG_NO_WPA_MSG */ @@ -243,7 +244,28 @@ PRINTF_FORMAT(3, 4); void wpa_msg_no_global(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4); -typedef void (*wpa_msg_cb_func)(void *ctx, int level, int global, +/** + * wpa_msg_global_only - Conditional printf for ctrl_iface monitors + * @ctx: Pointer to context data; this is the ctx variable registered + * with struct wpa_driver_ops::init() + * @level: priority level (MSG_*) of the message + * @fmt: printf format string, followed by optional arguments + * + * This function is used to print conditional debugging and error messages. + * This function is like wpa_msg_global(), but it sends the output only as a + * global event. + */ +void wpa_msg_global_only(void *ctx, int level, const char *fmt, ...) +PRINTF_FORMAT(3, 4); + +enum wpa_msg_type { + WPA_MSG_PER_INTERFACE, + WPA_MSG_GLOBAL, + WPA_MSG_NO_GLOBAL, + WPA_MSG_ONLY_GLOBAL, +}; + +typedef void (*wpa_msg_cb_func)(void *ctx, int level, enum wpa_msg_type type, const char *txt, size_t len); /** @@ -342,4 +364,7 @@ static inline void wpa_debug_close_linux_tracing(void) #define WPA_ASSERT(a) do { } while (0) #endif +const char * debug_level_str(int level); +int str_to_debug_level(const char *s); + #endif /* WPA_DEBUG_H */ diff --git a/contrib/wpa/src/utils/wpabuf.c b/contrib/wpa/src/utils/wpabuf.c index 7aafa0a..11e7323 100644 --- a/contrib/wpa/src/utils/wpabuf.c +++ b/contrib/wpa/src/utils/wpabuf.c @@ -17,7 +17,7 @@ struct wpabuf_trace { unsigned int magic; -}; +} __attribute__((aligned(8))); static struct wpabuf_trace * wpabuf_get_trace(const struct wpabuf *buf) { diff --git a/contrib/wpa/src/wps/http_client.c b/contrib/wpa/src/wps/http_client.c index 0290013..cdf3a51 100644 --- a/contrib/wpa/src/wps/http_client.c +++ b/contrib/wpa/src/wps/http_client.c @@ -85,15 +85,16 @@ static void http_client_tx_ready(int sock, void *eloop_ctx, void *sock_ctx) { struct http_client *c = eloop_ctx; int res; + size_t send_len; + send_len = wpabuf_len(c->req) - c->req_pos; wpa_printf(MSG_DEBUG, "HTTP: Send client request to %s:%d (%lu of %lu " "bytes remaining)", inet_ntoa(c->dst.sin_addr), ntohs(c->dst.sin_port), (unsigned long) wpabuf_len(c->req), - (unsigned long) wpabuf_len(c->req) - c->req_pos); + (unsigned long) send_len); - res = send(c->sd, wpabuf_head_u8(c->req) + c->req_pos, - wpabuf_len(c->req) - c->req_pos, 0); + res = send(c->sd, wpabuf_head_u8(c->req) + c->req_pos, send_len, 0); if (res < 0) { wpa_printf(MSG_DEBUG, "HTTP: Failed to send buffer: %s", strerror(errno)); @@ -102,12 +103,11 @@ static void http_client_tx_ready(int sock, void *eloop_ctx, void *sock_ctx) return; } - if ((size_t) res < wpabuf_len(c->req) - c->req_pos) { + if ((size_t) res < send_len) { wpa_printf(MSG_DEBUG, "HTTP: Sent %d of %lu bytes; %lu bytes " "remaining", res, (unsigned long) wpabuf_len(c->req), - (unsigned long) wpabuf_len(c->req) - c->req_pos - - res); + (unsigned long) send_len - res); c->req_pos += res; return; } @@ -146,24 +146,20 @@ struct http_client * http_client_addr(struct sockaddr_in *dst, c->cb_ctx = cb_ctx; c->sd = socket(AF_INET, SOCK_STREAM, 0); - if (c->sd < 0) { - http_client_free(c); - return NULL; - } + if (c->sd < 0) + goto fail; if (fcntl(c->sd, F_SETFL, O_NONBLOCK) != 0) { wpa_printf(MSG_DEBUG, "HTTP: fnctl(O_NONBLOCK) failed: %s", strerror(errno)); - http_client_free(c); - return NULL; + goto fail; } if (connect(c->sd, (struct sockaddr *) dst, sizeof(*dst))) { if (errno != EINPROGRESS) { wpa_printf(MSG_DEBUG, "HTTP: Failed to connect: %s", strerror(errno)); - http_client_free(c); - return NULL; + goto fail; } /* @@ -173,20 +169,18 @@ struct http_client * http_client_addr(struct sockaddr_in *dst, } if (eloop_register_sock(c->sd, EVENT_TYPE_WRITE, http_client_tx_ready, - c, NULL)) { - http_client_free(c); - return NULL; - } - - if (eloop_register_timeout(HTTP_CLIENT_TIMEOUT_SEC, 0, - http_client_timeout, c, NULL)) { - http_client_free(c); - return NULL; - } + c, NULL) || + eloop_register_timeout(HTTP_CLIENT_TIMEOUT_SEC, 0, + http_client_timeout, c, NULL)) + goto fail; c->req = req; return c; + +fail: + http_client_free(c); + return NULL; } diff --git a/contrib/wpa/src/wps/http_server.c b/contrib/wpa/src/wps/http_server.c index ac088c4..507abe8 100644 --- a/contrib/wpa/src/wps/http_server.c +++ b/contrib/wpa/src/wps/http_server.c @@ -277,11 +277,9 @@ struct http_server * http_server_init(struct in_addr *addr, int port, "%s", srv->port, strerror(errno)); goto fail; } - if (listen(srv->fd, 10 /* max backlog */) < 0) - goto fail; - if (fcntl(srv->fd, F_SETFL, O_NONBLOCK) < 0) - goto fail; - if (eloop_register_sock(srv->fd, EVENT_TYPE_READ, http_server_cb, + if (listen(srv->fd, 10 /* max backlog */) < 0 || + fcntl(srv->fd, F_SETFL, O_NONBLOCK) < 0 || + eloop_register_sock(srv->fd, EVENT_TYPE_READ, http_server_cb, srv, NULL)) goto fail; diff --git a/contrib/wpa/src/wps/httpread.c b/contrib/wpa/src/wps/httpread.c index 2f08f37..7a2ba50 100644 --- a/contrib/wpa/src/wps/httpread.c +++ b/contrib/wpa/src/wps/httpread.c @@ -44,16 +44,6 @@ #define HTTPREAD_HEADER_MAX_SIZE 4096 /* max allowed for headers */ #define HTTPREAD_BODYBUF_DELTA 4096 /* increase allocation by this */ -#if 0 -/* httpread_debug -- set this global variable > 0 e.g. from debugger - * to enable debugs (larger numbers for more debugs) - * Make this a #define of 0 to eliminate the debugging code. - */ -int httpread_debug = 99; -#else -#define httpread_debug 0 /* eliminates even the debugging code */ -#endif - /* control instance -- actual definition (opaque to application) */ @@ -136,8 +126,7 @@ static void httpread_timeout_handler(void *eloop_data, void *user_ctx); */ void httpread_destroy(struct httpread *h) { - if (httpread_debug >= 10) - wpa_printf(MSG_DEBUG, "ENTER httpread_destroy(%p)", h); + wpa_printf(MSG_DEBUG, "httpread_destroy(%p)", h); if (!h) return; @@ -177,6 +166,12 @@ static int httpread_hdr_option_analyze( if (!isdigit(*hbp)) return -1; h->content_length = atol(hbp); + if (h->content_length < 0 || h->content_length > h->max_bytes) { + wpa_printf(MSG_DEBUG, + "httpread: Unacceptable Content-Length %d", + h->content_length); + return -1; + } h->got_content_length = 1; return 0; } @@ -283,8 +278,6 @@ static int httpread_hdr_analyze(struct httpread *h) } } *uri = 0; /* null terminate */ - while (isgraph(*hbp)) - hbp++; while (*hbp == ' ' || *hbp == '\t') hbp++; /* get version */ @@ -380,15 +373,16 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) char *bbp; /* pointer into body buffer */ char readbuf[HTTPREAD_READBUF_SIZE]; /* temp use to read into */ - if (httpread_debug >= 20) - wpa_printf(MSG_DEBUG, "ENTER httpread_read_handler(%p)", h); - /* read some at a time, then search for the interal * boundaries between header and data and etc. */ + wpa_printf(MSG_DEBUG, "httpread: Trying to read more data(%p)", h); nread = read(h->sd, readbuf, sizeof(readbuf)); - if (nread < 0) + if (nread < 0) { + wpa_printf(MSG_DEBUG, "httpread failed: %s", strerror(errno)); goto bad; + } + wpa_hexdump_ascii(MSG_MSGDUMP, "httpread - read", readbuf, nread); if (nread == 0) { /* end of transmission... this may be normal * or may be an error... in some cases we can't @@ -411,8 +405,7 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) * although dropped connections can cause false * end */ - if (httpread_debug >= 10) - wpa_printf(MSG_DEBUG, "httpread ok eof(%p)", h); + wpa_printf(MSG_DEBUG, "httpread ok eof(%p)", h); h->got_body = 1; goto got_file; } @@ -432,6 +425,8 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) if (nread == 0) goto get_more; if (h->hdr_nbytes == HTTPREAD_HEADER_MAX_SIZE) { + wpa_printf(MSG_DEBUG, + "httpread: Too long header"); goto bad; } *hbp++ = *rbp++; @@ -453,16 +448,13 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) goto bad; } if (h->max_bytes == 0) { - if (httpread_debug >= 10) - wpa_printf(MSG_DEBUG, - "httpread no body hdr end(%p)", h); + wpa_printf(MSG_DEBUG, "httpread no body hdr end(%p)", + h); goto got_file; } if (h->got_content_length && h->content_length == 0) { - if (httpread_debug >= 10) - wpa_printf(MSG_DEBUG, - "httpread zero content length(%p)", - h); + wpa_printf(MSG_DEBUG, + "httpread zero content length(%p)", h); goto got_file; } } @@ -475,9 +467,7 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) !os_strncasecmp(h->hdr, "HEAD", 4) || !os_strncasecmp(h->hdr, "GET", 3)) { if (!h->got_body) { - if (httpread_debug >= 10) - wpa_printf(MSG_DEBUG, - "httpread NO BODY for sp. type"); + wpa_printf(MSG_DEBUG, "httpread NO BODY for sp. type"); } h->got_body = 1; goto got_file; @@ -498,8 +488,12 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) char *new_body; int new_alloc_nbytes; - if (h->body_nbytes >= h->max_bytes) + if (h->body_nbytes >= h->max_bytes) { + wpa_printf(MSG_DEBUG, + "httpread: body_nbytes=%d >= max_bytes=%d", + h->body_nbytes, h->max_bytes); goto bad; + } new_alloc_nbytes = h->body_alloc_nbytes + HTTPREAD_BODYBUF_DELTA; /* For content-length case, the first time @@ -509,9 +503,23 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) if (h->got_content_length && new_alloc_nbytes < (h->content_length + 1)) new_alloc_nbytes = h->content_length + 1; + if (new_alloc_nbytes < h->body_alloc_nbytes || + new_alloc_nbytes > h->max_bytes + + HTTPREAD_BODYBUF_DELTA) { + wpa_printf(MSG_DEBUG, + "httpread: Unacceptable body length %d (body_alloc_nbytes=%u max_bytes=%u)", + new_alloc_nbytes, + h->body_alloc_nbytes, + h->max_bytes); + goto bad; + } if ((new_body = os_realloc(h->body, new_alloc_nbytes)) - == NULL) + == NULL) { + wpa_printf(MSG_DEBUG, + "httpread: Failed to reallocate buffer (len=%d)", + new_alloc_nbytes); goto bad; + } h->body = new_body; h->body_alloc_nbytes = new_alloc_nbytes; @@ -530,9 +538,19 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) /* hdr line consists solely * of a hex numeral and CFLF */ - if (!isxdigit(*cbp)) + if (!isxdigit(*cbp)) { + wpa_printf(MSG_DEBUG, + "httpread: Unexpected chunk header value (not a hex digit)"); goto bad; + } h->chunk_size = strtoul(cbp, NULL, 16); + if (h->chunk_size < 0 || + h->chunk_size > h->max_bytes) { + wpa_printf(MSG_DEBUG, + "httpread: Invalid chunk size %d", + h->chunk_size); + goto bad; + } /* throw away chunk header * so we have only real data */ @@ -542,10 +560,9 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) /* end of chunking */ /* trailer follows */ h->in_trailer = 1; - if (httpread_debug >= 20) - wpa_printf( - MSG_DEBUG, - "httpread end chunks(%p)", h); + wpa_printf(MSG_DEBUG, + "httpread end chunks(%p)", + h); break; } h->in_chunk_data = 1; @@ -563,8 +580,11 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) */ if (bbp[-1] == '\n' && bbp[-2] == '\r') { - } else + } else { + wpa_printf(MSG_DEBUG, + "httpread: Invalid chunk end"); goto bad; + } h->body_nbytes -= 2; bbp -= 2; h->chunk_start = h->body_nbytes; @@ -574,10 +594,8 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) } else if (h->got_content_length && h->body_nbytes >= h->content_length) { h->got_body = 1; - if (httpread_debug >= 10) - wpa_printf( - MSG_DEBUG, - "httpread got content(%p)", h); + wpa_printf(MSG_DEBUG, + "httpread got content(%p)", h); goto got_file; } if (nread <= 0) @@ -601,6 +619,11 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) ncopy = nread; } /* Note: should never be 0 */ + if (ncopy < 0) { + wpa_printf(MSG_DEBUG, + "httpread: Invalid ncopy=%d", ncopy); + goto bad; + } if (ncopy > nread) ncopy = nread; os_memcpy(bbp, rbp, ncopy); @@ -635,10 +658,9 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) if (c == '\n') { h->trailer_state = trailer_line_begin; h->in_trailer = 0; - if (httpread_debug >= 10) - wpa_printf( - MSG_DEBUG, - "httpread got content(%p)", h); + wpa_printf(MSG_DEBUG, + "httpread got content(%p)", + h); h->got_body = 1; goto got_file; } @@ -666,13 +688,14 @@ bad: return; get_more: + wpa_printf(MSG_DEBUG, "httpread: get more (%p)", h); return; got_file: - if (httpread_debug >= 10) - wpa_printf(MSG_DEBUG, - "httpread got file %d bytes type %d", - h->body_nbytes, h->hdr_type); + wpa_printf(MSG_DEBUG, "httpread got file %d bytes type %d", + h->body_nbytes, h->hdr_type); + wpa_hexdump_ascii(MSG_MSGDUMP, "httpread: body", + h->body, h->body_nbytes); /* Null terminate for convenience of some applications */ if (h->body) h->body[h->body_nbytes] = 0; /* null terminate */ diff --git a/contrib/wpa/src/wps/ndef.c b/contrib/wpa/src/wps/ndef.c index d45dfc8..bb3c055 100644 --- a/contrib/wpa/src/wps/ndef.c +++ b/contrib/wpa/src/wps/ndef.c @@ -29,8 +29,8 @@ struct ndef_record { u32 total_length; }; -static char wifi_handover_type[] = "application/vnd.wfa.wsc"; -static char p2p_handover_type[] = "application/vnd.wfa.p2p"; +static const char wifi_handover_type[] = "application/vnd.wfa.wsc"; +static const char p2p_handover_type[] = "application/vnd.wfa.p2p"; static int ndef_parse_record(const u8 *data, u32 size, struct ndef_record *record) @@ -45,9 +45,14 @@ static int ndef_parse_record(const u8 *data, u32 size, return -1; record->payload_length = *pos++; } else { + u32 len; + if (size < 6) return -1; - record->payload_length = ntohl(*(u32 *)pos); + len = WPA_GET_BE32(pos); + if (len > size - 6 || len > 20000) + return -1; + record->payload_length = len; pos += sizeof(u32); } @@ -68,7 +73,8 @@ static int ndef_parse_record(const u8 *data, u32 size, pos += record->payload_length; record->total_length = pos - data; - if (record->total_length > size) + if (record->total_length > size || + record->total_length < record->payload_length) return -1; return 0; } @@ -97,7 +103,7 @@ static struct wpabuf * ndef_parse_records(const struct wpabuf *buf, } -static struct wpabuf * ndef_build_record(u8 flags, void *type, +static struct wpabuf * ndef_build_record(u8 flags, const void *type, u8 type_length, void *id, u8 id_length, const struct wpabuf *payload) diff --git a/contrib/wpa/src/wps/wps.c b/contrib/wpa/src/wps/wps.c index 2c68be8..fbaf85a 100644 --- a/contrib/wpa/src/wps/wps.c +++ b/contrib/wpa/src/wps/wps.c @@ -355,16 +355,16 @@ int wps_is_addr_authorized(const struct wpabuf *msg, const u8 *addr, int wps_ap_priority_compar(const struct wpabuf *wps_a, const struct wpabuf *wps_b) { - struct wps_parse_attr attr_a, attr_b; + struct wps_parse_attr attr; int sel_a, sel_b; - if (wps_a == NULL || wps_parse_msg(wps_a, &attr_a) < 0) + if (wps_a == NULL || wps_parse_msg(wps_a, &attr) < 0) return 1; - if (wps_b == NULL || wps_parse_msg(wps_b, &attr_b) < 0) - return -1; + sel_a = attr.selected_registrar && *attr.selected_registrar != 0; - sel_a = attr_a.selected_registrar && *attr_a.selected_registrar != 0; - sel_b = attr_b.selected_registrar && *attr_b.selected_registrar != 0; + if (wps_b == NULL || wps_parse_msg(wps_b, &attr) < 0) + return -1; + sel_b = attr.selected_registrar && *attr.selected_registrar != 0; if (sel_a && !sel_b) return -1; @@ -618,7 +618,8 @@ int wps_attr_text(struct wpabuf *data, char *buf, char *end) if (str == NULL) return pos - buf; for (i = 0; i < attr.dev_name_len; i++) { - if (attr.dev_name[i] < 32) + if (attr.dev_name[i] == 0 || + is_ctrl_char(attr.dev_name[i])) str[i] = '_'; else str[i] = attr.dev_name[i]; diff --git a/contrib/wpa/src/wps/wps.h b/contrib/wpa/src/wps/wps.h index 0a7f65d..2c91d16 100644 --- a/contrib/wpa/src/wps/wps.h +++ b/contrib/wpa/src/wps/wps.h @@ -9,6 +9,7 @@ #ifndef WPS_H #define WPS_H +#include "common/ieee802_11_defs.h" #include "wps_defs.h" /** @@ -44,7 +45,7 @@ struct wps_parse_attr; * @cred_attr_len: Length of cred_attr in octets */ struct wps_credential { - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; size_t ssid_len; u16 auth_type; u16 encr_type; @@ -78,7 +79,7 @@ struct wps_credential { * @sec_dev_type: Array of secondary device types * @num_sec_dev_type: Number of secondary device types * @os_version: OS Version - * @rf_bands: RF bands (WPS_RF_24GHZ, WPS_RF_50GHZ flags) + * @rf_bands: RF bands (WPS_RF_24GHZ, WPS_RF_50GHZ, WPS_RF_60GHZ flags) * @p2p: Whether the device is a P2P device */ struct wps_device_data { @@ -623,7 +624,7 @@ struct wps_context { * Credentials. In addition, AP uses it when acting as an Enrollee to * notify Registrar of the current configuration. */ - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; /** * ssid_len - Length of ssid in octets diff --git a/contrib/wpa/src/wps/wps_attr_parse.c b/contrib/wpa/src/wps/wps_attr_parse.c index 40bc1ad..11a967b 100644 --- a/contrib/wpa/src/wps/wps_attr_parse.c +++ b/contrib/wpa/src/wps/wps_attr_parse.c @@ -447,25 +447,55 @@ static int wps_set_attr(struct wps_parse_attr *attr, u16 type, break; case ATTR_MANUFACTURER: attr->manufacturer = pos; - attr->manufacturer_len = len; + if (len > WPS_MANUFACTURER_MAX_LEN) + attr->manufacturer_len = WPS_MANUFACTURER_MAX_LEN; + else + attr->manufacturer_len = len; break; case ATTR_MODEL_NAME: attr->model_name = pos; - attr->model_name_len = len; + if (len > WPS_MODEL_NAME_MAX_LEN) + attr->model_name_len = WPS_MODEL_NAME_MAX_LEN; + else + attr->model_name_len = len; break; case ATTR_MODEL_NUMBER: attr->model_number = pos; - attr->model_number_len = len; + if (len > WPS_MODEL_NUMBER_MAX_LEN) + attr->model_number_len = WPS_MODEL_NUMBER_MAX_LEN; + else + attr->model_number_len = len; break; case ATTR_SERIAL_NUMBER: attr->serial_number = pos; - attr->serial_number_len = len; + if (len > WPS_SERIAL_NUMBER_MAX_LEN) + attr->serial_number_len = WPS_SERIAL_NUMBER_MAX_LEN; + else + attr->serial_number_len = len; break; case ATTR_DEV_NAME: + if (len > WPS_DEV_NAME_MAX_LEN) { + wpa_printf(MSG_DEBUG, + "WPS: Ignore too long Device Name (len=%u)", + len); + break; + } attr->dev_name = pos; attr->dev_name_len = len; break; case ATTR_PUBLIC_KEY: + /* + * The Public Key attribute is supposed to be exactly 192 bytes + * in length. Allow couple of bytes shorter one to try to + * interoperate with implementations that do not use proper + * zero-padding. + */ + if (len < 190 || len > 192) { + wpa_printf(MSG_DEBUG, + "WPS: Ignore Public Key with unexpected length %u", + len); + break; + } attr->public_key = pos; attr->public_key_len = len; break; @@ -485,6 +515,11 @@ static int wps_set_attr(struct wps_parse_attr *attr, u16 type, attr->num_cred++; break; case ATTR_SSID: + if (len > SSID_MAX_LEN) { + wpa_printf(MSG_DEBUG, + "WPS: Ignore too long SSID (len=%u)", len); + break; + } attr->ssid = pos; attr->ssid_len = len; break; diff --git a/contrib/wpa/src/wps/wps_attr_parse.h b/contrib/wpa/src/wps/wps_attr_parse.h index 82c4739..8188fe9 100644 --- a/contrib/wpa/src/wps/wps_attr_parse.h +++ b/contrib/wpa/src/wps/wps_attr_parse.h @@ -59,43 +59,44 @@ struct wps_parse_attr { /* variable length fields */ const u8 *manufacturer; - size_t manufacturer_len; const u8 *model_name; - size_t model_name_len; const u8 *model_number; - size_t model_number_len; const u8 *serial_number; - size_t serial_number_len; const u8 *dev_name; - size_t dev_name_len; const u8 *public_key; - size_t public_key_len; const u8 *encr_settings; - size_t encr_settings_len; const u8 *ssid; /* <= 32 octets */ - size_t ssid_len; const u8 *network_key; /* <= 64 octets */ - size_t network_key_len; const u8 *authorized_macs; /* <= 30 octets */ - size_t authorized_macs_len; const u8 *sec_dev_type_list; /* <= 128 octets */ - size_t sec_dev_type_list_len; const u8 *oob_dev_password; /* 38..54 octets */ - size_t oob_dev_password_len; + u16 manufacturer_len; + u16 model_name_len; + u16 model_number_len; + u16 serial_number_len; + u16 dev_name_len; + u16 public_key_len; + u16 encr_settings_len; + u16 ssid_len; + u16 network_key_len; + u16 authorized_macs_len; + u16 sec_dev_type_list_len; + u16 oob_dev_password_len; /* attributes that can occur multiple times */ #define MAX_CRED_COUNT 10 - const u8 *cred[MAX_CRED_COUNT]; - size_t cred_len[MAX_CRED_COUNT]; - size_t num_cred; - #define MAX_REQ_DEV_TYPE_COUNT 10 - const u8 *req_dev_type[MAX_REQ_DEV_TYPE_COUNT]; - size_t num_req_dev_type; + unsigned int num_cred; + unsigned int num_req_dev_type; + unsigned int num_vendor_ext; + + u16 cred_len[MAX_CRED_COUNT]; + u16 vendor_ext_len[MAX_WPS_PARSE_VENDOR_EXT]; + + const u8 *cred[MAX_CRED_COUNT]; + const u8 *req_dev_type[MAX_REQ_DEV_TYPE_COUNT]; const u8 *vendor_ext[MAX_WPS_PARSE_VENDOR_EXT]; - size_t vendor_ext_len[MAX_WPS_PARSE_VENDOR_EXT]; - size_t num_vendor_ext; }; int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr); diff --git a/contrib/wpa/src/wps/wps_common.c b/contrib/wpa/src/wps/wps_common.c index c1ede6a..88f85fe 100644 --- a/contrib/wpa/src/wps/wps_common.c +++ b/contrib/wpa/src/wps/wps_common.c @@ -528,7 +528,7 @@ u16 wps_config_methods_str2bin(const char *str) { u16 methods = 0; - if (str == NULL) { + if (str == NULL || str[0] == '\0') { /* Default to enabling methods based on build configuration */ methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD; methods |= WPS_CONFIG_VIRT_DISPLAY; @@ -764,6 +764,8 @@ static int wps_build_ap_freq(struct wpabuf *msg, int freq) rf_band = WPS_RF_24GHZ; else if (mode == HOSTAPD_MODE_IEEE80211A) rf_band = WPS_RF_50GHZ; + else if (mode == HOSTAPD_MODE_IEEE80211AD) + rf_band = WPS_RF_60GHZ; else return 0; /* Unknown band */ ap_channel = channel; diff --git a/contrib/wpa/src/wps/wps_defs.h b/contrib/wpa/src/wps/wps_defs.h index 25cd14a..a23b979 100644 --- a/contrib/wpa/src/wps/wps_defs.h +++ b/contrib/wpa/src/wps/wps_defs.h @@ -41,6 +41,11 @@ extern int wps_corrupt_pkhash; #define WPS_OOB_DEVICE_PASSWORD_MIN_LEN 16 #define WPS_OOB_DEVICE_PASSWORD_LEN 32 #define WPS_OOB_PUBKEY_HASH_LEN 20 +#define WPS_DEV_NAME_MAX_LEN 32 +#define WPS_MANUFACTURER_MAX_LEN 64 +#define WPS_MODEL_NAME_MAX_LEN 32 +#define WPS_MODEL_NUMBER_MAX_LEN 32 +#define WPS_SERIAL_NUMBER_MAX_LEN 32 /* Attribute Types */ enum wps_attribute { @@ -232,6 +237,7 @@ enum wps_error_indication { /* RF Bands */ #define WPS_RF_24GHZ 0x01 #define WPS_RF_50GHZ 0x02 +#define WPS_RF_60GHZ 0x04 /* Config Methods */ #define WPS_CONFIG_USBA 0x0001 diff --git a/contrib/wpa/src/wps/wps_enrollee.c b/contrib/wpa/src/wps/wps_enrollee.c index 89957b1..9321b72 100644 --- a/contrib/wpa/src/wps/wps_enrollee.c +++ b/contrib/wpa/src/wps/wps_enrollee.c @@ -759,7 +759,7 @@ static int wps_process_cred_e(struct wps_data *wps, const u8 *cred, static int wps_process_creds(struct wps_data *wps, const u8 *cred[], - size_t cred_len[], size_t num_cred, int wps2) + u16 cred_len[], unsigned int num_cred, int wps2) { size_t i; int ok = 0; @@ -799,6 +799,7 @@ static int wps_process_ap_settings_e(struct wps_data *wps, struct wpabuf *attrs, int wps2) { struct wps_credential cred; + int ret = 0; if (!wps->wps->ap) return 0; @@ -877,10 +878,10 @@ static int wps_process_ap_settings_e(struct wps_data *wps, if (wps->wps->cred_cb) { cred.cred_attr = wpabuf_head(attrs); cred.cred_attr_len = wpabuf_len(attrs); - wps->wps->cred_cb(wps->wps->cb_ctx, &cred); + ret = wps->wps->cred_cb(wps->wps->cb_ctx, &cred); } - return 0; + return ret; } diff --git a/contrib/wpa/src/wps/wps_er.c b/contrib/wpa/src/wps/wps_er.c index 078ff72..b840acd 100644 --- a/contrib/wpa/src/wps/wps_er.c +++ b/contrib/wpa/src/wps/wps_er.c @@ -1649,11 +1649,15 @@ static void wps_er_http_put_message_cb(void *ctx, struct http_client *c, case HTTP_CLIENT_OK: wpa_printf(MSG_DEBUG, "WPS ER: PutMessage OK"); reply = http_client_get_body(c); - if (reply == NULL) - break; - msg = os_zalloc(wpabuf_len(reply) + 1); - if (msg == NULL) + if (reply) + msg = os_zalloc(wpabuf_len(reply) + 1); + if (msg == NULL) { + if (ap->wps) { + wps_deinit(ap->wps); + ap->wps = NULL; + } break; + } os_memcpy(msg, wpabuf_head(reply), wpabuf_len(reply)); break; case HTTP_CLIENT_FAILED: @@ -1709,21 +1713,30 @@ static void wps_er_ap_put_message(struct wps_er_ap *ap, url = http_client_url_parse(ap->control_url, &dst, &path); if (url == NULL) { wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL"); - return; + goto fail; } buf = wps_er_soap_hdr(msg, "PutMessage", "NewInMessage", path, &dst, &len_ptr, &body_ptr); os_free(url); if (buf == NULL) - return; + goto fail; wps_er_soap_end(buf, "PutMessage", len_ptr, body_ptr); ap->http = http_client_addr(&dst, buf, 10000, wps_er_http_put_message_cb, ap); - if (ap->http == NULL) + if (ap->http == NULL) { wpabuf_free(buf); + goto fail; + } + return; + +fail: + if (ap->wps) { + wps_deinit(ap->wps); + ap->wps = NULL; + } } diff --git a/contrib/wpa/src/wps/wps_er_ssdp.c b/contrib/wpa/src/wps/wps_er_ssdp.c index e381fec..280b2b3 100644 --- a/contrib/wpa/src/wps/wps_er_ssdp.c +++ b/contrib/wpa/src/wps/wps_er_ssdp.c @@ -78,9 +78,7 @@ static void wps_er_ssdp_rx(int sd, void *eloop_ctx, void *sock_ctx) if (os_strstr(start, "ssdp:byebye")) byebye = 1; } else if (os_strncasecmp(start, "CACHE-CONTROL:", 14) == 0) { - start += 9; - while (*start == ' ') - start++; + start += 14; pos2 = os_strstr(start, "max-age="); if (pos2 == NULL) continue; diff --git a/contrib/wpa/src/wps/wps_module_tests.c b/contrib/wpa/src/wps/wps_module_tests.c index 6800e86..3506307 100644 --- a/contrib/wpa/src/wps/wps_module_tests.c +++ b/contrib/wpa/src/wps/wps_module_tests.c @@ -17,7 +17,7 @@ struct wps_attr_parse_test { int extra; }; -struct wps_attr_parse_test wps_attr_parse_test_cases[] = { +const struct wps_attr_parse_test wps_attr_parse_test_cases[] = { /* Empty message */ { "", 0, 0 }, /* Truncated attribute header */ @@ -271,7 +271,7 @@ static int wps_attr_parse_tests(void) for (i = 0; i < ARRAY_SIZE(wps_attr_parse_test_cases); i++) { struct wpabuf *buf; size_t len; - struct wps_attr_parse_test *test = + const struct wps_attr_parse_test *test = &wps_attr_parse_test_cases[i]; len = os_strlen(test->data) / 2; diff --git a/contrib/wpa/src/wps/wps_registrar.c b/contrib/wpa/src/wps/wps_registrar.c index 48b7e12..4ca3a42 100644 --- a/contrib/wpa/src/wps/wps_registrar.c +++ b/contrib/wpa/src/wps/wps_registrar.c @@ -2605,13 +2605,16 @@ static enum wps_process_res wps_process_m1(struct wps_data *wps, token = wps_get_nfc_pw_token( &wps->wps->registrar->nfc_pw_tokens, wps->dev_pw_id); if (token && token->peer_pk_hash_known) { + size_t len; + wpa_printf(MSG_DEBUG, "WPS: Found matching NFC " "Password Token"); dl_list_del(&token->list); wps->nfc_pw_token = token; addr[0] = attr->public_key; - sha256_vector(1, addr, &attr->public_key_len, hash); + len = attr->public_key_len; + sha256_vector(1, addr, &len, hash); if (os_memcmp_const(hash, wps->nfc_pw_token->pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN) != 0) { @@ -3226,8 +3229,13 @@ static enum wps_process_res wps_process_wsc_done(struct wps_data *wps, os_memset(&cred, 0, sizeof(cred)); os_memcpy(cred.ssid, wps->wps->ssid, wps->wps->ssid_len); cred.ssid_len = wps->wps->ssid_len; - cred.auth_type = WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK; - cred.encr_type = WPS_ENCR_TKIP | WPS_ENCR_AES; + if (wps->wps->rf_band_cb(wps->wps->cb_ctx) == WPS_RF_60GHZ) { + cred.auth_type = WPS_AUTH_WPA2PSK; + cred.encr_type = WPS_ENCR_AES; + } else { + cred.auth_type = WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK; + cred.encr_type = WPS_ENCR_TKIP | WPS_ENCR_AES; + } os_memcpy(cred.key, wps->new_psk, wps->new_psk_len); cred.key_len = wps->new_psk_len; diff --git a/contrib/wpa/src/wps/wps_upnp.c b/contrib/wpa/src/wps/wps_upnp.c index 933d734..44318e0 100644 --- a/contrib/wpa/src/wps/wps_upnp.c +++ b/contrib/wpa/src/wps/wps_upnp.c @@ -695,6 +695,7 @@ struct subscription * subscription_start(struct upnp_wps_device_sm *sm, struct subscription *s; time_t now = time(NULL); time_t expire = now + UPNP_SUBSCRIBE_SEC; + char str[80]; /* Get rid of expired subscriptions so we have room */ subscription_list_age(sm, now); @@ -743,8 +744,10 @@ struct subscription * subscription_start(struct upnp_wps_device_sm *sm, subscription_destroy(s); return NULL; } - wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription %p started with %s", - s, callback_urls); + uuid_bin2str(s->uuid, str, sizeof(str)); + wpa_printf(MSG_DEBUG, + "WPS UPnP: Subscription %p (SID %s) started with %s", + s, str, callback_urls); /* Schedule sending this */ event_send_all_later(sm); return s; diff --git a/contrib/wpa/src/wps/wps_upnp_ap.c b/contrib/wpa/src/wps/wps_upnp_ap.c index 2949f14..cca3905 100644 --- a/contrib/wpa/src/wps/wps_upnp_ap.c +++ b/contrib/wpa/src/wps/wps_upnp_ap.c @@ -34,10 +34,8 @@ int upnp_er_set_selected_registrar(struct wps_registrar *reg, wpa_hexdump_buf(MSG_MSGDUMP, "WPS: SetSelectedRegistrar attributes", msg); - if (wps_validate_upnp_set_selected_registrar(msg) < 0) - return -1; - - if (wps_parse_msg(msg, &attr) < 0) + if (wps_validate_upnp_set_selected_registrar(msg) < 0 || + wps_parse_msg(msg, &attr) < 0) return -1; s->reg = reg; diff --git a/contrib/wpa/src/wps/wps_upnp_event.c b/contrib/wpa/src/wps/wps_upnp_event.c index 2c8ed4f1..94aae75 100644 --- a/contrib/wpa/src/wps/wps_upnp_event.c +++ b/contrib/wpa/src/wps/wps_upnp_event.c @@ -276,11 +276,9 @@ static int event_send_start(struct subscription *s) * Assume we are called ONLY with no current event and ONLY with * nonempty event queue and ONLY with at least one address to send to. */ - if (dl_list_empty(&s->addr_list)) - return -1; - if (s->current_event) - return -1; - if (dl_list_empty(&s->event_queue)) + if (dl_list_empty(&s->addr_list) || + s->current_event || + dl_list_empty(&s->event_queue)) return -1; s->current_event = e = event_dequeue(s); diff --git a/contrib/wpa/src/wps/wps_upnp_ssdp.c b/contrib/wpa/src/wps/wps_upnp_ssdp.c index 26a740d..968fc03 100644 --- a/contrib/wpa/src/wps/wps_upnp_ssdp.c +++ b/contrib/wpa/src/wps/wps_upnp_ssdp.c @@ -139,7 +139,7 @@ next_advertisement(struct upnp_wps_device_sm *sm, uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string)); msg = wpabuf_alloc(800); /* more than big enough */ if (msg == NULL) - goto fail; + return NULL; switch (a->type) { case ADVERTISE_UP: case ADVERTISE_DOWN: @@ -213,10 +213,6 @@ next_advertisement(struct upnp_wps_device_sm *sm, *islast = 1; return msg; - -fail: - wpabuf_free(msg); - return NULL; } @@ -744,11 +740,9 @@ int ssdp_listener_open(void) int sd; sd = socket(AF_INET, SOCK_DGRAM, 0); - if (sd < 0) - goto fail; - if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0) - goto fail; - if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) + if (sd < 0 || + fcntl(sd, F_SETFL, O_NONBLOCK) != 0 || + setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) goto fail; os_memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; @@ -760,9 +754,8 @@ int ssdp_listener_open(void) mcast_addr.imr_interface.s_addr = htonl(INADDR_ANY); mcast_addr.imr_multiaddr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS); if (setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, - (char *) &mcast_addr, sizeof(mcast_addr))) - goto fail; - if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL, + (char *) &mcast_addr, sizeof(mcast_addr)) || + setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl))) goto fail; diff --git a/contrib/wpa/src/wps/wps_upnp_web.c b/contrib/wpa/src/wps/wps_upnp_web.c index b1cf571..d5b0b5b 100644 --- a/contrib/wpa/src/wps/wps_upnp_web.c +++ b/contrib/wpa/src/wps/wps_upnp_web.c @@ -1003,6 +1003,8 @@ static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm, ret = HTTP_INTERNAL_SERVER_ERROR; goto error; } + if (len > 0 && callback_urls[len - 1] == '\r') + callback_urls[len - 1] = '\0'; continue; } /* SID is only for renewal */ @@ -1214,18 +1216,25 @@ static void web_connection_parse_unsubscribe(struct upnp_wps_device_sm *sm, } if (got_uuid) { + char str[80]; + + uuid_bin2str(uuid, str, sizeof(str)); + s = subscription_find(sm, uuid); if (s) { struct subscr_addr *sa; sa = dl_list_first(&s->addr_list, struct subscr_addr, list); - wpa_printf(MSG_DEBUG, "WPS UPnP: Unsubscribing %p %s", - s, (sa && sa->domain_and_port) ? + wpa_printf(MSG_DEBUG, + "WPS UPnP: Unsubscribing %p (SID %s) %s", + s, str, (sa && sa->domain_and_port) ? sa->domain_and_port : "-null-"); dl_list_del(&s->list); subscription_destroy(s); } else { - wpa_printf(MSG_INFO, "WPS UPnP: Could not find matching subscription to unsubscribe"); + wpa_printf(MSG_INFO, + "WPS UPnP: Could not find matching subscription to unsubscribe (SID %s)", + str); ret = HTTP_PRECONDITION_FAILED; goto send_msg; } diff --git a/contrib/wpa/src/wps/wps_validate.c b/contrib/wpa/src/wps/wps_validate.c index 1c6a14b..267b565 100644 --- a/contrib/wpa/src/wps/wps_validate.c +++ b/contrib/wpa/src/wps/wps_validate.c @@ -224,6 +224,8 @@ static int wps_validate_rf_bands(const u8 *rf_bands, int mandatory) return 0; } if (*rf_bands != WPS_RF_24GHZ && *rf_bands != WPS_RF_50GHZ && + *rf_bands != WPS_RF_60GHZ && + *rf_bands != (WPS_RF_24GHZ | WPS_RF_50GHZ | WPS_RF_60GHZ) && *rf_bands != (WPS_RF_24GHZ | WPS_RF_50GHZ)) { wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Rf Bands " "attribute value 0x%x", *rf_bands); diff --git a/contrib/wpa/wpa_supplicant/ChangeLog b/contrib/wpa/wpa_supplicant/ChangeLog index 1ac79b4..facd90e 100644 --- a/contrib/wpa/wpa_supplicant/ChangeLog +++ b/contrib/wpa/wpa_supplicant/ChangeLog @@ -1,5 +1,68 @@ ChangeLog for wpa_supplicant +2015-09-27 - v2.5 + * fixed P2P validation of SSID element length before copying it + [http://w1.fi/security/2015-1/] (CVE-2015-1863) + * fixed WPS UPnP vulnerability with HTTP chunked transfer encoding + [http://w1.fi/security/2015-2/] (CVE-2015-4141) + * fixed WMM Action frame parser (AP mode) + [http://w1.fi/security/2015-3/] (CVE-2015-4142) + * fixed EAP-pwd peer missing payload length validation + [http://w1.fi/security/2015-4/] + (CVE-2015-4143, CVE-2015-4144, CVE-2015-4145, CVE-2015-4146) + * fixed validation of WPS and P2P NFC NDEF record payload length + [http://w1.fi/security/2015-5/] + * nl80211: + - added VHT configuration for IBSS + - fixed vendor command handling to check OUI properly + - allow driver-based roaming to change ESS + * added AVG_BEACON_RSSI to SIGNAL_POLL output + * wpa_cli: added tab completion for number of commands + * removed unmaintained and not yet completed SChannel/CryptoAPI support + * modified Extended Capabilities element use in Probe Request frames to + include all cases if any of the values are non-zero + * added support for dynamically creating/removing a virtual interface + with interface_add/interface_remove + * added support for hashed password (NtHash) in EAP-pwd peer + * added support for memory-only PSK/passphrase (mem_only_psk=1 and + CTRL-REQ/RSP-PSK_PASSPHRASE) + * P2P + - optimize scan frequencies list when re-joining a persistent group + - fixed number of sequences with nl80211 P2P Device interface + - added operating class 125 for P2P use cases (this allows 5 GHz + channels 161 and 169 to be used if they are enabled in the current + regulatory domain) + - number of fixes to P2PS functionality + - do not allow 40 MHz co-ex PRI/SEC switch to force MCC + - extended support for preferred channel listing + * D-Bus: + - fixed WPS property of fi.w1.wpa_supplicant1.BSS interface + - fixed PresenceRequest to use group interface + - added new signals: FindStopped, WPS pbc-overlap, + GroupFormationFailure, WPS timeout, InvitationReceived + - added new methods: WPS Cancel, P2P Cancel, Reconnect, RemoveClient + - added manufacturer info + * added EAP-EKE peer support for deriving Session-Id + * added wps_priority configuration parameter to set the default priority + for all network profiles added by WPS + * added support to request a scan with specific SSIDs with the SCAN + command (optional "ssid <hexdump>" arguments) + * removed support for WEP40/WEP104 as a group cipher with WPA/WPA2 + * fixed SAE group selection in an error case + * modified SAE routines to be more robust and PWE generation to be + stronger against timing attacks + * added support for Brainpool Elliptic Curves with SAE + * added support for CCMP-256 and GCMP-256 as group ciphers with FT + * fixed BSS selection based on estimated throughput + * added option to disable TLSv1.0 with OpenSSL + (phase1="tls_disable_tlsv1_0=1") + * added Fast Session Transfer (FST) module + * fixed OpenSSL PKCS#12 extra certificate handling + * fixed key derivation for Suite B 192-bit AKM (this breaks + compatibility with the earlier version) + * added RSN IE to Mesh Peering Open/Confirm frames + * number of small fixes + 2015-03-15 - v2.4 * allow OpenSSL cipher configuration to be set for internal EAP server (openssl_ciphers parameter) diff --git a/contrib/wpa/wpa_supplicant/ap.c b/contrib/wpa/wpa_supplicant/ap.c index 7ecf7a8..7a4f4cf 100644 --- a/contrib/wpa/wpa_supplicant/ap.c +++ b/contrib/wpa/wpa_supplicant/ap.c @@ -142,6 +142,29 @@ void wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s, } } } + + if (conf->secondary_channel) { + struct wpa_supplicant *iface; + + for (iface = wpa_s->global->ifaces; iface; iface = iface->next) + { + if (iface == wpa_s || + iface->wpa_state < WPA_AUTHENTICATING || + (int) iface->assoc_freq != ssid->frequency) + continue; + + /* + * Do not allow 40 MHz co-ex PRI/SEC switch to force us + * to change our PRI channel since we have an existing, + * concurrent connection on that channel and doing + * multi-channel concurrency is likely to cause more + * harm than using different PRI/SEC selection in + * environment with multiple BSSes on these two channels + * with mixed 20 MHz or PRI channel selection. + */ + conf->no_pri_sec_switch = 1; + } + } #endif /* CONFIG_IEEE80211N */ } @@ -485,8 +508,13 @@ static int ap_probe_req_rx(void *ctx, const u8 *sa, const u8 *da, int ssi_signal) { struct wpa_supplicant *wpa_s = ctx; + unsigned int freq = 0; + + if (wpa_s->ap_iface) + freq = wpa_s->ap_iface->freq; + return wpas_p2p_probe_req_rx(wpa_s, sa, da, bssid, ie, ie_len, - ssi_signal); + freq, ssi_signal); } @@ -1156,6 +1184,7 @@ int ap_switch_channel(struct wpa_supplicant *wpa_s, } +#ifdef CONFIG_CTRL_IFACE int ap_ctrl_iface_chanswitch(struct wpa_supplicant *wpa_s, const char *pos) { struct csa_settings settings; @@ -1166,6 +1195,7 @@ int ap_ctrl_iface_chanswitch(struct wpa_supplicant *wpa_s, const char *pos) return ap_switch_channel(wpa_s, &settings); } +#endif /* CONFIG_CTRL_IFACE */ void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht, @@ -1175,7 +1205,10 @@ void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht, return; wpa_s->assoc_freq = freq; - hostapd_event_ch_switch(wpa_s->ap_iface->bss[0], freq, ht, offset, width, cf1, cf1); + if (wpa_s->current_ssid) + wpa_s->current_ssid->frequency = freq; + hostapd_event_ch_switch(wpa_s->ap_iface->bss[0], freq, ht, + offset, width, cf1, cf2); } @@ -1265,6 +1298,7 @@ int wpas_ap_wps_add_nfc_pw(struct wpa_supplicant *wpa_s, u16 pw_id, #endif /* CONFIG_WPS_NFC */ +#ifdef CONFIG_CTRL_IFACE int wpas_ap_stop_ap(struct wpa_supplicant *wpa_s) { struct hostapd_data *hapd; @@ -1274,6 +1308,7 @@ int wpas_ap_stop_ap(struct wpa_supplicant *wpa_s) hapd = wpa_s->ap_iface->bss[0]; return hostapd_ctrl_iface_stop_ap(hapd); } +#endif /* CONFIG_CTRL_IFACE */ #ifdef NEED_AP_MLME @@ -1337,3 +1372,10 @@ void wpas_event_dfs_cac_nop_finished(struct wpa_supplicant *wpa_s, radar->chan_width, radar->cf1, radar->cf2); } #endif /* NEED_AP_MLME */ + + +void ap_periodic(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->ap_iface) + hostapd_periodic_iface(wpa_s->ap_iface); +} diff --git a/contrib/wpa/wpa_supplicant/ap.h b/contrib/wpa/wpa_supplicant/ap.h index 3f4151d..594168c 100644 --- a/contrib/wpa/wpa_supplicant/ap.h +++ b/contrib/wpa/wpa_supplicant/ap.h @@ -93,4 +93,6 @@ void wpas_event_dfs_cac_aborted(struct wpa_supplicant *wpa_s, void wpas_event_dfs_cac_nop_finished(struct wpa_supplicant *wpa_s, struct dfs_event *radar); +void ap_periodic(struct wpa_supplicant *wpa_s); + #endif /* AP_H */ diff --git a/contrib/wpa/wpa_supplicant/bss.c b/contrib/wpa/wpa_supplicant/bss.c index b4c47e2..1051ee3 100644 --- a/contrib/wpa/wpa_supplicant/bss.c +++ b/contrib/wpa/wpa_supplicant/bss.c @@ -19,11 +19,6 @@ #include "bss.h" -/** - * WPA_BSS_EXPIRATION_PERIOD - Period of expiration run in seconds - */ -#define WPA_BSS_EXPIRATION_PERIOD 10 - #define WPA_BSS_FREQ_CHANGED_FLAG BIT(0) #define WPA_BSS_SIGNAL_CHANGED_FLAG BIT(1) #define WPA_BSS_PRIVACY_CHANGED_FLAG BIT(2) @@ -311,10 +306,18 @@ static int wpa_bss_known(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) { - return bss == wpa_s->current_bss || - (!is_zero_ether_addr(bss->bssid) && - (os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) == 0 || - os_memcmp(bss->bssid, wpa_s->pending_bssid, ETH_ALEN) == 0)); + if (bss == wpa_s->current_bss) + return 1; + + if (wpa_s->current_bss && + (bss->ssid_len != wpa_s->current_bss->ssid_len || + os_memcmp(bss->ssid, wpa_s->current_bss->ssid, + bss->ssid_len) != 0)) + return 0; /* SSID has changed */ + + return !is_zero_ether_addr(bss->bssid) && + (os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) == 0 || + os_memcmp(bss->bssid, wpa_s->pending_bssid, ETH_ALEN) == 0); } @@ -390,15 +393,16 @@ static struct wpa_bss * wpa_bss_add(struct wpa_supplicant *wpa_s, dl_list_add_tail(&wpa_s->bss_id, &bss->list_id); wpa_s->num_bss++; wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Add new id %u BSSID " MACSTR - " SSID '%s'", - bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len)); + " SSID '%s' freq %d", + bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len), + bss->freq); wpas_notify_bss_added(wpa_s, bss->bssid, bss->id); return bss; } static int are_ies_equal(const struct wpa_bss *old, - const struct wpa_scan_res *new, u32 ie) + const struct wpa_scan_res *new_res, u32 ie) { const u8 *old_ie, *new_ie; struct wpabuf *old_ie_buff = NULL; @@ -408,19 +412,19 @@ static int are_ies_equal(const struct wpa_bss *old, switch (ie) { case WPA_IE_VENDOR_TYPE: old_ie = wpa_bss_get_vendor_ie(old, ie); - new_ie = wpa_scan_get_vendor_ie(new, ie); + new_ie = wpa_scan_get_vendor_ie(new_res, ie); is_multi = 0; break; case WPS_IE_VENDOR_TYPE: old_ie_buff = wpa_bss_get_vendor_ie_multi(old, ie); - new_ie_buff = wpa_scan_get_vendor_ie_multi(new, ie); + new_ie_buff = wpa_scan_get_vendor_ie_multi(new_res, ie); is_multi = 1; break; case WLAN_EID_RSN: case WLAN_EID_SUPP_RATES: case WLAN_EID_EXT_SUPP_RATES: old_ie = wpa_bss_get_ie(old, ie); - new_ie = wpa_scan_get_ie(new, ie); + new_ie = wpa_scan_get_ie(new_res, ie); is_multi = 0; break; default: @@ -454,15 +458,15 @@ static int are_ies_equal(const struct wpa_bss *old, static u32 wpa_bss_compare_res(const struct wpa_bss *old, - const struct wpa_scan_res *new) + const struct wpa_scan_res *new_res) { u32 changes = 0; - int caps_diff = old->caps ^ new->caps; + int caps_diff = old->caps ^ new_res->caps; - if (old->freq != new->freq) + if (old->freq != new_res->freq) changes |= WPA_BSS_FREQ_CHANGED_FLAG; - if (old->level != new->level) + if (old->level != new_res->level) changes |= WPA_BSS_SIGNAL_CHANGED_FLAG; if (caps_diff & IEEE80211_CAP_PRIVACY) @@ -471,22 +475,22 @@ static u32 wpa_bss_compare_res(const struct wpa_bss *old, if (caps_diff & IEEE80211_CAP_IBSS) changes |= WPA_BSS_MODE_CHANGED_FLAG; - if (old->ie_len == new->ie_len && - os_memcmp(old + 1, new + 1, old->ie_len) == 0) + if (old->ie_len == new_res->ie_len && + os_memcmp(old + 1, new_res + 1, old->ie_len) == 0) return changes; changes |= WPA_BSS_IES_CHANGED_FLAG; - if (!are_ies_equal(old, new, WPA_IE_VENDOR_TYPE)) + if (!are_ies_equal(old, new_res, WPA_IE_VENDOR_TYPE)) changes |= WPA_BSS_WPAIE_CHANGED_FLAG; - if (!are_ies_equal(old, new, WLAN_EID_RSN)) + if (!are_ies_equal(old, new_res, WLAN_EID_RSN)) changes |= WPA_BSS_RSNIE_CHANGED_FLAG; - if (!are_ies_equal(old, new, WPS_IE_VENDOR_TYPE)) + if (!are_ies_equal(old, new_res, WPS_IE_VENDOR_TYPE)) changes |= WPA_BSS_WPS_CHANGED_FLAG; - if (!are_ies_equal(old, new, WLAN_EID_SUPP_RATES) || - !are_ies_equal(old, new, WLAN_EID_EXT_SUPP_RATES)) + if (!are_ies_equal(old, new_res, WLAN_EID_SUPP_RATES) || + !are_ies_equal(old, new_res, WLAN_EID_EXT_SUPP_RATES)) changes |= WPA_BSS_RATES_CHANGED_FLAG; return changes; @@ -534,6 +538,9 @@ wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, u32 changes; changes = wpa_bss_compare_res(bss, res); + if (changes & WPA_BSS_FREQ_CHANGED_FLAG) + wpa_printf(MSG_DEBUG, "BSS: " MACSTR " changed freq %d --> %d", + MAC2STR(bss->bssid), bss->freq, res->freq); bss->scan_miss_count = 0; bss->last_update_idx = wpa_s->bss_update_idx; wpa_bss_copy_res(bss, res, fetch_time); @@ -652,7 +659,7 @@ void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s, MACSTR, MAC2STR(res->bssid)); return; } - if (ssid[1] > 32) { + if (ssid[1] > SSID_MAX_LEN) { wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Too long SSID IE included for " MACSTR, MAC2STR(res->bssid)); return; @@ -679,7 +686,7 @@ void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s, * (to save memory) */ mesh = wpa_scan_get_ie(res, WLAN_EID_MESH_ID); - if (mesh && mesh[1] <= 32) + if (mesh && mesh[1] <= SSID_MAX_LEN) ssid = mesh; bss = wpa_bss_get(wpa_s, res->bssid, ssid + 2, ssid[1]); @@ -828,16 +835,6 @@ void wpa_bss_flush_by_age(struct wpa_supplicant *wpa_s, int age) } -static void wpa_bss_timeout(void *eloop_ctx, void *timeout_ctx) -{ - struct wpa_supplicant *wpa_s = eloop_ctx; - - wpa_bss_flush_by_age(wpa_s, wpa_s->conf->bss_expiration_age); - eloop_register_timeout(WPA_BSS_EXPIRATION_PERIOD, 0, - wpa_bss_timeout, wpa_s, NULL); -} - - /** * wpa_bss_init - Initialize BSS table * @wpa_s: Pointer to wpa_supplicant data @@ -850,8 +847,6 @@ int wpa_bss_init(struct wpa_supplicant *wpa_s) { dl_list_init(&wpa_s->bss); dl_list_init(&wpa_s->bss_id); - eloop_register_timeout(WPA_BSS_EXPIRATION_PERIOD, 0, - wpa_bss_timeout, wpa_s, NULL); return 0; } @@ -883,7 +878,6 @@ void wpa_bss_flush(struct wpa_supplicant *wpa_s) */ void wpa_bss_deinit(struct wpa_supplicant *wpa_s) { - eloop_cancel_timeout(wpa_bss_timeout, wpa_s, NULL); wpa_bss_flush(wpa_s); } diff --git a/contrib/wpa/wpa_supplicant/bss.h b/contrib/wpa/wpa_supplicant/bss.h index 634aa3c..b215380 100644 --- a/contrib/wpa/wpa_supplicant/bss.h +++ b/contrib/wpa/wpa_supplicant/bss.h @@ -69,7 +69,7 @@ struct wpa_bss { /** HESSID */ u8 hessid[ETH_ALEN]; /** SSID */ - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; /** Length of SSID */ size_t ssid_len; /** Frequency of the channel in MHz (e.g., 2412 = channel 1) */ diff --git a/contrib/wpa/wpa_supplicant/config.c b/contrib/wpa/wpa_supplicant/config.c index 8e6cd20..b1adab7 100644 --- a/contrib/wpa/wpa_supplicant/config.c +++ b/contrib/wpa/wpa_supplicant/config.c @@ -15,6 +15,7 @@ #include "rsn_supp/wpa.h" #include "eap_peer/eap.h" #include "p2p/p2p.h" +#include "fst/fst.h" #include "config.h" @@ -967,6 +968,13 @@ static int wpa_config_parse_group(const struct parse_data *data, val = wpa_config_parse_cipher(line, value); if (val == -1) return -1; + + /* + * Backwards compatibility - filter out WEP ciphers that were previously + * allowed. + */ + val &= ~(WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40); + if (val & ~WPA_ALLOWED_GROUP_CIPHERS) { wpa_printf(MSG_ERROR, "Line %d: not allowed group cipher " "(0x%x).", line, val); @@ -1296,6 +1304,7 @@ static int wpa_config_parse_eap(const struct parse_data *data, } +#ifndef NO_CONFIG_WRITE static char * wpa_config_write_eap(const struct parse_data *data, struct wpa_ssid *ssid) { @@ -1329,6 +1338,7 @@ static char * wpa_config_write_eap(const struct parse_data *data, return buf; } +#endif /* NO_CONFIG_WRITE */ static int wpa_config_parse_password(const struct parse_data *data, @@ -1411,6 +1421,7 @@ static int wpa_config_parse_password(const struct parse_data *data, } +#ifndef NO_CONFIG_WRITE static char * wpa_config_write_password(const struct parse_data *data, struct wpa_ssid *ssid) { @@ -1444,6 +1455,7 @@ static char * wpa_config_write_password(const struct parse_data *data, return buf; } +#endif /* NO_CONFIG_WRITE */ #endif /* IEEE8021X_EAPOL */ @@ -1810,12 +1822,13 @@ static char * wpa_config_write_mesh_basic_rates(const struct parse_data *data, * functions. */ static const struct parse_data ssid_fields[] = { - { STR_RANGE(ssid, 0, MAX_SSID_LEN) }, + { STR_RANGE(ssid, 0, SSID_MAX_LEN) }, { INT_RANGE(scan_ssid, 0, 1) }, { FUNC(bssid) }, { FUNC(bssid_blacklist) }, { FUNC(bssid_whitelist) }, { FUNC_KEY(psk) }, + { INT(mem_only_psk) }, { FUNC(proto) }, { FUNC(key_mgmt) }, { INT(bg_scan_period) }, @@ -2257,6 +2270,7 @@ void wpa_config_free(struct wpa_config *config) os_free(config->osu_dir); os_free(config->bgscan); os_free(config->wowlan_triggers); + os_free(config->fst_group_id); os_free(config); } @@ -2516,6 +2530,9 @@ int wpa_config_set_quoted(struct wpa_ssid *ssid, const char *var, */ char ** wpa_config_get_all(struct wpa_ssid *ssid, int get_keys) { +#ifdef NO_CONFIG_WRITE + return NULL; +#else /* NO_CONFIG_WRITE */ const struct parse_data *field; char *key, *value; size_t i; @@ -2561,6 +2578,7 @@ err: os_free(value++); os_free(props); return NULL; +#endif /* NO_CONFIG_WRITE */ } @@ -2947,7 +2965,7 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var, if (os_strcmp(var, "excluded_ssid") == 0) { struct excluded_ssid *e; - if (len > MAX_SSID_LEN) { + if (len > SSID_MAX_LEN) { wpa_printf(MSG_ERROR, "Line %d: invalid " "excluded_ssid length %d", line, (int) len); os_free(val); @@ -3499,9 +3517,12 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface, config->user_mpm = DEFAULT_USER_MPM; config->max_peer_links = DEFAULT_MAX_PEER_LINKS; config->mesh_max_inactivity = DEFAULT_MESH_MAX_INACTIVITY; + config->dot11RSNASAERetransPeriod = + DEFAULT_DOT11_RSNA_SAE_RETRANS_PERIOD; config->fast_reauth = DEFAULT_FAST_REAUTH; config->p2p_go_intent = DEFAULT_P2P_GO_INTENT; config->p2p_intra_bss = DEFAULT_P2P_INTRA_BSS; + config->p2p_go_freq_change_policy = DEFAULT_P2P_GO_FREQ_MOVE; config->p2p_go_max_inactivity = DEFAULT_P2P_GO_MAX_INACTIVITY; config->p2p_optimize_listen_chan = DEFAULT_P2P_OPTIMIZE_LISTEN_CHAN; config->p2p_go_ctwindow = DEFAULT_P2P_GO_CTWINDOW; @@ -4052,6 +4073,31 @@ static int wpa_config_get_str(const char *name, struct wpa_config *config, } +#ifdef CONFIG_P2P +static int wpa_config_get_ipv4(const char *name, struct wpa_config *config, + long offset, char *buf, size_t buflen, + int pretty_print) +{ + void *val = ((u8 *) config) + (long) offset; + int res; + char addr[INET_ADDRSTRLEN]; + + if (!val || !inet_ntop(AF_INET, val, addr, sizeof(addr))) + return -1; + + if (pretty_print) + res = os_snprintf(buf, buflen, "%s=%s\n", name, addr); + else + res = os_snprintf(buf, buflen, "%s", addr); + + if (os_snprintf_error(buflen, res)) + res = -1; + + return res; +} +#endif /* CONFIG_P2P */ + + #ifdef OFFSET #undef OFFSET #endif /* OFFSET */ @@ -4067,7 +4113,8 @@ static int wpa_config_get_str(const char *name, struct wpa_config *config, #define STR(f) _STR(f), NULL, NULL #define STR_RANGE(f, min, max) _STR(f), (void *) min, (void *) max #define BIN(f) #f, wpa_global_config_parse_bin, NULL, OFFSET(f), NULL, NULL -#define IPV4(f) #f, wpa_global_config_parse_ipv4, NULL, OFFSET(f), NULL, NULL +#define IPV4(f) #f, wpa_global_config_parse_ipv4, wpa_config_get_ipv4, \ + OFFSET(f), NULL, NULL static const struct global_parse_data global_fields[] = { #ifdef CONFIG_CTRL_IFACE @@ -4086,6 +4133,7 @@ static const struct global_parse_data global_fields[] = { { INT(user_mpm), 0 }, { INT_RANGE(max_peer_links, 0, 255), 0 }, { INT(mesh_max_inactivity), 0 }, + { INT(dot11RSNASAERetransPeriod), 0 }, #endif /* CONFIG_MESH */ { INT(disable_scan_offload), 0 }, { INT(fast_reauth), 0 }, @@ -4106,7 +4154,8 @@ static const struct global_parse_data global_fields[] = { { FUNC_NO_VAR(load_dynamic_eap), 0 }, #ifdef CONFIG_WPS { FUNC(uuid), CFG_CHANGED_UUID }, - { STR_RANGE(device_name, 0, 32), CFG_CHANGED_DEVICE_NAME }, + { STR_RANGE(device_name, 0, WPS_DEV_NAME_MAX_LEN), + CFG_CHANGED_DEVICE_NAME }, { STR_RANGE(manufacturer, 0, 64), CFG_CHANGED_WPS_STRING }, { STR_RANGE(model_name, 0, 32), CFG_CHANGED_WPS_STRING }, { STR_RANGE(model_number, 0, 32), CFG_CHANGED_WPS_STRING }, @@ -4119,8 +4168,8 @@ static const struct global_parse_data global_fields[] = { #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P { FUNC(sec_device_type), CFG_CHANGED_SEC_DEVICE_TYPE }, - { INT(p2p_listen_reg_class), 0 }, - { INT(p2p_listen_channel), 0 }, + { INT(p2p_listen_reg_class), CFG_CHANGED_P2P_LISTEN_CHANNEL }, + { INT(p2p_listen_channel), CFG_CHANGED_P2P_LISTEN_CHANNEL }, { INT(p2p_oper_reg_class), CFG_CHANGED_P2P_OPER_CHANNEL }, { INT(p2p_oper_channel), CFG_CHANGED_P2P_OPER_CHANNEL }, { INT_RANGE(p2p_go_intent, 0, 15), 0 }, @@ -4128,6 +4177,7 @@ static const struct global_parse_data global_fields[] = { { INT_RANGE(persistent_reconnect, 0, 1), 0 }, { INT_RANGE(p2p_intra_bss, 0, 1), CFG_CHANGED_P2P_INTRA_BSS }, { INT(p2p_group_idle), 0 }, + { INT_RANGE(p2p_go_freq_change_policy, 0, P2P_GO_FREQ_MOVE_MAX), 0 }, { INT_RANGE(p2p_passphrase_len, 8, 63), CFG_CHANGED_P2P_PASSPHRASE_LEN }, { FUNC(p2p_pref_chan), CFG_CHANGED_P2P_PREF_CHAN }, @@ -4144,6 +4194,7 @@ static const struct global_parse_data global_fields[] = { { IPV4(ip_addr_mask), 0 }, { IPV4(ip_addr_start), 0 }, { IPV4(ip_addr_end), 0 }, + { INT_RANGE(p2p_cli_probe, 0, 1), 0 }, #endif /* CONFIG_P2P */ { FUNC(country), CFG_CHANGED_COUNTRY }, { INT(bss_max_count), 0 }, @@ -4189,6 +4240,12 @@ static const struct global_parse_data global_fields[] = { { INT(key_mgmt_offload), 0}, { INT(passive_scan), 0 }, { INT(reassoc_same_bss_optim), 0 }, + { INT(wps_priority), 0}, +#ifdef CONFIG_FST + { STR_RANGE(fst_group_id, 1, FST_MAX_GROUP_ID_LEN), 0 }, + { INT_RANGE(fst_priority, 1, FST_MAX_PRIO_VALUE), 0 }, + { INT_RANGE(fst_llt, 1, FST_MAX_LLT_MS), 0 }, +#endif /* CONFIG_FST */ }; #undef FUNC diff --git a/contrib/wpa/wpa_supplicant/config.h b/contrib/wpa/wpa_supplicant/config.h index 34b754e..627f38b 100644 --- a/contrib/wpa/wpa_supplicant/config.h +++ b/contrib/wpa/wpa_supplicant/config.h @@ -18,6 +18,11 @@ #define DEFAULT_USER_MPM 1 #define DEFAULT_MAX_PEER_LINKS 99 #define DEFAULT_MESH_MAX_INACTIVITY 300 +/* + * The default dot11RSNASAERetransPeriod is defined as 40 ms in the standard, + * but use 1000 ms in practice to avoid issues on low power CPUs. + */ +#define DEFAULT_DOT11_RSNA_SAE_RETRANS_PERIOD 1000 #define DEFAULT_FAST_REAUTH 1 #define DEFAULT_P2P_GO_INTENT 7 #define DEFAULT_P2P_INTRA_BSS 1 @@ -37,6 +42,7 @@ #include "config_ssid.h" #include "wps/wps.h" +#include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" @@ -241,7 +247,7 @@ struct wpa_cred { char *phase2; struct excluded_ssid { - u8 ssid[MAX_SSID_LEN]; + u8 ssid[SSID_MAX_LEN]; size_t ssid_len; } *excluded_ssid; size_t num_excluded_ssid; @@ -400,6 +406,11 @@ struct wpa_config { * one by one until the driver reports successful association; each * network block should have explicit security policy (i.e., only one * option in the lists) for key_mgmt, pairwise, group, proto variables. + * + * Note: ap_scan=2 should not be used with the nl80211 driver interface + * (the current Linux interface). ap_scan=1 is optimized work working + * with nl80211. For finding networks using hidden SSID, scan_ssid=1 in + * the network block can be used with nl80211. */ int ap_scan; @@ -733,6 +744,34 @@ struct wpa_config { int p2p_group_idle; /** + * p2p_go_freq_change_policy - The GO frequency change policy + * + * This controls the behavior of the GO when there is a change in the + * map of the currently used frequencies in case more than one channel + * is supported. + * + * @P2P_GO_FREQ_MOVE_SCM: Prefer working in a single channel mode if + * possible. In case the GO is the only interface using its frequency + * and there are other station interfaces on other frequencies, the GO + * will migrate to one of these frequencies. + * + * @P2P_GO_FREQ_MOVE_SCM_PEER_SUPPORTS: Same as P2P_GO_FREQ_MOVE_SCM, + * but a transition is possible only in case one of the other used + * frequencies is one of the frequencies in the intersection of the + * frequency list of the local device and the peer device. + * + * @P2P_GO_FREQ_MOVE_STAY: Prefer to stay on the current frequency. + */ + 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_change_policy; + +#define DEFAULT_P2P_GO_FREQ_MOVE P2P_GO_FREQ_MOVE_STAY + + /** * p2p_passphrase_len - Passphrase length (8..63) for P2P GO * * This parameter controls the length of the random passphrase that is @@ -967,6 +1006,18 @@ struct wpa_config { int p2p_no_group_iface; /** + * p2p_cli_probe - Enable/disable P2P CLI probe request handling + * + * If this parameter is set to 1, a connected P2P Client will receive + * and handle Probe Request frames. Setting this parameter to 0 + * disables this option. Default value: 0. + * + * Note: Setting this property at run time takes effect on the following + * interface state transition to/from the WPA_COMPLETED state. + */ + int p2p_cli_probe; + + /** * okc - Whether to enable opportunistic key caching by default * * By default, OKC is disabled unless enabled by the per-network @@ -1148,6 +1199,15 @@ struct wpa_config { int mesh_max_inactivity; /** + * dot11RSNASAERetransPeriod - Timeout to retransmit SAE Auth frame + * + * This timeout value is used in mesh STA to retransmit + * SAE Authentication frame. + * By default: 1000 milliseconds. + */ + int dot11RSNASAERetransPeriod; + + /** * passive_scan - Whether to force passive scan for network connection * * This parameter can be used to force only passive scanning to be used @@ -1163,6 +1223,30 @@ struct wpa_config { * reassoc_same_bss_optim - Whether to optimize reassoc-to-same-BSS */ int reassoc_same_bss_optim; + + /** + * wps_priority - Priority for the networks added through WPS + * + * This priority value will be set to each network profile that is added + * by executing the WPS protocol. + */ + int wps_priority; + + /** + * fst_group_id - FST group ID + */ + char *fst_group_id; + + /** + * fst_priority - priority of the interface within the FST group + */ + int fst_priority; + + /** + * fst_llt - default FST LLT (Link-Lost Timeout) to be used for the + * interface. + */ + int fst_llt; }; diff --git a/contrib/wpa/wpa_supplicant/config_file.c b/contrib/wpa/wpa_supplicant/config_file.c index 3d3a6e4..fb438ea 100644 --- a/contrib/wpa/wpa_supplicant/config_file.c +++ b/contrib/wpa/wpa_supplicant/config_file.c @@ -501,7 +501,12 @@ static void write_bssid(FILE *f, struct wpa_ssid *ssid) static void write_psk(FILE *f, struct wpa_ssid *ssid) { - char *value = wpa_config_get(ssid, "psk"); + char *value; + + if (ssid->mem_only_psk) + return; + + value = wpa_config_get(ssid, "psk"); if (value == NULL) return; fprintf(f, "\tpsk=%s\n", value); @@ -673,6 +678,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) write_str(f, "bssid_blacklist", ssid); write_str(f, "bssid_whitelist", ssid); write_psk(f, ssid); + INT(mem_only_psk); write_proto(f, ssid); write_key_mgmt(f, ssid); INT_DEF(bg_scan_period, DEFAULT_BG_SCAN_PERIOD); @@ -1010,13 +1016,13 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) if (config->driver_param) fprintf(f, "driver_param=%s\n", config->driver_param); if (config->dot11RSNAConfigPMKLifetime) - fprintf(f, "dot11RSNAConfigPMKLifetime=%d\n", + fprintf(f, "dot11RSNAConfigPMKLifetime=%u\n", config->dot11RSNAConfigPMKLifetime); if (config->dot11RSNAConfigPMKReauthThreshold) - fprintf(f, "dot11RSNAConfigPMKReauthThreshold=%d\n", + fprintf(f, "dot11RSNAConfigPMKReauthThreshold=%u\n", config->dot11RSNAConfigPMKReauthThreshold); if (config->dot11RSNAConfigSATimeout) - fprintf(f, "dot11RSNAConfigSATimeout=%d\n", + fprintf(f, "dot11RSNAConfigSATimeout=%u\n", config->dot11RSNAConfigSATimeout); if (config->update_config) fprintf(f, "update_config=%d\n", config->update_config); @@ -1064,27 +1070,27 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P if (config->p2p_listen_reg_class) - fprintf(f, "p2p_listen_reg_class=%u\n", + fprintf(f, "p2p_listen_reg_class=%d\n", config->p2p_listen_reg_class); if (config->p2p_listen_channel) - fprintf(f, "p2p_listen_channel=%u\n", + fprintf(f, "p2p_listen_channel=%d\n", config->p2p_listen_channel); if (config->p2p_oper_reg_class) - fprintf(f, "p2p_oper_reg_class=%u\n", + fprintf(f, "p2p_oper_reg_class=%d\n", config->p2p_oper_reg_class); if (config->p2p_oper_channel) - fprintf(f, "p2p_oper_channel=%u\n", config->p2p_oper_channel); + fprintf(f, "p2p_oper_channel=%d\n", config->p2p_oper_channel); if (config->p2p_go_intent != DEFAULT_P2P_GO_INTENT) - fprintf(f, "p2p_go_intent=%u\n", config->p2p_go_intent); + fprintf(f, "p2p_go_intent=%d\n", config->p2p_go_intent); if (config->p2p_ssid_postfix) fprintf(f, "p2p_ssid_postfix=%s\n", config->p2p_ssid_postfix); if (config->persistent_reconnect) - fprintf(f, "persistent_reconnect=%u\n", + fprintf(f, "persistent_reconnect=%d\n", config->persistent_reconnect); if (config->p2p_intra_bss != DEFAULT_P2P_INTRA_BSS) - fprintf(f, "p2p_intra_bss=%u\n", config->p2p_intra_bss); + fprintf(f, "p2p_intra_bss=%d\n", config->p2p_intra_bss); if (config->p2p_group_idle) - fprintf(f, "p2p_group_idle=%u\n", config->p2p_group_idle); + fprintf(f, "p2p_group_idle=%d\n", config->p2p_group_idle); if (config->p2p_passphrase_len) fprintf(f, "p2p_passphrase_len=%u\n", config->p2p_passphrase_len); @@ -1112,19 +1118,24 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) fprintf(f, "p2p_optimize_listen_chan=%d\n", config->p2p_optimize_listen_chan); if (config->p2p_go_ht40) - fprintf(f, "p2p_go_ht40=%u\n", config->p2p_go_ht40); + fprintf(f, "p2p_go_ht40=%d\n", config->p2p_go_ht40); if (config->p2p_go_vht) - fprintf(f, "p2p_go_vht=%u\n", config->p2p_go_vht); + fprintf(f, "p2p_go_vht=%d\n", config->p2p_go_vht); if (config->p2p_go_ctwindow != DEFAULT_P2P_GO_CTWINDOW) - fprintf(f, "p2p_go_ctwindow=%u\n", config->p2p_go_ctwindow); + fprintf(f, "p2p_go_ctwindow=%d\n", config->p2p_go_ctwindow); if (config->p2p_disabled) - fprintf(f, "p2p_disabled=%u\n", config->p2p_disabled); + fprintf(f, "p2p_disabled=%d\n", config->p2p_disabled); if (config->p2p_no_group_iface) - fprintf(f, "p2p_no_group_iface=%u\n", + fprintf(f, "p2p_no_group_iface=%d\n", config->p2p_no_group_iface); if (config->p2p_ignore_shared_freq) - fprintf(f, "p2p_ignore_shared_freq=%u\n", + fprintf(f, "p2p_ignore_shared_freq=%d\n", config->p2p_ignore_shared_freq); + if (config->p2p_cli_probe) + fprintf(f, "p2p_cli_probe=%d\n", config->p2p_cli_probe); + 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); #endif /* CONFIG_P2P */ if (config->country[0] && config->country[1]) { fprintf(f, "country=%c%c\n", @@ -1144,14 +1155,14 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) if (config->max_num_sta != DEFAULT_MAX_NUM_STA) fprintf(f, "max_num_sta=%u\n", config->max_num_sta); if (config->disassoc_low_ack) - fprintf(f, "disassoc_low_ack=%u\n", config->disassoc_low_ack); + fprintf(f, "disassoc_low_ack=%d\n", config->disassoc_low_ack); #ifdef CONFIG_HS20 if (config->hs20) fprintf(f, "hs20=1\n"); #endif /* CONFIG_HS20 */ #ifdef CONFIG_INTERWORKING if (config->interworking) - fprintf(f, "interworking=%u\n", config->interworking); + fprintf(f, "interworking=%d\n", config->interworking); if (!is_zero_ether_addr(config->hessid)) fprintf(f, "hessid=" MACSTR "\n", MAC2STR(config->hessid)); if (config->access_network_type != DEFAULT_ACCESS_NETWORK_TYPE) @@ -1159,7 +1170,7 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) config->access_network_type); #endif /* CONFIG_INTERWORKING */ if (config->pbc_in_m1) - fprintf(f, "pbc_in_m1=%u\n", config->pbc_in_m1); + fprintf(f, "pbc_in_m1=%d\n", config->pbc_in_m1); if (config->wps_nfc_pw_from_config) { if (config->wps_nfc_dev_pw_id) fprintf(f, "wps_nfc_dev_pw_id=%d\n", @@ -1218,7 +1229,7 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) int i; fprintf(f, "freq_list="); for (i = 0; config->freq_list[i]; i++) { - fprintf(f, "%s%u", i > 0 ? " " : "", + fprintf(f, "%s%d", i > 0 ? " " : "", config->freq_list[i]); } fprintf(f, "\n"); @@ -1259,7 +1270,7 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) fprintf(f, "preassoc_mac_addr=%d\n", config->preassoc_mac_addr); if (config->key_mgmt_offload != DEFAULT_KEY_MGMT_OFFLOAD) - fprintf(f, "key_mgmt_offload=%u\n", config->key_mgmt_offload); + fprintf(f, "key_mgmt_offload=%d\n", config->key_mgmt_offload); if (config->user_mpm != DEFAULT_USER_MPM) fprintf(f, "user_mpm=%d\n", config->user_mpm); @@ -1274,12 +1285,20 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) fprintf(f, "mesh_max_inactivity=%d\n", config->mesh_max_inactivity); + if (config->dot11RSNASAERetransPeriod != + DEFAULT_DOT11_RSNA_SAE_RETRANS_PERIOD) + fprintf(f, "dot11RSNASAERetransPeriod=%d\n", + config->dot11RSNASAERetransPeriod); + if (config->passive_scan) fprintf(f, "passive_scan=%d\n", config->passive_scan); if (config->reassoc_same_bss_optim) fprintf(f, "reassoc_same_bss_optim=%d\n", config->reassoc_same_bss_optim); + + if (config->wps_priority) + fprintf(f, "wps_priority=%d\n", config->wps_priority); } #endif /* CONFIG_NO_CONFIG_WRITE */ @@ -1342,6 +1361,8 @@ int wpa_config_write(const char *name, struct wpa_config *config) } #endif /* CONFIG_NO_CONFIG_BLOBS */ + os_fdatasync(f); + fclose(f); if (tmp_name) { diff --git a/contrib/wpa/wpa_supplicant/config_ssid.h b/contrib/wpa/wpa_supplicant/config_ssid.h index 7c826cf..7ef326c 100644 --- a/contrib/wpa/wpa_supplicant/config_ssid.h +++ b/contrib/wpa/wpa_supplicant/config_ssid.h @@ -13,8 +13,6 @@ #include "utils/list.h" #include "eap_peer/eap_config.h" -#define MAX_SSID_LEN 32 - #define DEFAULT_EAP_WORKAROUND ((unsigned int) -1) #define DEFAULT_EAPOL_FLAGS (EAPOL_FLAG_REQUIRE_KEY_UNICAST | \ @@ -22,8 +20,7 @@ #define DEFAULT_PROTO (WPA_PROTO_WPA | WPA_PROTO_RSN) #define DEFAULT_KEY_MGMT (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X) #define DEFAULT_PAIRWISE (WPA_CIPHER_CCMP | WPA_CIPHER_TKIP) -#define DEFAULT_GROUP (WPA_CIPHER_CCMP | WPA_CIPHER_TKIP | \ - WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40) +#define DEFAULT_GROUP (WPA_CIPHER_CCMP | WPA_CIPHER_TKIP) #define DEFAULT_FRAGMENT_SIZE 1398 #define DEFAULT_BG_SCAN_PERIOD -1 @@ -181,6 +178,14 @@ struct wpa_ssid { char *ext_psk; /** + * mem_only_psk - Whether to keep PSK/passphrase only in memory + * + * 0 = allow psk/passphrase to be stored to the configuration file + * 1 = do not store psk/passphrase to the configuration file + */ + int mem_only_psk; + + /** * pairwise_cipher - Bitfield of allowed pairwise ciphers, WPA_CIPHER_* */ int pairwise_cipher; @@ -220,7 +225,9 @@ struct wpa_ssid { * * scan_ssid can be used to scan for APs using hidden SSIDs. * Note: Many drivers do not support this. ap_mode=2 can be used with - * such drivers to use hidden SSIDs. + * such drivers to use hidden SSIDs. Note2: Most nl80211-based drivers + * do support scan_ssid=1 and that should be used with them instead of + * ap_scan=2. */ int scan_ssid; diff --git a/contrib/wpa/wpa_supplicant/ctrl_iface.c b/contrib/wpa/wpa_supplicant/ctrl_iface.c index b4aefb6..3b97806 100644 --- a/contrib/wpa/wpa_supplicant/ctrl_iface.c +++ b/contrib/wpa/wpa_supplicant/ctrl_iface.c @@ -28,6 +28,8 @@ #include "rsn_supp/pmksa_cache.h" #include "l2_packet/l2_packet.h" #include "wps/wps.h" +#include "fst/fst.h" +#include "fst/fst_ctrl_iface.h" #include "config.h" #include "wpa_supplicant_i.h" #include "driver_i.h" @@ -153,7 +155,8 @@ static int set_disallow_aps(struct wpa_supplicant *wpa_s, char *val) } ssid = ns; - if ((end - pos) & 0x01 || end - pos > 2 * 32 || + if ((end - pos) & 0x01 || + end - pos > 2 * SSID_MAX_LEN || hexstr2bin(pos, ssid[ssid_count].ssid, (end - pos) / 2) < 0) { os_free(ssid); @@ -283,6 +286,30 @@ static int wpas_ctrl_pno(struct wpa_supplicant *wpa_s, char *cmd) } +static int wpas_ctrl_set_band(struct wpa_supplicant *wpa_s, char *band) +{ + union wpa_event_data event; + + if (os_strcmp(band, "AUTO") == 0) + wpa_s->setband = WPA_SETBAND_AUTO; + else if (os_strcmp(band, "5G") == 0) + wpa_s->setband = WPA_SETBAND_5G; + else if (os_strcmp(band, "2G") == 0) + wpa_s->setband = WPA_SETBAND_2G; + else + return -1; + + if (wpa_drv_setband(wpa_s, wpa_s->setband) == 0) { + os_memset(&event, 0, sizeof(event)); + event.channel_list_changed.initiator = REGDOM_SET_BY_USER; + event.channel_list_changed.type = REGDOM_TYPE_UNKNOWN; + wpa_supplicant_event(wpa_s, EVENT_CHANNEL_LIST_CHANGED, &event); + } + + return 0; +} + + static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, char *cmd) { @@ -446,14 +473,7 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, ret = wpas_ctrl_set_blob(wpa_s, value); #endif /* CONFIG_NO_CONFIG_BLOBS */ } else if (os_strcasecmp(cmd, "setband") == 0) { - if (os_strcmp(value, "AUTO") == 0) - wpa_s->setband = WPA_SETBAND_AUTO; - else if (os_strcmp(value, "5G") == 0) - wpa_s->setband = WPA_SETBAND_5G; - else if (os_strcmp(value, "2G") == 0) - wpa_s->setband = WPA_SETBAND_2G; - else - ret = -1; + ret = wpas_ctrl_set_band(wpa_s, value); } else { value[-1] = '='; ret = wpa_config_process_global(wpa_s->conf, cmd, -1); @@ -759,6 +779,33 @@ static int wpa_supplicant_ctrl_iface_tdls_cancel_chan_switch( return wpa_tdls_disable_chan_switch(wpa_s->wpa, peer); } + +static int wpa_supplicant_ctrl_iface_tdls_link_status( + struct wpa_supplicant *wpa_s, const char *addr, + char *buf, size_t buflen) +{ + u8 peer[ETH_ALEN]; + const char *tdls_status; + int ret; + + if (hwaddr_aton(addr, peer)) { + wpa_printf(MSG_DEBUG, + "CTRL_IFACE TDLS_LINK_STATUS: Invalid address '%s'", + addr); + return -1; + } + wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_LINK_STATUS " MACSTR, + MAC2STR(peer)); + + tdls_status = wpa_tdls_get_link_status(wpa_s->wpa, peer); + wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_LINK_STATUS: %s", tdls_status); + ret = os_snprintf(buf, buflen, "TDLS link status: %s\n", tdls_status); + if (os_snprintf_error(buflen, ret)) + return -1; + + return ret; +} + #endif /* CONFIG_TDLS */ @@ -1728,7 +1775,7 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, if (ssid) { u8 *_ssid = ssid->ssid; size_t ssid_len = ssid->ssid_len; - u8 ssid_buf[MAX_SSID_LEN]; + u8 ssid_buf[SSID_MAX_LEN]; if (ssid_len == 0) { int _res = wpa_drv_get_ssid(wpa_s, ssid_buf); if (_res < 0) @@ -2089,45 +2136,6 @@ static int wpa_supplicant_ctrl_iface_blacklist(struct wpa_supplicant *wpa_s, } -static const char * debug_level_str(int level) -{ - switch (level) { - case MSG_EXCESSIVE: - return "EXCESSIVE"; - case MSG_MSGDUMP: - return "MSGDUMP"; - case MSG_DEBUG: - return "DEBUG"; - case MSG_INFO: - return "INFO"; - case MSG_WARNING: - return "WARNING"; - case MSG_ERROR: - return "ERROR"; - default: - return "?"; - } -} - - -static int str_to_debug_level(const char *s) -{ - if (os_strcasecmp(s, "EXCESSIVE") == 0) - return MSG_EXCESSIVE; - if (os_strcasecmp(s, "MSGDUMP") == 0) - return MSG_MSGDUMP; - if (os_strcasecmp(s, "DEBUG") == 0) - return MSG_DEBUG; - if (os_strcasecmp(s, "INFO") == 0) - return MSG_INFO; - if (os_strcasecmp(s, "WARNING") == 0) - return MSG_WARNING; - if (os_strcasecmp(s, "ERROR") == 0) - return MSG_ERROR; - return -1; -} - - static int wpa_supplicant_ctrl_iface_log_level(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) @@ -2160,7 +2168,7 @@ static int wpa_supplicant_ctrl_iface_log_level(struct wpa_supplicant *wpa_s, } } - if (cmd && os_strlen(cmd)) { + if (os_strlen(cmd)) { int level = str_to_debug_level(cmd); if (level < 0) return -1; @@ -2366,6 +2374,14 @@ static char * wpa_supplicant_ie_txt(char *pos, char *end, const char *proto, } #endif /* CONFIG_SUITEB192 */ + if (data.key_mgmt & WPA_KEY_MGMT_OSEN) { + ret = os_snprintf(pos, end - pos, "%sOSEN", + pos == start ? "" : "+"); + if (os_snprintf_error(end - pos, ret)) + return pos; + pos += ret; + } + pos = wpa_supplicant_cipher_txt(pos, end, data.pairwise_cipher); if (data.capabilities & WPA_CAPABILITY_PREAUTH) { @@ -2433,7 +2449,7 @@ static int wpa_supplicant_ctrl_iface_scan_result( { char *pos, *end; int ret; - const u8 *ie, *ie2, *p2p, *mesh; + const u8 *ie, *ie2, *osen_ie, *p2p, *mesh; mesh = wpa_bss_get_ie(bss, WLAN_EID_MESH_ID); p2p = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE); @@ -2460,8 +2476,12 @@ static int wpa_supplicant_ctrl_iface_scan_result( pos = wpa_supplicant_ie_txt(pos, end, mesh ? "RSN" : "WPA2", ie2, 2 + ie2[1]); } + osen_ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE); + if (osen_ie) + pos = wpa_supplicant_ie_txt(pos, end, "OSEN", + osen_ie, 2 + osen_ie[1]); pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss); - if (!ie && !ie2 && bss->caps & IEEE80211_CAP_PRIVACY) { + if (!ie && !ie2 && !osen_ie && (bss->caps & IEEE80211_CAP_PRIVACY)) { ret = os_snprintf(pos, end - pos, "[WEP]"); if (os_snprintf_error(end - pos, ret)) return -1; @@ -2525,6 +2545,14 @@ static int wpa_supplicant_ctrl_iface_scan_result( pos += ret; } #endif /* CONFIG_HS20 */ +#ifdef CONFIG_FST + if (wpa_bss_get_ie(bss, WLAN_EID_MULTI_BAND)) { + ret = os_snprintf(pos, end - pos, "[FST]"); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } +#endif /* CONFIG_FST */ ret = os_snprintf(pos, end - pos, "\t%s", wpa_ssid_txt(bss->ssid, bss->ssid_len)); @@ -2716,6 +2744,8 @@ static int wpa_supplicant_ctrl_iface_select_network( } } + wpa_s->scan_min_time.sec = 0; + wpa_s->scan_min_time.usec = 0; wpa_supplicant_select_network(wpa_s, ssid); return 0; @@ -2753,6 +2783,8 @@ static int wpa_supplicant_ctrl_iface_enable_network( return 0; } } + wpa_s->scan_min_time.sec = 0; + wpa_s->scan_min_time.usec = 0; wpa_supplicant_enable_network(wpa_s, ssid); return 0; @@ -2836,7 +2868,8 @@ static int wpa_supplicant_ctrl_iface_remove_network( #endif /* CONFIG_SME */ wpa_sm_set_config(wpa_s->wpa, NULL); eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); - wpa_s->own_disconnect_req = 1; + if (wpa_s->wpa_state >= WPA_AUTHENTICATING) + wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); } @@ -2883,7 +2916,8 @@ static int wpa_supplicant_ctrl_iface_remove_network( wpa_sm_set_config(wpa_s->wpa, NULL); eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); - wpa_s->own_disconnect_req = 1; + if (wpa_s->wpa_state >= WPA_AUTHENTICATING) + wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); } @@ -3005,19 +3039,19 @@ static int wpa_supplicant_ctrl_iface_get_network( *name++ = '\0'; id = atoi(cmd); - wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_NETWORK id=%d name='%s'", + wpa_printf(MSG_EXCESSIVE, "CTRL_IFACE: GET_NETWORK id=%d name='%s'", id, name); ssid = wpa_config_get_network(wpa_s->conf, id); if (ssid == NULL) { - wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network " + wpa_printf(MSG_EXCESSIVE, "CTRL_IFACE: Could not find network " "id=%d", id); return -1; } value = wpa_config_get_no_key(ssid, name); if (value == NULL) { - wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to get network " + wpa_printf(MSG_EXCESSIVE, "CTRL_IFACE: Failed to get network " "variable '%s'", name); return -1; } @@ -3035,7 +3069,8 @@ static int wpa_supplicant_ctrl_iface_get_network( static int wpa_supplicant_ctrl_iface_dup_network( - struct wpa_supplicant *wpa_s, char *cmd) + struct wpa_supplicant *wpa_s, char *cmd, + struct wpa_supplicant *dst_wpa_s) { struct wpa_ssid *ssid_s, *ssid_d; char *name, *id, *value; @@ -3054,8 +3089,10 @@ static int wpa_supplicant_ctrl_iface_dup_network( id_s = atoi(cmd); id_d = atoi(id); - wpa_printf(MSG_DEBUG, "CTRL_IFACE: DUP_NETWORK id=%d -> %d name='%s'", - id_s, id_d, name); + + wpa_printf(MSG_DEBUG, + "CTRL_IFACE: DUP_NETWORK ifname=%s->%s id=%d->%d name='%s'", + wpa_s->ifname, dst_wpa_s->ifname, id_s, id_d, name); ssid_s = wpa_config_get_network(wpa_s->conf, id_s); if (ssid_s == NULL) { @@ -3064,7 +3101,7 @@ static int wpa_supplicant_ctrl_iface_dup_network( return -1; } - ssid_d = wpa_config_get_network(wpa_s->conf, id_d); + ssid_d = wpa_config_get_network(dst_wpa_s->conf, id_d); if (ssid_d == NULL) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find " "network id=%d", id_d); @@ -3078,7 +3115,7 @@ static int wpa_supplicant_ctrl_iface_dup_network( return -1; } - ret = wpa_supplicant_ctrl_iface_update_network(wpa_s, ssid_d, name, + ret = wpa_supplicant_ctrl_iface_update_network(dst_wpa_s, ssid_d, name, value); os_free(value); @@ -3889,6 +3926,15 @@ static int wpa_supplicant_ctrl_iface_get_capability( } #endif /* CONFIG_EPR */ +#ifdef CONFIG_FIPS + if (os_strcmp(field, "fips") == 0) { + res = os_snprintf(buf, buflen, "FIPS"); + if (os_snprintf_error(buflen, res)) + return -1; + return res; + } +#endif /* CONFIG_FIPS */ + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'", field); @@ -3937,7 +3983,7 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, size_t i; int ret; char *pos, *end; - const u8 *ie, *ie2; + const u8 *ie, *ie2, *osen_ie; pos = buf; end = buf + buflen; @@ -4054,8 +4100,13 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, if (ie2) pos = wpa_supplicant_ie_txt(pos, end, "WPA2", ie2, 2 + ie2[1]); + osen_ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE); + if (osen_ie) + pos = wpa_supplicant_ie_txt(pos, end, "OSEN", + osen_ie, 2 + osen_ie[1]); pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss); - if (!ie && !ie2 && bss->caps & IEEE80211_CAP_PRIVACY) { + if (!ie && !ie2 && !osen_ie && + (bss->caps & IEEE80211_CAP_PRIVACY)) { ret = os_snprintf(pos, end - pos, "[WEP]"); if (os_snprintf_error(end - pos, ret)) return 0; @@ -4133,9 +4184,10 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, if (mask & WPA_BSS_MASK_WPS_SCAN) { ie = (const u8 *) (bss + 1); ret = wpas_wps_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_WPS */ @@ -4236,6 +4288,15 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, pos += ret; } +#ifdef CONFIG_FST + if (mask & WPA_BSS_MASK_FST) { + ret = fst_ctrl_iface_mb_info(bss->bssid, pos, end - pos); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + } +#endif /* CONFIG_FST */ + if (mask & WPA_BSS_MASK_DELIM) { ret = os_snprintf(pos, end - pos, "====\n"); if (os_snprintf_error(end - pos, ret)) @@ -4548,37 +4609,83 @@ static int p2p_ctrl_find(struct wpa_supplicant *wpa_s, char *cmd) } else search_delay = wpas_p2p_search_delay(wpa_s); + pos = os_strstr(cmd, "freq="); + if (pos) { + pos += 5; + freq = atoi(pos); + if (freq <= 0) + return -1; + } + /* Must be searched for last, because it adds nul termination */ pos = os_strstr(cmd, " seek="); + if (pos) + pos += 6; while (pos && seek_count < P2P_MAX_QUERY_HASH + 1) { char *term; - term = os_strchr(pos + 1, ' '); - _seek[seek_count++] = pos + 6; + _seek[seek_count++] = pos; seek = _seek; - pos = os_strstr(pos + 6, " seek="); - - if (term) - *term = '\0'; + term = os_strchr(pos, ' '); + if (!term) + break; + *term = '\0'; + pos = os_strstr(term + 1, "seek="); + if (pos) + pos += 5; } if (seek_count > P2P_MAX_QUERY_HASH) { seek[0] = NULL; seek_count = 1; } - pos = os_strstr(cmd, "freq="); - if (pos) { - pos += 5; - freq = atoi(pos); - if (freq <= 0) - return -1; - } - return wpas_p2p_find(wpa_s, timeout, type, _dev_type != NULL, _dev_type, _dev_id, search_delay, seek_count, seek, freq); } +static int p2ps_ctrl_parse_cpt_priority(const char *pos, u8 *cpt) +{ + const char *last = NULL; + const char *token; + long int token_len; + unsigned int i; + + /* Expected predefined CPT names delimited by ':' */ + for (i = 0; (token = cstr_token(pos, ": \t", &last)); i++) { + if (i >= P2PS_FEATURE_CAPAB_CPT_MAX) { + wpa_printf(MSG_ERROR, + "P2PS: CPT name list is too long, expected up to %d names", + P2PS_FEATURE_CAPAB_CPT_MAX); + cpt[0] = 0; + return -1; + } + + token_len = last - token; + + if (token_len == 3 && + os_memcmp(token, "UDP", token_len) == 0) { + cpt[i] = P2PS_FEATURE_CAPAB_UDP_TRANSPORT; + } else if (token_len == 3 && + os_memcmp(token, "MAC", token_len) == 0) { + cpt[i] = P2PS_FEATURE_CAPAB_MAC_TRANSPORT; + } else { + wpa_printf(MSG_ERROR, + "P2PS: Unsupported CPT name '%s'", token); + cpt[0] = 0; + return -1; + } + + if (isblank(*last)) { + i++; + break; + } + } + cpt[i] = 0; + return 0; +} + + static struct p2ps_provision * p2p_parse_asp_provision_cmd(const char *cmd) { struct p2ps_provision *p2ps_prov; @@ -4587,6 +4694,7 @@ static struct p2ps_provision * p2p_parse_asp_provision_cmd(const char *cmd) char *info = NULL; u8 role = P2PS_SETUP_NONE; long long unsigned val; + int i; pos = os_strstr(cmd, "info="); if (pos) { @@ -4645,6 +4753,18 @@ static struct p2ps_provision * p2p_parse_asp_provision_cmd(const char *cmd) if (!pos || hwaddr_aton(pos + 12, p2ps_prov->session_mac)) goto invalid_args; + pos = os_strstr(cmd, "cpt="); + if (pos) { + if (p2ps_ctrl_parse_cpt_priority(pos + 4, + p2ps_prov->cpt_priority)) + goto invalid_args; + } else { + p2ps_prov->cpt_priority[0] = P2PS_FEATURE_CAPAB_UDP_TRANSPORT; + } + + for (i = 0; p2ps_prov->cpt_priority[i]; i++) + p2ps_prov->cpt_mask |= p2ps_prov->cpt_priority[i]; + /* force conncap with tstCap (no sanity checks) */ pos = os_strstr(cmd, "tstCap="); if (pos) { @@ -4721,6 +4841,8 @@ static int p2p_ctrl_asp_provision(struct wpa_supplicant *wpa_s, char *cmd) if (!p2ps_prov) return -1; + p2ps_prov->pd_seeker = 1; + return wpas_p2p_prov_disc(wpa_s, addr, NULL, WPAS_P2P_PD_FOR_ASP, p2ps_prov); } @@ -5140,6 +5262,8 @@ static int p2p_ctrl_service_add_asp(struct wpa_supplicant *wpa_s, char *adv_str; u32 auto_accept, adv_id, svc_state, config_methods; char *svc_info = NULL; + char *cpt_prio_str; + u8 cpt_prio[P2PS_FEATURE_CAPAB_CPT_MAX + 1]; pos = os_strchr(cmd, ' '); if (pos == NULL) @@ -5212,6 +5336,19 @@ static int p2p_ctrl_service_add_asp(struct wpa_supplicant *wpa_s, if (pos != NULL) *pos++ = '\0'; + cpt_prio_str = (pos && pos[0]) ? os_strstr(pos, "cpt=") : NULL; + if (cpt_prio_str) { + pos = os_strchr(pos, ' '); + if (pos != NULL) + *pos++ = '\0'; + + if (p2ps_ctrl_parse_cpt_priority(cpt_prio_str + 4, cpt_prio)) + return -1; + } else { + cpt_prio[0] = P2PS_FEATURE_CAPAB_UDP_TRANSPORT; + cpt_prio[1] = 0; + } + /* Service and Response Information are optional */ if (pos && pos[0]) { size_t len; @@ -5229,7 +5366,7 @@ static int p2p_ctrl_service_add_asp(struct wpa_supplicant *wpa_s, return wpas_p2p_service_add_asp(wpa_s, auto_accept, adv_id, adv_str, (u8) svc_state, (u16) config_methods, - svc_info); + svc_info, cpt_prio); } @@ -5299,6 +5436,11 @@ static int p2p_ctrl_service_del_asp(struct wpa_supplicant *wpa_s, char *cmd) { u32 adv_id; + if (os_strcmp(cmd, "all") == 0) { + wpas_p2p_service_flush_asp(wpa_s); + return 0; + } + if (sscanf(cmd, "%x", &adv_id) != 1) return -1; @@ -5449,13 +5591,10 @@ static int p2p_ctrl_invite(struct wpa_supplicant *wpa_s, char *cmd) static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s, - char *cmd, int freq, int ht40, - int vht) + int id, int freq, int ht40, int vht) { - int id; struct wpa_ssid *ssid; - id = atoi(cmd); ssid = wpa_config_get_network(wpa_s->conf, id); if (ssid == NULL || ssid->disabled != 2) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d " @@ -5465,37 +5604,41 @@ static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s, } return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0, ht40, vht, - NULL, 0); + NULL, 0, 0); } static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd) { - int freq = 0, ht40, vht; - char *pos; + 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; + char *token, *context = NULL; - pos = os_strstr(cmd, "freq="); - if (pos) - freq = atoi(pos + 5); + while ((token = str_token(cmd, " ", &context))) { + if (sscanf(token, "freq=%d", &freq) == 1 || + sscanf(token, "persistent=%d", &group_id) == 1) { + continue; + } else if (os_strcmp(token, "ht40") == 0) { + ht40 = 1; + } else if (os_strcmp(token, "vht") == 0) { + vht = 1; + ht40 = 1; + } else if (os_strcmp(token, "persistent") == 0) { + persistent = 1; + } else { + wpa_printf(MSG_DEBUG, + "CTRL: Invalid P2P_GROUP_ADD parameter: '%s'", + token); + return -1; + } + } - vht = (os_strstr(cmd, "vht") != NULL) || wpa_s->conf->p2p_go_vht; - ht40 = (os_strstr(cmd, "ht40") != NULL) || wpa_s->conf->p2p_go_ht40 || - vht; + if (group_id >= 0) + return p2p_ctrl_group_add_persistent(wpa_s, group_id, + freq, ht40, vht); - if (os_strncmp(cmd, "persistent=", 11) == 0) - return p2p_ctrl_group_add_persistent(wpa_s, cmd + 11, freq, - ht40, vht); - if (os_strcmp(cmd, "persistent") == 0 || - os_strncmp(cmd, "persistent ", 11) == 0) - return wpas_p2p_group_add(wpa_s, 1, freq, ht40, vht); - if (os_strncmp(cmd, "freq=", 5) == 0) - return wpas_p2p_group_add(wpa_s, 0, freq, ht40, vht); - if (ht40) - return wpas_p2p_group_add(wpa_s, 0, freq, ht40, vht); - - wpa_printf(MSG_DEBUG, "CTRL: Invalid P2P_GROUP_ADD parameters '%s'", - cmd); - return -1; + return wpas_p2p_group_add(wpa_s, persistent, freq, ht40, vht); } @@ -5625,7 +5768,7 @@ static int p2p_ctrl_disallow_freq(struct wpa_supplicant *wpa_s, freq->min, freq->max); } - wpas_p2p_update_channel_list(wpa_s); + wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_DISALLOW); return 0; } @@ -5842,6 +5985,7 @@ static void p2p_ctrl_flush(struct wpa_supplicant *wpa_s) os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN); wpa_s->force_long_sd = 0; wpas_p2p_stop_find(wpa_s); + wpa_s->parent->p2ps_method_config_any = 0; if (wpa_s->global->p2p) p2p_flush(wpa_s->global->p2p); } @@ -6476,6 +6620,61 @@ static int wpa_supplicant_signal_poll(struct wpa_supplicant *wpa_s, char *buf, pos += ret; } + if (si.avg_beacon_signal) { + ret = os_snprintf(pos, end - pos, + "AVG_BEACON_RSSI=%d\n", si.avg_beacon_signal); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } + + return pos - buf; +} + + +static int wpas_ctrl_iface_get_pref_freq_list( + struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) +{ + unsigned int freq_list[100], num = 100, i; + int ret; + enum wpa_driver_if_type iface_type; + char *pos, *end; + + pos = buf; + end = buf + buflen; + + /* buf: "<interface_type>" */ + if (os_strcmp(cmd, "STATION") == 0) + iface_type = WPA_IF_STATION; + else if (os_strcmp(cmd, "AP") == 0) + iface_type = WPA_IF_AP_BSS; + else if (os_strcmp(cmd, "P2P_GO") == 0) + iface_type = WPA_IF_P2P_GO; + else if (os_strcmp(cmd, "P2P_CLIENT") == 0) + iface_type = WPA_IF_P2P_CLIENT; + else if (os_strcmp(cmd, "IBSS") == 0) + iface_type = WPA_IF_IBSS; + else if (os_strcmp(cmd, "TDLS") == 0) + iface_type = WPA_IF_TDLS; + else + return -1; + + wpa_printf(MSG_DEBUG, + "CTRL_IFACE: GET_PREF_FREQ_LIST iface_type=%d (%s)", + iface_type, buf); + + ret = wpa_drv_get_pref_freq_list(wpa_s, iface_type, &num, freq_list); + if (ret) + return -1; + + for (i = 0; i < num; i++) { + ret = os_snprintf(pos, end - pos, "%s%u", + i > 0 ? "," : "", freq_list[i]); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } + return pos - buf; } @@ -6602,6 +6801,7 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) p2p_wpa_s->p2p_disable_ip_addr_req = 0; os_free(p2p_wpa_s->global->p2p_go_avoid_freq.range); 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; #endif /* CONFIG_P2P */ @@ -6689,6 +6889,8 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) MAC2STR(wpa_s->bssid), MAC2STR(wpa_s->pending_bssid)); } + + eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL); } @@ -6922,6 +7124,8 @@ static void wpas_ctrl_scan(struct wpa_supplicant *wpa_s, char *params, void (*scan_res_handler)(struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res); int *manual_scan_freqs = NULL; + struct wpa_ssid_value *ssid = NULL, *ns; + unsigned int ssid_count = 0; if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { *reply_len = -1; @@ -6935,6 +7139,15 @@ static void wpas_ctrl_scan(struct wpa_supplicant *wpa_s, char *params, return; } +#ifdef CONFIG_INTERWORKING + if (wpa_s->fetch_anqp_in_progress || wpa_s->network_select) { + wpa_printf(MSG_DEBUG, + "Interworking select in progress - reject new scan"); + *reply_len = os_snprintf(reply, reply_size, "FAIL-BUSY\n"); + return; + } +#endif /* CONFIG_INTERWORKING */ + if (params) { if (os_strncasecmp(params, "TYPE=ONLY", 9) == 0) scan_only = 1; @@ -6967,6 +7180,60 @@ static void wpas_ctrl_scan(struct wpa_supplicant *wpa_s, char *params, *reply_len = -1; goto done; } + + pos = params; + while (pos && *pos != '\0') { + if (os_strncmp(pos, "ssid ", 5) == 0) { + char *end; + + pos += 5; + end = pos; + while (*end) { + if (*end == '\0' || *end == ' ') + break; + end++; + } + + ns = os_realloc_array( + ssid, ssid_count + 1, + sizeof(struct wpa_ssid_value)); + if (ns == NULL) { + *reply_len = -1; + goto done; + } + ssid = ns; + + if ((end - pos) & 0x01 || + end - pos > 2 * SSID_MAX_LEN || + hexstr2bin(pos, ssid[ssid_count].ssid, + (end - pos) / 2) < 0) { + wpa_printf(MSG_DEBUG, + "Invalid SSID value '%s'", + pos); + *reply_len = -1; + goto done; + } + ssid[ssid_count].ssid_len = (end - pos) / 2; + wpa_hexdump_ascii(MSG_DEBUG, "scan SSID", + ssid[ssid_count].ssid, + ssid[ssid_count].ssid_len); + ssid_count++; + pos = end; + } + + pos = os_strchr(pos, ' '); + if (pos) + pos++; + } + } + + wpa_s->num_ssids_from_scan_req = ssid_count; + os_free(wpa_s->ssids_from_scan_req); + if (ssid_count) { + wpa_s->ssids_from_scan_req = ssid; + ssid = NULL; + } else { + wpa_s->ssids_from_scan_req = NULL; } if (scan_only) @@ -7030,6 +7297,7 @@ static void wpas_ctrl_scan(struct wpa_supplicant *wpa_s, char *params, done: os_free(manual_scan_freqs); + os_free(ssid); } @@ -7231,7 +7499,7 @@ 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; - const struct iphdr *ip; + struct iphdr ip; const u8 *pos; unsigned int i; @@ -7239,14 +7507,14 @@ void wpas_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) return; eth = (const struct ether_header *) buf; - ip = (const struct iphdr *) (eth + 1); - pos = (const u8 *) (ip + 1); + os_memcpy(&ip, eth + 1, sizeof(ip)); + pos = &buf[sizeof(*eth) + sizeof(ip)]; - if (ip->ihl != 5 || ip->version != 4 || - ntohs(ip->tot_len) != HWSIM_IP_LEN) + if (ip.ihl != 5 || ip.version != 4 || + ntohs(ip.tot_len) != HWSIM_IP_LEN) return; - for (i = 0; i < HWSIM_IP_LEN - sizeof(*ip); i++) { + for (i = 0; i < HWSIM_IP_LEN - sizeof(ip); i++) { if (*pos != (u8) i) return; pos++; @@ -7293,7 +7561,7 @@ static int wpas_ctrl_iface_data_test_tx(struct wpa_supplicant *wpa_s, char *cmd) int used; long int val; u8 tos; - u8 buf[HWSIM_PACKETLEN]; + u8 buf[2 + HWSIM_PACKETLEN]; struct ether_header *eth; struct iphdr *ip; u8 *dpos; @@ -7321,7 +7589,7 @@ static int wpas_ctrl_iface_data_test_tx(struct wpa_supplicant *wpa_s, char *cmd) return -1; tos = val; - eth = (struct ether_header *) buf; + eth = (struct ether_header *) &buf[2]; os_memcpy(eth->ether_dhost, dst, ETH_ALEN); os_memcpy(eth->ether_shost, src, ETH_ALEN); eth->ether_type = htons(ETHERTYPE_IP); @@ -7333,14 +7601,14 @@ static int wpas_ctrl_iface_data_test_tx(struct wpa_supplicant *wpa_s, char *cmd) ip->tos = tos; ip->tot_len = htons(HWSIM_IP_LEN); ip->protocol = 1; - ip->saddr = htonl(192 << 24 | 168 << 16 | 1 << 8 | 1); - ip->daddr = htonl(192 << 24 | 168 << 16 | 1 << 8 | 2); + ip->saddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 1); + ip->daddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 2); ip->check = ipv4_hdr_checksum(ip, sizeof(*ip)); dpos = (u8 *) (ip + 1); for (i = 0; i < HWSIM_IP_LEN - sizeof(*ip); i++) *dpos++ = i; - if (l2_packet_send(wpa_s->l2_test, dst, ETHERTYPE_IP, buf, + if (l2_packet_send(wpa_s->l2_test, dst, ETHERTYPE_IP, &buf[2], HWSIM_PACKETLEN) < 0) return -1; @@ -7429,6 +7697,44 @@ static int wpas_ctrl_get_alloc_fail(struct wpa_supplicant *wpa_s, #endif /* WPA_TRACE_BFD */ } + +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); + pos = os_strchr(cmd, ':'); + if (pos) { + pos++; + os_strlcpy(wpa_trace_test_fail_func, pos, + sizeof(wpa_trace_test_fail_func)); + } else { + wpa_trace_test_fail_after = 0; + } + return 0; +#else /* WPA_TRACE_BFD */ + return -1; +#endif /* WPA_TRACE_BFD */ +} + + +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 */ + return -1; +#endif /* WPA_TRACE_BFD */ +} + #endif /* CONFIG_TESTING_OPTIONS */ @@ -7665,7 +7971,7 @@ static int wpas_ctrl_iface_send_neigbor_rep(struct wpa_supplicant *wpa_s, if (os_strncmp(cmd, " ssid=", 6) == 0) { ssid.ssid_len = os_strlen(cmd + 6); - if (ssid.ssid_len > 32) + if (ssid.ssid_len > SSID_MAX_LEN) return -1; ssid.ssid = (u8 *) (cmd + 6); ssid_p = &ssid; @@ -7806,6 +8112,19 @@ static int wpas_ctrl_iface_mac_rand_scan(struct wpa_supplicant *wpa_s, } +static int wpas_ctrl_cmd_debug_level(const char *cmd) +{ + if (os_strcmp(cmd, "PING") == 0 || + os_strncmp(cmd, "BSS ", 4) == 0 || + os_strncmp(cmd, "GET_NETWORK ", 12) == 0 || + os_strncmp(cmd, "STATUS", 6) == 0 || + os_strncmp(cmd, "STA ", 4) == 0 || + os_strncmp(cmd, "STA-", 4) == 0) + return MSG_EXCESSIVE; + return MSG_DEBUG; +} + + char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, char *buf, size_t *resp_len) { @@ -7829,9 +8148,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, wpa_hexdump_ascii_key(MSG_DEBUG, "RX ctrl_iface", (const u8 *) buf, os_strlen(buf)); } else { - int level = MSG_DEBUG; - if (os_strcmp(buf, "PING") == 0) - level = MSG_EXCESSIVE; + int level = wpas_ctrl_cmd_debug_level(buf); wpa_dbg(wpa_s, level, "Control interface command '%s'", buf); } @@ -8066,7 +8383,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, if (wpas_p2p_group_remove(wpa_s, buf + 17)) reply_len = -1; } else if (os_strcmp(buf, "P2P_GROUP_ADD") == 0) { - if (wpas_p2p_group_add(wpa_s, 0, 0, 0, 0)) + if (p2p_ctrl_group_add(wpa_s, "")) reply_len = -1; } else if (os_strncmp(buf, "P2P_GROUP_ADD ", 14) == 0) { if (p2p_ctrl_group_add(wpa_s, buf + 14)) @@ -8240,6 +8557,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, wpa_supplicant_cancel_scan(wpa_s); wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); + eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL); } 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) { @@ -8269,7 +8587,8 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, reply_len = wpa_supplicant_ctrl_iface_get_network( wpa_s, buf + 12, reply, reply_size); } else if (os_strncmp(buf, "DUP_NETWORK ", 12) == 0) { - if (wpa_supplicant_ctrl_iface_dup_network(wpa_s, buf + 12)) + if (wpa_supplicant_ctrl_iface_dup_network(wpa_s, buf + 12, + wpa_s)) reply_len = -1; } else if (os_strcmp(buf, "LIST_CREDS") == 0) { reply_len = wpa_supplicant_ctrl_iface_list_creds( @@ -8372,6 +8691,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, if (wpa_supplicant_ctrl_iface_tdls_cancel_chan_switch(wpa_s, buf + 24)) reply_len = -1; + } else if (os_strncmp(buf, "TDLS_LINK_STATUS ", 17) == 0) { + reply_len = wpa_supplicant_ctrl_iface_tdls_link_status( + wpa_s, buf + 17, reply, reply_size); #endif /* CONFIG_TDLS */ } else if (os_strcmp(buf, "WMM_AC_STATUS") == 0) { reply_len = wpas_wmm_ac_status(wpa_s, reply, reply_size); @@ -8442,6 +8764,11 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, reply_len = -1; } else if (os_strcmp(buf, "GET_ALLOC_FAIL") == 0) { reply_len = wpas_ctrl_get_alloc_fail(wpa_s, reply, reply_size); + } else if (os_strncmp(buf, "TEST_FAIL ", 10) == 0) { + if (wpas_ctrl_test_fail(wpa_s, buf + 10) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "GET_FAIL") == 0) { + reply_len = wpas_ctrl_get_fail(wpa_s, reply, reply_size); #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) @@ -8460,6 +8787,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "MAC_RAND_SCAN ", 14) == 0) { if (wpas_ctrl_iface_mac_rand_scan(wpa_s, buf + 14)) reply_len = -1; + } else if (os_strncmp(buf, "GET_PREF_FREQ_LIST ", 19) == 0) { + reply_len = wpas_ctrl_iface_get_pref_freq_list( + wpa_s, buf + 19, reply, reply_size); } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; @@ -8479,11 +8809,14 @@ static int wpa_supplicant_global_iface_add(struct wpa_global *global, char *cmd) { struct wpa_interface iface; - char *pos; + char *pos, *extra; + struct wpa_supplicant *wpa_s; + unsigned int create_iface = 0; + u8 mac_addr[ETH_ALEN]; /* * <ifname>TAB<confname>TAB<driver>TAB<ctrl_interface>TAB<driver_param> - * TAB<bridge_ifname> + * TAB<bridge_ifname>[TAB<create>] */ wpa_printf(MSG_DEBUG, "CTRL_IFACE GLOBAL INTERFACE_ADD '%s'", cmd); @@ -8543,12 +8876,54 @@ static int wpa_supplicant_global_iface_add(struct wpa_global *global, iface.bridge_ifname = NULL; if (pos == NULL) break; + + extra = pos; + pos = os_strchr(pos, '\t'); + if (pos) + *pos++ = '\0'; + if (!extra[0]) + break; + + if (os_strcmp(extra, "create") == 0) + create_iface = 1; + else { + wpa_printf(MSG_DEBUG, + "INTERFACE_ADD unsupported extra parameter: '%s'", + extra); + return -1; + } } while (0); + if (create_iface) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE creating interface '%s'", + iface.ifname); + if (!global->ifaces) + return -1; + if (wpa_drv_if_add(global->ifaces, WPA_IF_STATION, iface.ifname, + NULL, NULL, NULL, mac_addr, NULL) < 0) { + wpa_printf(MSG_ERROR, + "CTRL_IFACE interface creation failed"); + return -1; + } + + wpa_printf(MSG_DEBUG, + "CTRL_IFACE interface '%s' created with MAC addr: " + MACSTR, iface.ifname, MAC2STR(mac_addr)); + } + if (wpa_supplicant_get_iface(global, iface.ifname)) - return -1; + goto fail; + + wpa_s = wpa_supplicant_add_iface(global, &iface, NULL); + if (!wpa_s) + goto fail; + wpa_s->added_vif = create_iface; + return 0; - return wpa_supplicant_add_iface(global, &iface, NULL) ? 0 : -1; +fail: + if (create_iface) + wpa_drv_if_remove(global->ifaces, WPA_IF_STATION, iface.ifname); + return -1; } @@ -8556,13 +8931,22 @@ static int wpa_supplicant_global_iface_remove(struct wpa_global *global, char *cmd) { struct wpa_supplicant *wpa_s; + int ret; + unsigned int delete_iface; wpa_printf(MSG_DEBUG, "CTRL_IFACE GLOBAL INTERFACE_REMOVE '%s'", cmd); wpa_s = wpa_supplicant_get_iface(global, cmd); if (wpa_s == NULL) return -1; - return wpa_supplicant_remove_iface(global, wpa_s, 0); + delete_iface = wpa_s->added_vif; + ret = wpa_supplicant_remove_iface(global, wpa_s, 0); + if (!ret && delete_iface) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE deleting the interface '%s'", + cmd); + ret = wpa_drv_if_remove(global->ifaces, WPA_IF_STATION, cmd); + } + return ret; } @@ -8589,7 +8973,7 @@ static int wpa_supplicant_global_iface_list(struct wpa_global *global, char *pos, *end; for (i = 0; wpa_drivers[i]; i++) { - struct wpa_driver_ops *drv = wpa_drivers[i]; + const struct wpa_driver_ops *drv = wpa_drivers[i]; if (drv->get_interfaces == NULL) continue; tmp = drv->get_interfaces(global->drv_priv[i]); @@ -8807,6 +9191,41 @@ static int wpas_global_ctrl_iface_set(struct wpa_global *global, char *cmd) } +static int wpas_global_ctrl_iface_dup_network(struct wpa_global *global, + char *cmd) +{ + struct wpa_supplicant *wpa_s[2]; /* src, dst */ + char *p; + unsigned int i; + + /* cmd: "<src ifname> <dst ifname> <src network id> <dst network id> + * <variable name> */ + + for (i = 0; i < ARRAY_SIZE(wpa_s) ; i++) { + p = os_strchr(cmd, ' '); + if (p == NULL) + return -1; + *p = '\0'; + + wpa_s[i] = global->ifaces; + for (; wpa_s[i]; wpa_s[i] = wpa_s[i]->next) { + if (os_strcmp(cmd, wpa_s[i]->ifname) == 0) + break; + } + + if (!wpa_s[i]) { + wpa_printf(MSG_DEBUG, + "CTRL_IFACE: Could not find iface=%s", cmd); + return -1; + } + + cmd = p + 1; + } + + return wpa_supplicant_ctrl_iface_dup_network(wpa_s[0], cmd, wpa_s[1]); +} + + #ifndef CONFIG_NO_CONFIG_WRITE static int wpas_global_ctrl_iface_save_config(struct wpa_global *global) { @@ -8888,6 +9307,59 @@ static int wpas_global_ctrl_iface_status(struct wpa_global *global, } +#ifdef CONFIG_FST + +static int wpas_global_ctrl_iface_fst_attach(struct wpa_global *global, + char *cmd, char *buf, + size_t reply_size) +{ + char ifname[IFNAMSIZ + 1]; + struct fst_iface_cfg cfg; + struct wpa_supplicant *wpa_s; + struct fst_wpa_obj iface_obj; + + if (!fst_parse_attach_command(cmd, ifname, sizeof(ifname), &cfg)) { + wpa_s = wpa_supplicant_get_iface(global, ifname); + if (wpa_s) { + if (wpa_s->fst) { + wpa_printf(MSG_INFO, "FST: Already attached"); + return -1; + } + fst_wpa_supplicant_fill_iface_obj(wpa_s, &iface_obj); + wpa_s->fst = fst_attach(ifname, wpa_s->own_addr, + &iface_obj, &cfg); + if (wpa_s->fst) + return os_snprintf(buf, reply_size, "OK\n"); + } + } + + return -1; +} + + +static int wpas_global_ctrl_iface_fst_detach(struct wpa_global *global, + char *cmd, char *buf, + size_t reply_size) +{ + char ifname[IFNAMSIZ + 1]; + struct wpa_supplicant *wpa_s; + + if (!fst_parse_detach_command(cmd, ifname, sizeof(ifname))) { + wpa_s = wpa_supplicant_get_iface(global, ifname); + if (wpa_s) { + if (!fst_iface_detach(ifname)) { + wpa_s->fst = NULL; + return os_snprintf(buf, reply_size, "OK\n"); + } + } + } + + return -1; +} + +#endif /* CONFIG_FST */ + + char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global, char *buf, size_t *resp_len) { @@ -8939,6 +9411,18 @@ char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global, } else if (os_strcmp(buf, "INTERFACES") == 0) { reply_len = wpa_supplicant_global_iface_interfaces( global, 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, + reply, + reply_size); + } else if (os_strncmp(buf, "FST-DETACH ", 11) == 0) { + reply_len = wpas_global_ctrl_iface_fst_detach(global, buf + 11, + reply, + reply_size); + } else if (os_strncmp(buf, "FST-MANAGER ", 12) == 0) { + reply_len = fst_ctrl_iface_receive(buf + 12, reply, reply_size); +#endif /* CONFIG_FST */ } else if (os_strcmp(buf, "TERMINATE") == 0) { wpa_supplicant_terminate_proc(global); } else if (os_strcmp(buf, "SUSPEND") == 0) { @@ -8959,6 +9443,9 @@ char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global, #endif /* CONFIG_P2P */ reply_len = -1; } + } else if (os_strncmp(buf, "DUP_NETWORK ", 12) == 0) { + if (wpas_global_ctrl_iface_dup_network(global, buf + 12)) + reply_len = -1; #ifndef CONFIG_NO_CONFIG_WRITE } else if (os_strcmp(buf, "SAVE_CONFIG") == 0) { if (wpas_global_ctrl_iface_save_config(global)) diff --git a/contrib/wpa/wpa_supplicant/ctrl_iface_named_pipe.c b/contrib/wpa/wpa_supplicant/ctrl_iface_named_pipe.c index dc02db2..54e0e2f 100644 --- a/contrib/wpa/wpa_supplicant/ctrl_iface_named_pipe.c +++ b/contrib/wpa/wpa_supplicant/ctrl_iface_named_pipe.c @@ -423,7 +423,8 @@ static int ctrl_iface_parse(struct ctrl_iface_priv *priv, const char *params) } -static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, int global, +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; diff --git a/contrib/wpa/wpa_supplicant/ctrl_iface_udp.c b/contrib/wpa/wpa_supplicant/ctrl_iface_udp.c index bf6a3df..76f69f2 100644 --- a/contrib/wpa/wpa_supplicant/ctrl_iface_udp.c +++ b/contrib/wpa/wpa_supplicant/ctrl_iface_udp.c @@ -322,7 +322,8 @@ static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, } -static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, int global, +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; diff --git a/contrib/wpa/wpa_supplicant/ctrl_iface_unix.c b/contrib/wpa/wpa_supplicant/ctrl_iface_unix.c index b1ac766..11f2814 100644 --- a/contrib/wpa/wpa_supplicant/ctrl_iface_unix.c +++ b/contrib/wpa/wpa_supplicant/ctrl_iface_unix.c @@ -13,6 +13,10 @@ #include <stddef.h> #include <unistd.h> #include <fcntl.h> +#ifdef __linux__ +#include <sys/ioctl.h> +#include <linux/sockios.h> +#endif /* __linux__ */ #ifdef ANDROID #include <cutils/sockets.h> #endif /* ANDROID */ @@ -72,6 +76,32 @@ static int wpas_ctrl_iface_global_reinit(struct wpa_global *global, struct ctrl_iface_global_priv *priv); +static void wpas_ctrl_sock_debug(const char *title, int sock, const char *buf, + size_t len) +{ +#ifdef __linux__ + socklen_t optlen; + int sndbuf, outq; + int level = MSG_MSGDUMP; + + if (len >= 5 && os_strncmp(buf, "PONG\n", 5) == 0) + level = MSG_EXCESSIVE; + + optlen = sizeof(sndbuf); + sndbuf = 0; + if (getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sndbuf, &optlen) < 0) + sndbuf = -1; + + if (ioctl(sock, SIOCOUTQ, &outq) < 0) + outq = -1; + + wpa_printf(level, + "CTRL-DEBUG: %s: sock=%d sndbuf=%d outq=%d send_len=%d", + title, sock, sndbuf, outq, (int) len); +#endif /* __linux__ */ +} + + static int wpa_supplicant_ctrl_iface_attach(struct dl_list *ctrl_dst, struct sockaddr_un *from, socklen_t fromlen, int global) @@ -197,6 +227,13 @@ static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, reply_buf = wpa_supplicant_ctrl_iface_process(wpa_s, buf, &reply_len); reply = reply_buf; + + /* + * There could be some password/key material in the command, so + * clear the buffer explicitly now that it is not needed + * anymore. + */ + os_memset(buf, 0, res); } if (!reply && reply_len == 1) { @@ -208,6 +245,8 @@ static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, } if (reply) { + wpas_ctrl_sock_debug("ctrl_sock-sendto", sock, reply, + reply_len); if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen) < 0) { int _errno = errno; @@ -295,7 +334,8 @@ static char * wpa_supplicant_ctrl_iface_path(struct wpa_supplicant *wpa_s) } -static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, int global, +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; @@ -303,19 +343,19 @@ static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, int global, if (wpa_s == NULL) return; - if (global != 2 && wpa_s->global->ctrl_iface) { + 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)) { - wpa_supplicant_ctrl_iface_send(wpa_s, global ? NULL : - wpa_s->ifname, - priv->sock, - &priv->ctrl_dst, - level, txt, len, NULL, - priv); + 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); } } - if (wpa_s->ctrl_iface == NULL) + 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, @@ -544,6 +584,53 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) if (wpa_s->conf->ctrl_interface == NULL) return priv; +#ifdef ANDROID + if (wpa_s->global->params.ctrl_interface) { + int same = 0; + + if (wpa_s->global->params.ctrl_interface[0] == '/') { + if (os_strcmp(wpa_s->global->params.ctrl_interface, + wpa_s->conf->ctrl_interface) == 0) + same = 1; + } else if (os_strncmp(wpa_s->global->params.ctrl_interface, + "@android:", 9) == 0 || + os_strncmp(wpa_s->global->params.ctrl_interface, + "@abstract:", 10) == 0) { + char *pos; + + /* + * Currently, Android uses @android:wpa_* as the naming + * convention for the global ctrl interface. This logic + * needs to be revisited if the above naming convention + * is modified. + */ + pos = os_strchr(wpa_s->global->params.ctrl_interface, + '_'); + if (pos && + os_strcmp(pos + 1, + wpa_s->conf->ctrl_interface) == 0) + same = 1; + } + + if (same) { + /* + * The invalid configuration combination might be + * possible to hit in an Android OTA upgrade case, so + * instead of refusing to start the wpa_supplicant + * process, do not open the per-interface ctrl_iface + * and continue with the global control interface that + * was set from the command line since the Wi-Fi + * framework will use it for operations. + */ + wpa_printf(MSG_ERROR, + "global ctrl interface %s matches ctrl interface %s - do not open per-interface ctrl interface", + wpa_s->global->params.ctrl_interface, + wpa_s->conf->ctrl_interface); + return priv; + } + } +#endif /* ANDROID */ + if (wpas_ctrl_iface_open_sock(wpa_s, priv) < 0) { os_free(priv); return NULL; @@ -708,8 +795,10 @@ static void wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s, 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_DEBUG, "CTRL_IFACE monitor sent successfully to %s", + wpa_printf(MSG_MSGDUMP, + "CTRL_IFACE monitor sent successfully to %s", addr_txt); dst->errors = 0; continue; @@ -846,6 +935,13 @@ static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx, reply_buf = wpa_supplicant_global_ctrl_iface_process( global, buf, &reply_len); reply = reply_buf; + + /* + * There could be some password/key material in the command, so + * clear the buffer explicitly now that it is not needed + * anymore. + */ + os_memset(buf, 0, res); } if (!reply && reply_len == 1) { @@ -857,6 +953,8 @@ static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx, } if (reply) { + wpas_ctrl_sock_debug("global_ctrl_sock-sendto", + sock, reply, reply_len); if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen) < 0) { wpa_printf(MSG_DEBUG, "ctrl_iface sendto failed: %s", diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new.c b/contrib/wpa/wpa_supplicant/dbus/dbus_new.c index 30ef03a..67d0e28 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new.c @@ -137,7 +137,7 @@ static void wpas_dbus_signal_interface(struct wpa_supplicant *wpa_s, iface = wpa_s->global->dbus; /* Do nothing if the control interface is not turned on */ - if (iface == NULL) + if (iface == NULL || !wpa_s->dbus_new_path) return; msg = dbus_message_new_signal(WPAS_DBUS_NEW_PATH, @@ -200,7 +200,7 @@ void wpas_dbus_signal_scan_done(struct wpa_supplicant *wpa_s, int success) iface = wpa_s->global->dbus; /* Do nothing if the control interface is not turned on */ - if (iface == NULL) + if (iface == NULL || !wpa_s->dbus_new_path) return; msg = dbus_message_new_signal(wpa_s->dbus_new_path, @@ -239,7 +239,7 @@ static void wpas_dbus_signal_bss(struct wpa_supplicant *wpa_s, iface = wpa_s->global->dbus; /* Do nothing if the control interface is not turned on */ - if (iface == NULL) + if (iface == NULL || !wpa_s->dbus_new_path) return; msg = dbus_message_new_signal(wpa_s->dbus_new_path, @@ -307,7 +307,7 @@ static void wpas_dbus_signal_blob(struct wpa_supplicant *wpa_s, iface = wpa_s->global->dbus; /* Do nothing if the control interface is not turned on */ - if (iface == NULL) + if (iface == NULL || !wpa_s->dbus_new_path) return; msg = dbus_message_new_signal(wpa_s->dbus_new_path, @@ -374,7 +374,7 @@ static void wpas_dbus_signal_network(struct wpa_supplicant *wpa_s, iface = wpa_s->global->dbus; /* Do nothing if the control interface is not turned on */ - if (iface == NULL) + if (iface == NULL || !wpa_s->dbus_new_path) return; os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, @@ -467,7 +467,7 @@ void wpas_dbus_signal_network_request(struct wpa_supplicant *wpa_s, iface = wpa_s->global->dbus; /* Do nothing if the control interface is not turned on */ - if (iface == NULL) + if (iface == NULL || !wpa_s->dbus_new_path) return; field = wpa_supplicant_ctrl_req_to_string(rtype, default_txt, &txt); @@ -511,6 +511,8 @@ void wpas_dbus_signal_network_enabled_changed(struct wpa_supplicant *wpa_s, char path[WPAS_DBUS_OBJECT_PATH_MAX]; + if (!wpa_s->dbus_new_path) + return; os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%d", wpa_s->dbus_new_path, ssid->id); @@ -523,6 +525,44 @@ void wpas_dbus_signal_network_enabled_changed(struct wpa_supplicant *wpa_s, #ifdef CONFIG_WPS /** + * wpas_dbus_signal_wps_event_pbc_overlap - Signals PBC overlap WPS event + * @wpa_s: %wpa_supplicant network interface data + * + * Sends Event dbus signal with name "pbc-overlap" and empty dict as arguments + */ +void wpas_dbus_signal_wps_event_pbc_overlap(struct wpa_supplicant *wpa_s) +{ + + DBusMessage *msg; + DBusMessageIter iter, dict_iter; + struct wpas_dbus_priv *iface; + char *key = "pbc-overlap"; + + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (iface == NULL || !wpa_s->dbus_new_path) + return; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_WPS, "Event"); + if (msg == NULL) + return; + + dbus_message_iter_init_append(msg, &iter); + + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key) || + !wpa_dbus_dict_open_write(&iter, &dict_iter) || + !wpa_dbus_dict_close_write(&iter, &dict_iter)) + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + else + dbus_connection_send(iface->con, msg, NULL); + + dbus_message_unref(msg); +} + + +/** * wpas_dbus_signal_wps_event_success - Signals Success WPS event * @wpa_s: %wpa_supplicant network interface data * @@ -539,7 +579,7 @@ void wpas_dbus_signal_wps_event_success(struct wpa_supplicant *wpa_s) iface = wpa_s->global->dbus; /* Do nothing if the control interface is not turned on */ - if (iface == NULL) + if (iface == NULL || !wpa_s->dbus_new_path) return; msg = dbus_message_new_signal(wpa_s->dbus_new_path, @@ -563,6 +603,7 @@ void wpas_dbus_signal_wps_event_success(struct wpa_supplicant *wpa_s) /** * wpas_dbus_signal_wps_event_fail - Signals Fail WPS event * @wpa_s: %wpa_supplicant network interface data + * @fail: WPS failure information * * Sends Event dbus signal with name "fail" and dictionary containing * "msg field with fail message number (int32) as arguments @@ -579,7 +620,7 @@ void wpas_dbus_signal_wps_event_fail(struct wpa_supplicant *wpa_s, iface = wpa_s->global->dbus; /* Do nothing if the control interface is not turned on */ - if (iface == NULL) + if (iface == NULL || !wpa_s->dbus_new_path) return; msg = dbus_message_new_signal(wpa_s->dbus_new_path, @@ -592,6 +633,10 @@ void wpas_dbus_signal_wps_event_fail(struct wpa_supplicant *wpa_s, if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key) || !wpa_dbus_dict_open_write(&iter, &dict_iter) || !wpa_dbus_dict_append_int32(&dict_iter, "msg", fail->msg) || + !wpa_dbus_dict_append_int32(&dict_iter, "config_error", + fail->config_error) || + !wpa_dbus_dict_append_int32(&dict_iter, "error_indication", + fail->error_indication) || !wpa_dbus_dict_close_write(&iter, &dict_iter)) wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); else @@ -604,6 +649,7 @@ void wpas_dbus_signal_wps_event_fail(struct wpa_supplicant *wpa_s, /** * wpas_dbus_signal_wps_event_m2d - Signals M2D WPS event * @wpa_s: %wpa_supplicant network interface data + * @m2d: M2D event data information * * Sends Event dbus signal with name "m2d" and dictionary containing * fields of wps_event_m2d structure. @@ -620,7 +666,7 @@ void wpas_dbus_signal_wps_event_m2d(struct wpa_supplicant *wpa_s, iface = wpa_s->global->dbus; /* Do nothing if the control interface is not turned on */ - if (iface == NULL) + if (iface == NULL || !wpa_s->dbus_new_path) return; msg = dbus_message_new_signal(wpa_s->dbus_new_path, @@ -669,6 +715,7 @@ void wpas_dbus_signal_wps_event_m2d(struct wpa_supplicant *wpa_s, /** * wpas_dbus_signal_wps_cred - Signals new credentials * @wpa_s: %wpa_supplicant network interface data + * @cred: WPS Credential information * * Sends signal with credentials in directory argument */ @@ -686,7 +733,7 @@ void wpas_dbus_signal_wps_cred(struct wpa_supplicant *wpa_s, iface = wpa_s->global->dbus; /* Do nothing if the control interface is not turned on */ - if (iface == NULL) + if (iface == NULL || !wpa_s->dbus_new_path) return; msg = dbus_message_new_signal(wpa_s->dbus_new_path, @@ -760,7 +807,7 @@ void wpas_dbus_signal_certification(struct wpa_supplicant *wpa_s, iface = wpa_s->global->dbus; /* Do nothing if the control interface is not turned on */ - if (iface == NULL) + if (iface == NULL || !wpa_s->dbus_new_path) return; msg = dbus_message_new_signal(wpa_s->dbus_new_path, @@ -801,7 +848,7 @@ void wpas_dbus_signal_eap_status(struct wpa_supplicant *wpa_s, iface = wpa_s->global->dbus; /* Do nothing if the control interface is not turned on */ - if (iface == NULL) + if (iface == NULL || !wpa_s->dbus_new_path) return; msg = dbus_message_new_signal(wpa_s->dbus_new_path, @@ -844,7 +891,7 @@ static void wpas_dbus_signal_sta(struct wpa_supplicant *wpa_s, iface = wpa_s->global->dbus; /* Do nothing if the control interface is not turned on */ - if (iface == NULL) + if (iface == NULL || !wpa_s->dbus_new_path) return; msg = dbus_message_new_signal(wpa_s->dbus_new_path, @@ -916,7 +963,8 @@ void wpas_dbus_signal_p2p_group_removed(struct wpa_supplicant *wpa_s, if (parent->p2p_mgmt) parent = parent->parent; - if (!wpa_s->dbus_groupobj_path) + if (!wpa_s->dbus_groupobj_path || !wpa_s->dbus_new_path || + !parent->dbus_new_path) return; msg = dbus_message_new_signal(parent->dbus_new_path, @@ -984,6 +1032,8 @@ void wpas_dbus_signal_p2p_provision_discovery(struct wpa_supplicant *wpa_s, if (wpa_s->p2p_mgmt) wpa_s = wpa_s->parent; + if (!wpa_s->dbus_new_path) + return; if (request || !status) { if (config_methods & WPS_CONFIG_DISPLAY) @@ -1057,8 +1107,19 @@ error: } +/** + * wpas_dbus_signal_p2p_go_neg_req - Signal P2P GO Negotiation Request RX + * @wpa_s: %wpa_supplicant network interface data + * @src: Source address of the message triggering this notification + * @dev_passwd_id: WPS Device Password Id + * @go_intent: Peer's GO Intent value + * + * Sends signal to notify that a peer P2P Device is requesting group owner + * negotiation with us. + */ void wpas_dbus_signal_p2p_go_neg_req(struct wpa_supplicant *wpa_s, - const u8 *src, u16 dev_passwd_id) + const u8 *src, u16 dev_passwd_id, + u8 go_intent) { DBusMessage *msg; DBusMessageIter iter; @@ -1073,6 +1134,8 @@ void wpas_dbus_signal_p2p_go_neg_req(struct wpa_supplicant *wpa_s, if (wpa_s->p2p_mgmt) wpa_s = wpa_s->parent; + if (!wpa_s->dbus_new_path) + return; os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR, @@ -1090,7 +1153,9 @@ void wpas_dbus_signal_p2p_go_neg_req(struct wpa_supplicant *wpa_s, if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path) || !dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT16, - &dev_passwd_id)) + &dev_passwd_id) || + !dbus_message_iter_append_basic(&iter, DBUS_TYPE_BYTE, + &go_intent)) wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); else dbus_connection_send(iface->con, msg, NULL); @@ -1105,7 +1170,8 @@ static int wpas_dbus_get_group_obj_path(struct wpa_supplicant *wpa_s, { char group_name[3]; - if (os_memcmp(ssid->ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN)) + if (!wpa_s->dbus_new_path || + os_memcmp(ssid->ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN)) return -1; os_memcpy(group_name, ssid->ssid + P2P_WILDCARD_SSID_LEN, 2); @@ -1209,7 +1275,7 @@ void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s, iface = parent->global->dbus; /* Do nothing if the control interface is not turned on */ - if (iface == NULL) + if (iface == NULL || !parent->dbus_new_path || !wpa_s->dbus_new_path) return; if (wpa_s->dbus_groupobj_path == NULL) @@ -1248,10 +1314,9 @@ void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s, /** - * - * Method to emit GONegotiation Success or Failure signals based - * on status. - * @status: Status of the GO neg request. 0 for success, other for errors. + * wpas_dbus_signal_p2p_go_neg_resp - Emit GONegotiation Success/Failure signal + * @wpa_s: %wpa_supplicant network interface data + * @res: Result of the GO Neg Request */ void wpas_dbus_signal_p2p_go_neg_resp(struct wpa_supplicant *wpa_s, struct p2p_go_neg_results *res) @@ -1272,7 +1337,7 @@ void wpas_dbus_signal_p2p_go_neg_resp(struct wpa_supplicant *wpa_s, os_memset(freqs, 0, sizeof(freqs)); /* Do nothing if the control interface is not turned on */ - if (iface == NULL) + if (iface == NULL || !wpa_s->dbus_new_path) return; os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, @@ -1363,12 +1428,10 @@ err: /** - * - * Method to emit Invitation Result signal based on status and - * bssid - * @status: Status of the Invite request. 0 for success, other - * for errors - * @bssid : Basic Service Set Identifier + * wpas_dbus_signal_p2p_invitation_result - Emit InvitationResult signal + * @wpa_s: %wpa_supplicant network interface data + * @status: Status of invitation process + * @bssid: Basic Service Set Identifier */ void wpas_dbus_signal_p2p_invitation_result(struct wpa_supplicant *wpa_s, int status, const u8 *bssid) @@ -1386,6 +1449,8 @@ void wpas_dbus_signal_p2p_invitation_result(struct wpa_supplicant *wpa_s, if (wpa_s->p2p_mgmt) wpa_s = wpa_s->parent; + if (!wpa_s->dbus_new_path) + return; msg = dbus_message_new_signal(wpa_s->dbus_new_path, WPAS_DBUS_NEW_IFACE_P2PDEVICE, @@ -1439,6 +1504,8 @@ void wpas_dbus_signal_p2p_peer_joined(struct wpa_supplicant *wpa_s, parent = wpa_s->parent; if (parent->p2p_mgmt) parent = parent->parent; + if (!parent->dbus_new_path) + return; os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" @@ -1494,6 +1561,8 @@ void wpas_dbus_signal_p2p_peer_disconnected(struct wpa_supplicant *wpa_s, parent = wpa_s->parent; if (parent->p2p_mgmt) parent = parent->parent; + if (!parent->dbus_new_path) + return; os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" @@ -1551,6 +1620,8 @@ void wpas_dbus_signal_p2p_sd_request(struct wpa_supplicant *wpa_s, if (wpa_s->p2p_mgmt) wpa_s = wpa_s->parent; + if (!wpa_s->dbus_new_path) + return; /* Check if this is a known peer */ if (!p2p_peer_known(wpa_s->global->p2p, sa)) @@ -1617,6 +1688,8 @@ void wpas_dbus_signal_p2p_sd_response(struct wpa_supplicant *wpa_s, if (wpa_s->p2p_mgmt) wpa_s = wpa_s->parent; + if (!wpa_s->dbus_new_path) + return; /* Check if this is a known peer */ if (!p2p_peer_known(wpa_s->global->p2p, sa)) @@ -1678,6 +1751,8 @@ static void wpas_dbus_signal_persistent_group(struct wpa_supplicant *wpa_s, if (wpa_s->p2p_mgmt) wpa_s = wpa_s->parent; + if (!wpa_s->dbus_new_path) + return; os_snprintf(pgrp_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%u", @@ -1740,6 +1815,7 @@ static void wpas_dbus_signal_persistent_group_removed( /** * wpas_dbus_signal_p2p_wps_failed - Signals WpsFailed event * @wpa_s: %wpa_supplicant network interface data + * @fail: WPS failure information * * Sends Event dbus signal with name "fail" and dictionary containing * "msg" field with fail message number (int32) as arguments @@ -1762,6 +1838,8 @@ void wpas_dbus_signal_p2p_wps_failed(struct wpa_supplicant *wpa_s, if (wpa_s->p2p_mgmt) wpa_s = wpa_s->parent; + if (!wpa_s->dbus_new_path) + return; msg = dbus_message_new_signal(wpa_s->dbus_new_path, WPAS_DBUS_NEW_IFACE_P2PDEVICE, "WpsFailed"); @@ -1783,6 +1861,98 @@ void wpas_dbus_signal_p2p_wps_failed(struct wpa_supplicant *wpa_s, dbus_message_unref(msg); } + +/** + * wpas_dbus_signal_p2p_group_formation_failure - Signals GroupFormationFailure event + * @wpa_s: %wpa_supplicant network interface data + * @reason: indicates the reason code for group formation failure + * + * Sends Event dbus signal and string reason code when available. + */ +void wpas_dbus_signal_p2p_group_formation_failure(struct wpa_supplicant *wpa_s, + const char *reason) +{ + DBusMessage *msg; + struct wpas_dbus_priv *iface; + + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (iface == NULL) + return; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_P2PDEVICE, + "GroupFormationFailure"); + if (msg == NULL) + return; + + if (dbus_message_append_args(msg, DBUS_TYPE_STRING, &reason, + DBUS_TYPE_INVALID)) + dbus_connection_send(iface->con, msg, NULL); + else + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + + dbus_message_unref(msg); +} + + +/** + * wpas_dbus_signal_p2p_invitation_received - Emit InvitationReceived signal + * @wpa_s: %wpa_supplicant network interface data + * @sa: Source address of the Invitation Request + * @dev_add: GO Device Address + * @bssid: P2P Group BSSID or %NULL if not received + * @id: Persistent group id or %0 if not persistent group + * @op_freq: Operating frequency for the group + */ + +void wpas_dbus_signal_p2p_invitation_received(struct wpa_supplicant *wpa_s, + const u8 *sa, const u8 *dev_addr, + const u8 *bssid, int id, + int op_freq) +{ + DBusMessage *msg; + DBusMessageIter iter, dict_iter; + struct wpas_dbus_priv *iface; + + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (iface == NULL) + return; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_P2PDEVICE, + "InvitationReceived"); + if (msg == NULL) + return; + + dbus_message_iter_init_append(msg, &iter); + if (!wpa_dbus_dict_open_write(&iter, &dict_iter) || + (sa && + !wpa_dbus_dict_append_byte_array(&dict_iter, "sa", + (const char *) sa, ETH_ALEN)) || + (dev_addr && + !wpa_dbus_dict_append_byte_array(&dict_iter, "go_dev_addr", + (const char *) dev_addr, + ETH_ALEN)) || + (bssid && + !wpa_dbus_dict_append_byte_array(&dict_iter, "bssid", + (const char *) bssid, + ETH_ALEN)) || + (id && + !wpa_dbus_dict_append_int32(&dict_iter, "persistent_id", id)) || + !wpa_dbus_dict_append_int32(&dict_iter, "op_freq", op_freq) || + !wpa_dbus_dict_close_write(&iter, &dict_iter)) { + dbus_message_unref(msg); + return; + } + + dbus_connection_send(iface->con, msg, NULL); +} + + #endif /* CONFIG_P2P */ @@ -1862,6 +2032,9 @@ void wpas_dbus_bss_signal_prop_changed(struct wpa_supplicant *wpa_s, char path[WPAS_DBUS_OBJECT_PATH_MAX]; char *prop; + if (!wpa_s->dbus_new_path) + return; + switch (property) { case WPAS_DBUS_BSS_PROP_SIGNAL: prop = "Signal"; @@ -2177,7 +2350,7 @@ int wpas_dbus_register_network(struct wpa_supplicant *wpa_s, #endif /* CONFIG_P2P */ /* Do nothing if the control interface is not turned on */ - if (wpa_s == NULL || wpa_s->global == NULL) + if (wpa_s == NULL || wpa_s->global == NULL || !wpa_s->dbus_new_path) return 0; ctrl_iface = wpa_s->global->dbus; if (ctrl_iface == NULL) @@ -2351,7 +2524,7 @@ int wpas_dbus_unregister_bss(struct wpa_supplicant *wpa_s, char bss_obj_path[WPAS_DBUS_OBJECT_PATH_MAX]; /* Do nothing if the control interface is not turned on */ - if (wpa_s == NULL || wpa_s->global == NULL) + if (wpa_s == NULL || wpa_s->global == NULL || !wpa_s->dbus_new_path) return 0; ctrl_iface = wpa_s->global->dbus; if (ctrl_iface == NULL) @@ -2394,7 +2567,7 @@ int wpas_dbus_register_bss(struct wpa_supplicant *wpa_s, struct bss_handler_args *arg; /* Do nothing if the control interface is not turned on */ - if (wpa_s == NULL || wpa_s->global == NULL) + if (wpa_s == NULL || wpa_s->global == NULL || !wpa_s->dbus_new_path) return 0; ctrl_iface = wpa_s->global->dbus; if (ctrl_iface == NULL) @@ -2486,6 +2659,12 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { END_ARGS } }, + { "Reconnect", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_reconnect, + { + END_ARGS + } + }, { "RemoveNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE, (WPADBusMethodHandler) wpas_dbus_handler_remove_network, { @@ -2558,6 +2737,12 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { END_ARGS } }, + { "Cancel", WPAS_DBUS_NEW_IFACE_WPS, + (WPADBusMethodHandler) wpas_dbus_handler_wps_cancel, + { + END_ARGS + } + }, #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P { "Find", WPAS_DBUS_NEW_IFACE_P2PDEVICE, @@ -2617,6 +2802,12 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { END_ARGS } }, + { "Cancel", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + (WPADBusMethodHandler) wpas_dbus_handler_p2p_cancel, + { + END_ARGS + } + }, { "Invite", WPAS_DBUS_NEW_IFACE_P2PDEVICE, (WPADBusMethodHandler) wpas_dbus_handler_p2p_invite, { @@ -2637,6 +2828,13 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { END_ARGS } }, + { "RemoveClient", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + (WPADBusMethodHandler) wpas_dbus_handler_p2p_remove_client, + { + { "args", "a{sv}", ARG_IN }, + END_ARGS + } + }, { "Flush", WPAS_DBUS_NEW_IFACE_P2PDEVICE, (WPADBusMethodHandler) wpas_dbus_handler_p2p_flush, { @@ -3014,6 +3212,11 @@ static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = { END_ARGS } }, + { "FindStopped", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + END_ARGS + } + }, { "ProvisionDiscoveryRequestDisplayPin", WPAS_DBUS_NEW_IFACE_P2PDEVICE, { { "peer_object", "o", ARG_OUT }, @@ -3065,6 +3268,12 @@ static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = { END_ARGS } }, + { "GroupFormationFailure", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + { "reason", "s", ARG_OUT }, + END_ARGS + } + }, { "GONegotiationSuccess", WPAS_DBUS_NEW_IFACE_P2PDEVICE, { { "properties", "a{sv}", ARG_OUT }, @@ -3080,7 +3289,8 @@ static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = { { "GONegotiationRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE, { { "path", "o", ARG_OUT }, - { "dev_passwd_id", "i", ARG_OUT }, + { "dev_passwd_id", "q", ARG_OUT }, + { "device_go_intent", "y", ARG_OUT }, END_ARGS } }, @@ -3128,6 +3338,12 @@ static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = { END_ARGS } }, + { "InvitationReceived", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + { "properties", "a{sv}", ARG_OUT }, + END_ARGS + } + }, #endif /* CONFIG_P2P */ #ifdef CONFIG_AP { "ProbeRequest", WPAS_DBUS_NEW_IFACE_INTERFACE, @@ -3174,6 +3390,11 @@ static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = { }; +/** + * wpas_dbus_register_interface - Register an interface with D-Bus + * @wpa_s: wpa_supplicant interface structure + * Returns: 0 on success, -1 on failure + */ int wpas_dbus_register_interface(struct wpa_supplicant *wpa_s) { @@ -3224,6 +3445,11 @@ err: } +/** + * wpas_dbus_unregister_interface - Unregister the interface from D-Bus + * @wpa_s: wpa_supplicant interface structure + * Returns: 0 on success, -1 on failure + */ int wpas_dbus_unregister_interface(struct wpa_supplicant *wpa_s) { struct wpas_dbus_priv *ctrl_iface; @@ -3265,6 +3491,22 @@ static const struct wpa_dbus_property_desc wpas_dbus_p2p_peer_properties[] = { wpas_dbus_getter_p2p_peer_device_name, NULL }, + { "Manufacturer", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s", + wpas_dbus_getter_p2p_peer_manufacturer, + NULL + }, + { "ModelName", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s", + wpas_dbus_getter_p2p_peer_modelname, + NULL + }, + { "ModelNumber", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s", + wpas_dbus_getter_p2p_peer_modelnumber, + NULL + }, + { "SerialNumber", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s", + wpas_dbus_getter_p2p_peer_serialnumber, + NULL + }, { "PrimaryDeviceType", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay", wpas_dbus_getter_p2p_peer_primary_device_type, NULL @@ -3345,7 +3587,7 @@ static void wpas_dbus_signal_peer(struct wpa_supplicant *wpa_s, iface = wpa_s->global->dbus; /* Do nothing if the control interface is not turned on */ - if (iface == NULL) + if (iface == NULL || !wpa_s->dbus_new_path) return; os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, @@ -3372,7 +3614,7 @@ static void wpas_dbus_signal_peer(struct wpa_supplicant *wpa_s, /** * wpas_dbus_signal_peer_found - Send a peer found signal * @wpa_s: %wpa_supplicant network interface data - * @dev: peer device object + * @dev_addr: Peer P2P Device Address * * Notify listeners about find a p2p peer device found */ @@ -3387,7 +3629,7 @@ void wpas_dbus_signal_peer_device_found(struct wpa_supplicant *wpa_s, /** * wpas_dbus_signal_peer_lost - Send a peer lost signal * @wpa_s: %wpa_supplicant network interface data - * @dev: peer device object + * @dev_addr: Peer P2P Device Address * * Notify listeners about lost a p2p peer device */ @@ -3402,7 +3644,7 @@ void wpas_dbus_signal_peer_device_lost(struct wpa_supplicant *wpa_s, /** * wpas_dbus_register_peer - Register a discovered peer object with dbus * @wpa_s: wpa_supplicant interface structure - * @ssid: network configuration data + * @dev_addr: P2P Device Address of the peer * Returns: 0 on success, -1 on failure * * Registers network representing object with dbus @@ -3422,8 +3664,9 @@ int wpas_dbus_register_peer(struct wpa_supplicant *wpa_s, const u8 *dev_addr) if (ctrl_iface == NULL) return 0; - if (wpa_s->p2p_mgmt) - wpa_s = wpa_s->parent; + wpa_s = wpa_s->parent->parent; + if (!wpa_s->dbus_new_path) + return 0; os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR, @@ -3481,12 +3724,12 @@ int wpas_dbus_unregister_peer(struct wpa_supplicant *wpa_s, int ret; /* Do nothing if the control interface is not turned on */ - if (wpa_s == NULL || wpa_s->global == NULL || - wpa_s->dbus_new_path == NULL) + if (wpa_s == NULL || wpa_s->global == NULL) return 0; - if (wpa_s->p2p_mgmt) - wpa_s = wpa_s->parent; + wpa_s = wpa_s->parent->parent; + if (!wpa_s->dbus_new_path) + return 0; ctrl_iface = wpa_s->global->dbus; if (ctrl_iface == NULL) @@ -3504,6 +3747,42 @@ int wpas_dbus_unregister_peer(struct wpa_supplicant *wpa_s, } +/** + * wpas_dbus_signal_p2p_find_stopped - Send P2P Find stopped signal + * @wpa_s: %wpa_supplicant network interface data + * + * Notify listeners about P2P Find stopped + */ +void wpas_dbus_signal_p2p_find_stopped(struct wpa_supplicant *wpa_s) +{ + struct wpas_dbus_priv *iface; + DBusMessage *msg; + + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (iface == NULL || !wpa_s->dbus_new_path) + return; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_P2PDEVICE, + "FindStopped"); + if (msg == NULL) + return; + + dbus_connection_send(iface->con, msg, NULL); + + dbus_message_unref(msg); +} + + +/** + * wpas_dbus_signal_peer_groups_changed - Send peer group change property signal + * @wpa_s: %wpa_supplicant network interface data + * @dev_addr: P2P Device Address + * + * Notify listeners about peer Groups property changes. + */ void wpas_dbus_signal_peer_groups_changed(struct wpa_supplicant *wpa_s, const u8 *dev_addr) { @@ -3512,6 +3791,8 @@ void wpas_dbus_signal_peer_groups_changed(struct wpa_supplicant *wpa_s, if (wpa_s->p2p_mgmt) wpa_s = wpa_s->parent; + if (!wpa_s->dbus_new_path) + return; os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR, wpa_s->dbus_new_path, MAC2STR(dev_addr)); @@ -3713,6 +3994,9 @@ int wpas_dbus_register_persistent_group(struct wpa_supplicant *wpa_s, /* Do nothing if the control interface is not turned on */ if (wpa_s == NULL || wpa_s->global == NULL) return 0; + wpa_s = wpa_s->parent->parent; + if (!wpa_s->dbus_new_path) + return 0; /* Make sure ssid is a persistent group */ if (ssid->disabled != 2 && !ssid->p2p_persistent_group) @@ -3797,15 +4081,13 @@ int wpas_dbus_unregister_persistent_group(struct wpa_supplicant *wpa_s, int ret; /* Do nothing if the control interface is not turned on */ - if (wpa_s == NULL || wpa_s->global == NULL || - wpa_s->dbus_new_path == NULL) + if (wpa_s == NULL || wpa_s->global == NULL) return 0; - if (wpa_s->p2p_mgmt) - wpa_s = wpa_s->parent; + wpa_s = wpa_s->parent->parent; ctrl_iface = wpa_s->global->dbus; - if (ctrl_iface == NULL) + if (ctrl_iface == NULL || !wpa_s->dbus_new_path) return 0; os_snprintf(pgrp_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new.h b/contrib/wpa/wpa_supplicant/dbus/dbus_new.h index d162d2b..6d240ff 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new.h +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new.h @@ -152,6 +152,7 @@ void wpas_dbus_signal_wps_event_m2d(struct wpa_supplicant *wpa_s, void wpas_dbus_signal_wps_event_fail(struct wpa_supplicant *wpa_s, struct wps_event_fail *fail); void wpas_dbus_signal_wps_event_success(struct wpa_supplicant *wpa_s); +void wpas_dbus_signal_wps_event_pbc_overlap(struct wpa_supplicant *wpa_s); int wpas_dbus_register_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); int wpas_dbus_unregister_network(struct wpa_supplicant *wpa_s, int nid); @@ -168,6 +169,7 @@ void wpas_dbus_signal_debug_timestamp_changed(struct wpa_global *global); void wpas_dbus_signal_debug_show_keys_changed(struct wpa_global *global); int wpas_dbus_register_peer(struct wpa_supplicant *wpa_s, const u8 *dev_addr); +void wpas_dbus_signal_p2p_find_stopped(struct wpa_supplicant *wpa_s); void wpas_dbus_signal_peer_device_found(struct wpa_supplicant *wpa_s, const u8 *dev_addr); int wpas_dbus_unregister_peer(struct wpa_supplicant *wpa_s, @@ -184,10 +186,13 @@ void wpas_dbus_signal_p2p_provision_discovery(struct wpa_supplicant *wpa_s, u16 config_methods, unsigned int generated_pin); void wpas_dbus_signal_p2p_go_neg_req(struct wpa_supplicant *wpa_s, - const u8 *src, u16 dev_passwd_id); + 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); +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, struct wpa_ssid *ssid); void wpas_dbus_signal_p2p_go_neg_resp(struct wpa_supplicant *wpa_s, @@ -228,6 +233,10 @@ void wpas_dbus_signal_sta_authorized(struct wpa_supplicant *wpa_s, const u8 *sta); void wpas_dbus_signal_sta_deauthorized(struct wpa_supplicant *wpa_s, const u8 *sta); +void wpas_dbus_signal_p2p_invitation_received(struct wpa_supplicant *wpa_s, + const u8 *sa, const u8 *dev_addr, + const u8 *bssid, int id, + int op_freq); #else /* CONFIG_CTRL_IFACE_DBUS_NEW */ @@ -295,6 +304,11 @@ static inline void wpas_dbus_signal_wps_event_success( { } +static inline void wpas_dbus_signal_wps_event_pbc_overlap( + struct wpa_supplicant *wpa_s) +{ +} + static inline int wpas_dbus_register_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { @@ -377,10 +391,10 @@ wpas_dbus_signal_p2p_provision_discovery(struct wpa_supplicant *wpa_s, { } -static inline void wpas_dbus_signal_p2p_go_neg_req( - struct wpa_supplicant *wpa_s, - const u8 *src, - u16 dev_passwd_id) +static inline void wpas_dbus_signal_p2p_go_neg_req(struct wpa_supplicant *wpa_s, + const u8 *src, + u16 dev_passwd_id, + u8 go_intent) { } @@ -392,6 +406,12 @@ wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s, } static inline void +wpas_dbus_signal_p2p_group_formation_failure(struct wpa_supplicant *wpa_s, + const char *reason) +{ +} + +static inline void wpas_dbus_register_p2p_group(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { @@ -460,6 +480,11 @@ wpas_dbus_signal_p2p_peer_joined(struct wpa_supplicant *wpa_s, } static inline void +wpas_dbus_signal_p2p_find_stopped(struct wpa_supplicant *wpa_s) +{ +} + +static inline void wpas_dbus_signal_peer_device_found(struct wpa_supplicant *wpa_s, const u8 *dev_addr) { @@ -519,6 +544,14 @@ void wpas_dbus_signal_sta_deauthorized(struct wpa_supplicant *wpa_s, { } +static inline +void wpas_dbus_signal_p2p_invitation_received(struct wpa_supplicant *wpa_s, + const u8 *sa, const u8 *dev_addr, + const u8 *bssid, int id, + int op_freq) +{ +} + #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ #endif /* CTRL_IFACE_DBUS_H_NEW */ diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.c b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.c index f2e62ca..67562a5 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.c @@ -157,7 +157,8 @@ static struct wpa_supplicant * get_iface_by_dbus_path( struct wpa_supplicant *wpa_s; for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { - if (os_strcmp(wpa_s->dbus_new_path, path) == 0) + if (wpa_s->dbus_new_path && + os_strcmp(wpa_s->dbus_new_path, path) == 0) return wpa_s; } return NULL; @@ -213,7 +214,7 @@ dbus_bool_t set_network_properties(struct wpa_supplicant *wpa_s, } else if (entry.type == DBUS_TYPE_STRING) { if (should_quote_opt(entry.key)) { size = os_strlen(entry.str_value); - if (size <= 0) + if (size == 0) goto error; size += 3; @@ -600,7 +601,7 @@ DBusMessage * wpas_dbus_handler_create_interface(DBusMessage *message, iface.bridge_ifname = bridge_ifname; /* Otherwise, have wpa_supplicant attach to it. */ wpa_s = wpa_supplicant_add_iface(global, &iface, NULL); - if (wpa_s) { + if (wpa_s && wpa_s->dbus_new_path) { const char *path = wpa_s->dbus_new_path; reply = dbus_message_new_method_return(message); @@ -684,7 +685,7 @@ DBusMessage * wpas_dbus_handler_get_interface(DBusMessage *message, DBUS_TYPE_INVALID); wpa_s = wpa_supplicant_get_iface(global, ifname); - if (wpa_s == NULL) + if (wpa_s == NULL || wpa_s->dbus_new_path == NULL) return wpas_dbus_error_iface_unknown(message); path = wpa_s->dbus_new_path; @@ -876,8 +877,10 @@ dbus_bool_t wpas_dbus_getter_interfaces(DBusMessageIter *iter, unsigned int i = 0, num = 0; dbus_bool_t success; - for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) - num++; + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { + if (wpa_s->dbus_new_path) + num++; + } paths = os_calloc(num, sizeof(char *)); if (!paths) { @@ -885,8 +888,10 @@ dbus_bool_t wpas_dbus_getter_interfaces(DBusMessageIter *iter, return FALSE; } - for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) - paths[i++] = wpa_s->dbus_new_path; + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { + if (wpa_s->dbus_new_path) + paths[i++] = wpa_s->dbus_new_path; + } success = wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_OBJECT_PATH, @@ -1034,10 +1039,10 @@ static int wpas_dbus_get_scan_ssids(DBusMessage *message, DBusMessageIter *var, dbus_message_iter_get_fixed_array(&sub_array_iter, &val, &len); - if (len > MAX_SSID_LEN) { + if (len > SSID_MAX_LEN) { wpa_printf(MSG_DEBUG, "%s[dbus]: SSID too long (len=%d max_len=%d)", - __func__, len, MAX_SSID_LEN); + __func__, len, SSID_MAX_LEN); *reply = wpas_dbus_error_invalid_args( message, "Invalid SSID: too long"); return -1; @@ -1327,14 +1332,26 @@ DBusMessage * wpas_dbus_handler_scan(DBusMessage *message, message, "You can specify only Channels in passive scan"); goto out; - } else if (params.freqs && params.freqs[0]) { - if (wpa_supplicant_trigger_scan(wpa_s, ¶ms)) { - reply = wpas_dbus_error_scan_error( - message, "Scan request rejected"); - } } else { - wpa_s->scan_req = MANUAL_SCAN_REQ; - wpa_supplicant_req_scan(wpa_s, 0, 0); + if (wpa_s->sched_scanning) { + wpa_printf(MSG_DEBUG, + "%s[dbus]: Stop ongoing sched_scan to allow requested scan to proceed", + __func__); + wpa_supplicant_cancel_sched_scan(wpa_s); + } + + if (params.freqs && params.freqs[0]) { + wpa_s->last_scan_req = MANUAL_SCAN_REQ; + if (wpa_supplicant_trigger_scan(wpa_s, + ¶ms)) { + reply = wpas_dbus_error_scan_error( + message, + "Scan request rejected"); + } + } else { + wpa_s->scan_req = MANUAL_SCAN_REQ; + wpa_supplicant_req_scan(wpa_s, 0, 0); + } } } else if (os_strcmp(type, "active") == 0) { if (!params.num_ssids) { @@ -1344,6 +1361,14 @@ DBusMessage * wpas_dbus_handler_scan(DBusMessage *message, #ifdef CONFIG_AUTOSCAN autoscan_deinit(wpa_s); #endif /* CONFIG_AUTOSCAN */ + if (wpa_s->sched_scanning) { + wpa_printf(MSG_DEBUG, + "%s[dbus]: Stop ongoing sched_scan to allow requested scan to proceed", + __func__); + wpa_supplicant_cancel_sched_scan(wpa_s); + } + + wpa_s->last_scan_req = MANUAL_SCAN_REQ; if (wpa_supplicant_trigger_scan(wpa_s, ¶ms)) { reply = wpas_dbus_error_scan_error( message, "Scan request rejected"); @@ -1478,7 +1503,8 @@ DBusMessage * wpas_dbus_handler_add_network(DBusMessage *message, dbus_message_iter_init(message, &iter); - ssid = wpa_config_add_network(wpa_s->conf); + if (wpa_s->dbus_new_path) + ssid = wpa_config_add_network(wpa_s->conf); if (ssid == NULL) { wpa_printf(MSG_ERROR, "%s[dbus]: can't add new interface.", __func__); @@ -1577,6 +1603,30 @@ DBusMessage * wpas_dbus_handler_reattach(DBusMessage *message, /** + * wpas_dbus_handler_reconnect - Reconnect if disconnected + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: InterfaceDisabled DBus error message if disabled + * or NULL otherwise. + * + * Handler function for "Reconnect" method call of network interface. + */ +DBusMessage * wpas_dbus_handler_reconnect(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { + return dbus_message_new_error(message, + WPAS_DBUS_ERROR_IFACE_DISABLED, + "This interface is disabled"); + } + + if (wpa_s->disconnected) + wpas_request_connection(wpa_s); + return NULL; +} + + +/** * wpas_dbus_handler_remove_network - Remove a configured network * @message: Pointer to incoming dbus message * @wpa_s: wpa_supplicant structure for a network interface @@ -1602,7 +1652,7 @@ DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message, iface = wpas_dbus_new_decompose_object_path(op, WPAS_DBUS_NEW_NETWORKS_PART, &net_id); - if (iface == NULL || net_id == NULL || + if (iface == NULL || net_id == NULL || !wpa_s->dbus_new_path || os_strcmp(iface, wpa_s->dbus_new_path) != 0) { reply = wpas_dbus_error_invalid_args(message, op); goto out; @@ -1715,7 +1765,7 @@ DBusMessage * wpas_dbus_handler_select_network(DBusMessage *message, iface = wpas_dbus_new_decompose_object_path(op, WPAS_DBUS_NEW_NETWORKS_PART, &net_id); - if (iface == NULL || net_id == NULL || + if (iface == NULL || net_id == NULL || !wpa_s->dbus_new_path || os_strcmp(iface, wpa_s->dbus_new_path) != 0) { reply = wpas_dbus_error_invalid_args(message, op); goto out; @@ -1773,7 +1823,7 @@ DBusMessage * wpas_dbus_handler_network_reply(DBusMessage *message, iface = wpas_dbus_new_decompose_object_path(op, WPAS_DBUS_NEW_NETWORKS_PART, &net_id); - if (iface == NULL || net_id == NULL || + if (iface == NULL || net_id == NULL || !wpa_s->dbus_new_path || os_strcmp(iface, wpa_s->dbus_new_path) != 0) { reply = wpas_dbus_error_invalid_args(message, op); goto out; @@ -2266,12 +2316,14 @@ DBusMessage * wpas_dbus_handler_set_pkcs11_engine_and_module_path( message, DBUS_ERROR_FAILED, "Reinit of the EAPOL state machine with the new PKCS #11 engine and module path failed."); - wpa_dbus_mark_property_changed( - wpa_s->global->dbus, wpa_s->dbus_new_path, - WPAS_DBUS_NEW_IFACE_INTERFACE, "PKCS11EnginePath"); - wpa_dbus_mark_property_changed( - wpa_s->global->dbus, wpa_s->dbus_new_path, - WPAS_DBUS_NEW_IFACE_INTERFACE, "PKCS11ModulePath"); + if (wpa_s->dbus_new_path) { + wpa_dbus_mark_property_changed( + wpa_s->global->dbus, wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_INTERFACE, "PKCS11EnginePath"); + wpa_dbus_mark_property_changed( + wpa_s->global->dbus, wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_INTERFACE, "PKCS11ModulePath"); + } return NULL; } @@ -3024,7 +3076,7 @@ dbus_bool_t wpas_dbus_getter_current_bss(DBusMessageIter *iter, struct wpa_supplicant *wpa_s = user_data; char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *bss_obj_path = path_buf; - if (wpa_s->current_bss) + if (wpa_s->current_bss && wpa_s->dbus_new_path) os_snprintf(bss_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_BSSIDS_PART "/%u", wpa_s->dbus_new_path, wpa_s->current_bss->id); @@ -3052,7 +3104,7 @@ dbus_bool_t wpas_dbus_getter_current_network(DBusMessageIter *iter, struct wpa_supplicant *wpa_s = user_data; char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *net_obj_path = path_buf; - if (wpa_s->current_ssid) + if (wpa_s->current_ssid && wpa_s->dbus_new_path) os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%u", wpa_s->dbus_new_path, wpa_s->current_ssid->id); @@ -3140,6 +3192,12 @@ dbus_bool_t wpas_dbus_getter_bsss(DBusMessageIter *iter, DBusError *error, unsigned int i = 0; dbus_bool_t success = FALSE; + if (!wpa_s->dbus_new_path) { + dbus_set_error(error, DBUS_ERROR_FAILED, + "%s: no D-Bus interface", __func__); + return FALSE; + } + paths = os_calloc(wpa_s->num_bss, sizeof(char *)); if (!paths) { dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); @@ -3191,6 +3249,12 @@ dbus_bool_t wpas_dbus_getter_networks(DBusMessageIter *iter, DBusError *error, unsigned int i = 0, num = 0; dbus_bool_t success = FALSE; + if (!wpa_s->dbus_new_path) { + dbus_set_error(error, DBUS_ERROR_FAILED, + "%s: no D-Bus interface", __func__); + return FALSE; + } + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) if (!network_is_persistent_group(ssid)) num++; @@ -3791,6 +3855,7 @@ dbus_bool_t wpas_dbus_getter_bss_wps(DBusMessageIter *iter, DBusError *error, struct wpabuf *wps_ie; #endif /* CONFIG_WPS */ DBusMessageIter iter_dict, variant_iter; + int wps_support = 0; const char *type = ""; res = get_bss_helper(args, error, __func__); @@ -3805,6 +3870,7 @@ dbus_bool_t wpas_dbus_getter_bss_wps(DBusMessageIter *iter, DBusError *error, #ifdef CONFIG_WPS wps_ie = wpa_bss_get_vendor_ie_multi(res, WPS_IE_VENDOR_TYPE); if (wps_ie) { + wps_support = 1; if (wps_is_selected_pbc_registrar(wps_ie)) type = "pbc"; else if (wps_is_selected_pin_registrar(wps_ie)) @@ -3814,7 +3880,7 @@ dbus_bool_t wpas_dbus_getter_bss_wps(DBusMessageIter *iter, DBusError *error, } #endif /* CONFIG_WPS */ - if (!wpa_dbus_dict_append_string(&iter_dict, "Type", type) || + if ((wps_support && !wpa_dbus_dict_append_string(&iter_dict, "Type", type)) || !wpa_dbus_dict_close_write(&variant_iter, &iter_dict) || !dbus_message_iter_close_container(iter, &variant_iter)) goto nomem; @@ -4102,7 +4168,7 @@ void wpas_dbus_signal_preq(struct wpa_supplicant *wpa_s, struct wpas_dbus_priv *priv = wpa_s->global->dbus; /* Do nothing if the control interface is not turned on */ - if (priv == NULL) + if (priv == NULL || !wpa_s->dbus_new_path) return; if (wpa_s->preq_notify_peer == NULL) diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.h b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.h index 6113db5..50f72ec 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.h +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.h @@ -107,6 +107,9 @@ DBusMessage * wpas_dbus_handler_reassociate(DBusMessage *message, DBusMessage * wpas_dbus_handler_reattach(DBusMessage *message, struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_reconnect(DBusMessage *message, + struct wpa_supplicant *wpa_s); + DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message, struct wpa_supplicant *wpa_s); @@ -291,6 +294,9 @@ dbus_bool_t wpas_dbus_setter_network_properties(DBusMessageIter *iter, DBusMessage * wpas_dbus_handler_wps_start(DBusMessage *message, struct wpa_supplicant *wpa_s); +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); 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 0eff763..67c079e 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.c @@ -127,8 +127,7 @@ DBusMessage * wpas_dbus_handler_p2p_find(DBusMessage *message, wpa_dbus_dict_entry_clear(&entry); } - if (wpa_s->p2p_dev) - wpa_s = wpa_s->p2p_dev; + wpa_s = wpa_s->global->p2p_init_wpa_s; wpas_p2p_find(wpa_s, timeout, type, num_req_dev_types, req_dev_types, NULL, 0, 0, NULL, 0); @@ -147,10 +146,7 @@ error: DBusMessage * wpas_dbus_handler_p2p_stop_find(DBusMessage *message, struct wpa_supplicant *wpa_s) { - if (wpa_s->p2p_dev) - wpa_s = wpa_s->p2p_dev; - - wpas_p2p_stop_find(wpa_s); + wpas_p2p_stop_find(wpa_s->global->p2p_init_wpa_s); return NULL; } @@ -168,8 +164,7 @@ DBusMessage * wpas_dbus_handler_p2p_rejectpeer(DBusMessage *message, if (parse_peer_object_path(peer_object_path, peer_addr) < 0) return wpas_dbus_error_invalid_args(message, NULL); - if (wpa_s->p2p_dev) - wpa_s = wpa_s->p2p_dev; + wpa_s = wpa_s->global->p2p_init_wpa_s; if (wpas_p2p_reject(wpa_s, peer_addr) < 0) return wpas_dbus_error_unknown_error(message, @@ -188,8 +183,7 @@ DBusMessage * wpas_dbus_handler_p2p_listen(DBusMessage *message, DBUS_TYPE_INVALID)) return wpas_dbus_error_no_memory(message); - if (wpa_s->p2p_dev) - wpa_s = wpa_s->p2p_dev; + wpa_s = wpa_s->global->p2p_init_wpa_s; if (wpas_p2p_listen(wpa_s, (unsigned int) timeout)) { return dbus_message_new_error(message, @@ -230,8 +224,7 @@ DBusMessage * wpas_dbus_handler_p2p_extendedlisten( wpa_dbus_dict_entry_clear(&entry); } - if (wpa_s->p2p_dev) - wpa_s = wpa_s->p2p_dev; + wpa_s = wpa_s->global->p2p_init_wpa_s; if (wpas_p2p_ext_listen(wpa_s, period, interval)) return wpas_dbus_error_unknown_error( @@ -282,9 +275,6 @@ DBusMessage * wpas_dbus_handler_p2p_presence_request( wpa_dbus_dict_entry_clear(&entry); } - if (wpa_s->p2p_dev) - wpa_s = wpa_s->p2p_dev; - if (wpas_p2p_presence_req(wpa_s, dur1, int1, dur2, int2) < 0) return wpas_dbus_error_unknown_error(message, "Failed to invoke presence request."); @@ -339,8 +329,7 @@ DBusMessage * wpas_dbus_handler_p2p_group_add(DBusMessage *message, wpa_dbus_dict_entry_clear(&entry); } - if (wpa_s->p2p_dev) - wpa_s = wpa_s->p2p_dev; + wpa_s = wpa_s->global->p2p_init_wpa_s; if (pg_object_path != NULL) { char *net_id_str; @@ -354,7 +343,8 @@ DBusMessage * wpas_dbus_handler_p2p_group_add(DBusMessage *message, pg_object_path, WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART, &net_id_str); if (iface == NULL || net_id_str == NULL || - os_strcmp(iface, wpa_s->dbus_new_path) != 0) { + !wpa_s->parent->dbus_new_path || + os_strcmp(iface, wpa_s->parent->dbus_new_path) != 0) { reply = wpas_dbus_error_invalid_args(message, pg_object_path); @@ -374,7 +364,7 @@ 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)) { + NULL, 0, 0)) { reply = wpas_dbus_error_unknown_error( message, "Failed to reinvoke a persistent group"); @@ -426,6 +416,64 @@ static dbus_bool_t wpa_dbus_p2p_check_enabled(struct wpa_supplicant *wpa_s, } +DBusMessage * wpas_dbus_handler_p2p_remove_client(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessageIter iter_dict; + DBusMessage *reply = NULL; + DBusMessageIter iter; + struct wpa_dbus_dict_entry entry; + char *peer_object_path = NULL; + char *interface_addr = NULL; + u8 peer_addr[ETH_ALEN]; + + if (!wpa_dbus_p2p_check_enabled(wpa_s, message, &reply, NULL)) + return reply; + + dbus_message_iter_init(message, &iter); + + if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) + goto err; + + while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { + if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) + goto err; + + if (os_strcmp(entry.key, "peer") == 0 && + entry.type == DBUS_TYPE_OBJECT_PATH) { + os_free(peer_object_path); + peer_object_path = os_strdup(entry.str_value); + wpa_dbus_dict_entry_clear(&entry); + } else if (os_strcmp(entry.key, "iface") == 0 && + entry.type == DBUS_TYPE_STRING) { + os_free(interface_addr); + interface_addr = os_strdup(entry.str_value); + wpa_dbus_dict_entry_clear(&entry); + } else { + wpa_dbus_dict_entry_clear(&entry); + goto err; + } + } + + if ((!peer_object_path && !interface_addr) || + (peer_object_path && + (parse_peer_object_path(peer_object_path, peer_addr) < 0 || + !p2p_peer_known(wpa_s->global->p2p, peer_addr))) || + (interface_addr && hwaddr_aton(interface_addr, peer_addr) < 0)) + goto err; + + wpas_p2p_remove_client(wpa_s, peer_addr, interface_addr != NULL); + reply = NULL; +out: + os_free(peer_object_path); + os_free(interface_addr); + return reply; +err: + reply = wpas_dbus_error_invalid_args(message, "Invalid address format"); + goto out; +} + + DBusMessage * wpas_dbus_handler_p2p_flush(DBusMessage *message, struct wpa_supplicant *wpa_s) { @@ -434,8 +482,7 @@ DBusMessage * wpas_dbus_handler_p2p_flush(DBusMessage *message, if (!wpa_dbus_p2p_check_enabled(wpa_s, message, &reply, NULL)) return reply; - if (wpa_s->p2p_dev) - wpa_s = wpa_s->p2p_dev; + wpa_s = wpa_s->global->p2p_init_wpa_s; os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN); wpa_s->force_long_sd = 0; @@ -531,8 +578,7 @@ DBusMessage * wpas_dbus_handler_p2p_connect(DBusMessage *message, if ((!pin || !pin[0]) && wps_method == WPS_PIN_KEYPAD) goto inv_args; - if (wpa_s->p2p_dev) - wpa_s = wpa_s->p2p_dev; + wpa_s = wpa_s->global->p2p_init_wpa_s; new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method, persistent_group, 0, join, authorize_only, @@ -587,6 +633,26 @@ inv_args: } +/** + * wpas_dbus_handler_p2p_cancel - Cancel P2P group formation + * @message: Pointer to incoming dbus message + * @wpa_s: %wpa_supplicant data structure + * Returns: NULL on success or DBus error on failure + * + * Handler for "Cancel" method call. Returns NULL if P2P cancel succeeds or DBus + * error on P2P cancel failure + */ +DBusMessage * wpas_dbus_handler_p2p_cancel(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + if (wpas_p2p_cancel(wpa_s)) + return wpas_dbus_error_unknown_error(message, + "P2P cancel failed"); + + return NULL; +} + + DBusMessage * wpas_dbus_handler_p2p_invite(DBusMessage *message, struct wpa_supplicant *wpa_s) { @@ -634,8 +700,7 @@ DBusMessage * wpas_dbus_handler_p2p_invite(DBusMessage *message, !p2p_peer_known(wpa_s->global->p2p, peer_addr)) goto err; - if (wpa_s->p2p_dev) - wpa_s = wpa_s->p2p_dev; + wpa_s = wpa_s->global->p2p_init_wpa_s; if (persistent) { char *net_id_str; @@ -649,7 +714,8 @@ DBusMessage * wpas_dbus_handler_p2p_invite(DBusMessage *message, WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART, &net_id_str); if (iface == NULL || net_id_str == NULL || - os_strcmp(iface, wpa_s->dbus_new_path) != 0) { + !wpa_s->parent->dbus_new_path || + os_strcmp(iface, wpa_s->parent->dbus_new_path) != 0) { reply = wpas_dbus_error_invalid_args(message, pg_object_path); goto out; @@ -726,8 +792,7 @@ DBusMessage * wpas_dbus_handler_p2p_prov_disc_req(DBusMessage *message, os_strcmp(config_method, "pushbutton")) return wpas_dbus_error_invalid_args(message, NULL); - if (wpa_s->p2p_dev) - wpa_s = wpa_s->p2p_dev; + wpa_s = wpa_s->global->p2p_init_wpa_s; if (wpas_p2p_prov_disc(wpa_s, peer_addr, config_method, WPAS_P2P_PD_FOR_GO_NEG, NULL) < 0) @@ -758,8 +823,7 @@ dbus_bool_t wpas_dbus_getter_p2p_device_config(DBusMessageIter *iter, if (!wpa_dbus_p2p_check_enabled(wpa_s, NULL, NULL, error)) return FALSE; - if (wpa_s->p2p_dev) - wpa_s = wpa_s->p2p_dev; + wpa_s = wpa_s->global->p2p_init_wpa_s; if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "a{sv}", &variant_iter) || @@ -864,8 +928,7 @@ dbus_bool_t wpas_dbus_setter_p2p_device_config(DBusMessageIter *iter, if (!wpa_dbus_p2p_check_enabled(wpa_s, NULL, NULL, error)) return FALSE; - if (wpa_s->p2p_dev) - wpa_s = wpa_s->p2p_dev; + wpa_s = wpa_s->global->p2p_init_wpa_s; dbus_message_iter_recurse(iter, &variant_iter); if (!wpa_dbus_dict_open_read(&variant_iter, &iter_dict, error)) @@ -1043,7 +1106,8 @@ dbus_bool_t wpas_dbus_getter_p2p_peers(DBusMessageIter *iter, DBusError *error, char **peer_obj_paths = NULL; - if (!wpa_dbus_p2p_check_enabled(wpa_s, NULL, NULL, error)) + if (!wpa_dbus_p2p_check_enabled(wpa_s, NULL, NULL, error) || + !wpa_s->parent->parent->dbus_new_path) return FALSE; dl_list_init(&peer_objpath_list); @@ -1064,7 +1128,8 @@ dbus_bool_t wpas_dbus_getter_p2p_peers(DBusMessageIter *iter, DBusError *error, os_snprintf(node->path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR, - wpa_s->dbus_new_path, MAC2STR(addr)); + wpa_s->parent->parent->dbus_new_path, + MAC2STR(addr)); dl_list_add_tail(&peer_objpath_list, &node->list); num++; @@ -1184,13 +1249,17 @@ dbus_bool_t wpas_dbus_getter_p2p_peergo(DBusMessageIter *iter, struct wpa_supplicant *wpa_s = user_data; char go_peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path; + if (!wpa_s->parent->parent->dbus_new_path) + return FALSE; + if (wpas_get_p2p_role(wpa_s) != WPAS_P2P_ROLE_CLIENT) os_snprintf(go_peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "/"); else os_snprintf(go_peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR, - wpa_s->dbus_new_path, MAC2STR(wpa_s->go_dev_addr)); + wpa_s->parent->parent->dbus_new_path, + MAC2STR(wpa_s->go_dev_addr)); path = go_peer_obj_path; return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_OBJECT_PATH, @@ -1240,6 +1309,154 @@ 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) +{ + struct peer_handler_args *peer_args = user_data; + const struct p2p_peer_info *info; + char *tmp; + + if (!wpa_dbus_p2p_check_enabled(peer_args->wpa_s, NULL, NULL, error)) + return FALSE; + + /* get the peer info */ + info = p2p_get_peer_found(peer_args->wpa_s->global->p2p, + peer_args->p2p_device_addr, 0); + if (info == NULL) { + dbus_set_error(error, DBUS_ERROR_FAILED, "failed to find peer"); + return FALSE; + } + + tmp = os_strdup(info->manufacturer); + if (!tmp) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + return FALSE; + } + + if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &tmp, + error)) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + os_free(tmp); + return FALSE; + } + + os_free(tmp); + return TRUE; +} + + +dbus_bool_t wpas_dbus_getter_p2p_peer_modelname(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct peer_handler_args *peer_args = user_data; + const struct p2p_peer_info *info; + char *tmp; + + if (!wpa_dbus_p2p_check_enabled(peer_args->wpa_s, NULL, NULL, error)) + return FALSE; + + /* get the peer info */ + info = p2p_get_peer_found(peer_args->wpa_s->global->p2p, + peer_args->p2p_device_addr, 0); + if (info == NULL) { + dbus_set_error(error, DBUS_ERROR_FAILED, "failed to find peer"); + return FALSE; + } + + tmp = os_strdup(info->model_name); + if (!tmp) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + return FALSE; + } + + if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &tmp, + error)) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + os_free(tmp); + return FALSE; + } + + os_free(tmp); + return TRUE; +} + + +dbus_bool_t wpas_dbus_getter_p2p_peer_modelnumber(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct peer_handler_args *peer_args = user_data; + const struct p2p_peer_info *info; + char *tmp; + + if (!wpa_dbus_p2p_check_enabled(peer_args->wpa_s, NULL, NULL, error)) + return FALSE; + + /* get the peer info */ + info = p2p_get_peer_found(peer_args->wpa_s->global->p2p, + peer_args->p2p_device_addr, 0); + if (info == NULL) { + dbus_set_error(error, DBUS_ERROR_FAILED, "failed to find peer"); + return FALSE; + } + + tmp = os_strdup(info->model_number); + if (!tmp) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + return FALSE; + } + + if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &tmp, + error)) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + os_free(tmp); + return FALSE; + } + + os_free(tmp); + return TRUE; +} + + +dbus_bool_t wpas_dbus_getter_p2p_peer_serialnumber(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct peer_handler_args *peer_args = user_data; + const struct p2p_peer_info *info; + char *tmp; + + if (!wpa_dbus_p2p_check_enabled(peer_args->wpa_s, NULL, NULL, error)) + return FALSE; + + /* get the peer info */ + info = p2p_get_peer_found(peer_args->wpa_s->global->p2p, + peer_args->p2p_device_addr, 0); + if (info == NULL) { + dbus_set_error(error, DBUS_ERROR_FAILED, "failed to find peer"); + return FALSE; + } + + tmp = os_strdup(info->serial_number); + if (!tmp) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + return FALSE; + } + + if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &tmp, + error)) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + os_free(tmp); + return FALSE; + } + + os_free(tmp); + return TRUE; +} + + dbus_bool_t wpas_dbus_getter_p2p_peer_primary_device_type( DBusMessageIter *iter, DBusError *error, void *user_data) { @@ -1578,8 +1795,7 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_groups(DBusMessageIter *iter, os_memset(&data, 0, sizeof(data)); wpa_s = peer_args->wpa_s; - if (wpa_s->p2p_dev) - wpa_s = wpa_s->p2p_dev; + wpa_s = wpa_s->global->p2p_init_wpa_s; wpa_s_go = wpas_get_p2p_client_iface(wpa_s, info->p2p_device_addr); if (wpa_s_go) { @@ -1636,6 +1852,10 @@ dbus_bool_t wpas_dbus_getter_persistent_groups(DBusMessageIter *iter, unsigned int i = 0, num = 0; dbus_bool_t success = FALSE; + wpa_s = wpa_s->global->p2p_init_wpa_s; + if (!wpa_s->parent->dbus_new_path) + return FALSE; + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) if (network_is_persistent_group(ssid)) num++; @@ -1659,7 +1879,7 @@ dbus_bool_t wpas_dbus_getter_persistent_groups(DBusMessageIter *iter, /* Construct the object path for this network. */ os_snprintf(paths[i++], WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%d", - wpa_s->dbus_new_path, ssid->id); + wpa_s->parent->dbus_new_path, ssid->id); } success = wpas_dbus_simple_array_property_getter(iter, @@ -1698,7 +1918,7 @@ dbus_bool_t wpas_dbus_getter_persistent_group_properties(DBusMessageIter *iter, /** - * wpas_dbus_setter_persistent_group_properties - Get options for a persistent + * wpas_dbus_setter_persistent_group_properties - Set options for a persistent * group * @iter: Pointer to incoming dbus message iter * @error: Location to store error on failure @@ -1746,7 +1966,9 @@ DBusMessage * wpas_dbus_handler_add_persistent_group( dbus_message_iter_init(message, &iter); - ssid = wpa_config_add_network(wpa_s->conf); + wpa_s = wpa_s->global->p2p_init_wpa_s; + if (wpa_s->parent->dbus_new_path) + ssid = wpa_config_add_network(wpa_s->conf); if (ssid == NULL) { wpa_printf(MSG_ERROR, "dbus: %s: Cannot add new persistent group", @@ -1779,7 +2001,7 @@ DBusMessage * wpas_dbus_handler_add_persistent_group( /* Construct the object path for this network. */ os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%d", - wpa_s->dbus_new_path, ssid->id); + wpa_s->parent->dbus_new_path, ssid->id); reply = dbus_message_new_method_return(message); if (reply == NULL) { @@ -1826,6 +2048,8 @@ DBusMessage * wpas_dbus_handler_remove_persistent_group( dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &op, DBUS_TYPE_INVALID); + wpa_s = wpa_s->global->p2p_init_wpa_s; + /* * Extract the network ID and ensure the network is actually a child of * this interface. @@ -1834,7 +2058,8 @@ DBusMessage * wpas_dbus_handler_remove_persistent_group( op, WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART, &persistent_group_id); if (iface == NULL || persistent_group_id == NULL || - os_strcmp(iface, wpa_s->dbus_new_path) != 0) { + !wpa_s->parent->dbus_new_path || + os_strcmp(iface, wpa_s->parent->dbus_new_path) != 0) { reply = wpas_dbus_error_invalid_args(message, op); goto out; } @@ -1899,6 +2124,8 @@ DBusMessage * wpas_dbus_handler_remove_all_persistent_groups( struct wpa_ssid *ssid, *next; struct wpa_config *config; + wpa_s = wpa_s->global->p2p_init_wpa_s; + config = wpa_s->conf; ssid = config->ssid; while (ssid) { @@ -1928,6 +2155,9 @@ dbus_bool_t wpas_dbus_getter_p2p_group_members(DBusMessageIter *iter, const u8 *addr; dbus_bool_t success = FALSE; + if (!wpa_s->parent->parent->dbus_new_path) + return FALSE; + /* Verify correct role for this property */ if (wpas_get_p2p_role(wpa_s) != WPAS_P2P_ROLE_GO) { return wpas_dbus_simple_array_property_getter( @@ -1955,7 +2185,8 @@ dbus_bool_t wpas_dbus_getter_p2p_group_members(DBusMessageIter *iter, os_snprintf(paths[i], WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR, - wpa_s->parent->dbus_new_path, MAC2STR(addr)); + wpa_s->parent->parent->dbus_new_path, + MAC2STR(addr)); i++; } 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 fdaccba..2aecbbe 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.h +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.h @@ -46,6 +46,9 @@ DBusMessage *wpas_dbus_handler_p2p_connect( DBusMessage *message, struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_p2p_cancel(DBusMessage *message, + struct wpa_supplicant *wpa_s); + DBusMessage *wpas_dbus_handler_p2p_invite( DBusMessage *message, struct wpa_supplicant *wpa_s); @@ -53,6 +56,9 @@ DBusMessage *wpas_dbus_handler_p2p_invite( DBusMessage *wpas_dbus_handler_p2p_disconnect( DBusMessage *message, struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_p2p_remove_client( + DBusMessage *message, struct wpa_supplicant *wpa_s); + DBusMessage *wpas_dbus_handler_p2p_flush( DBusMessage *message, struct wpa_supplicant *wpa_s); @@ -112,6 +118,22 @@ 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); 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 a94a0e5..b2251ba 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_wps.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_wps.c @@ -53,7 +53,7 @@ static int wpas_dbus_handler_wps_role(DBusMessage *message, else if (os_strcmp(val, "registrar") == 0) params->role = 2; else { - wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Uknown role %s", val); + wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Unknown role %s", val); *reply = wpas_dbus_error_invalid_args(message, val); return -1; } @@ -113,7 +113,7 @@ static int wpas_dbus_handler_wps_bssid(DBusMessage *message, dbus_message_iter_recurse(&variant_iter, &array_iter); dbus_message_iter_get_fixed_array(&array_iter, ¶ms->bssid, &len); if (len != ETH_ALEN) { - wpa_printf(MSG_DEBUG, "dbus: WPS.Stsrt - Wrong Bssid length %d", + wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong Bssid length %d", len); *reply = wpas_dbus_error_invalid_args(message, "Bssid is wrong length"); @@ -320,6 +320,26 @@ DBusMessage * wpas_dbus_handler_wps_start(DBusMessage *message, /** + * wpas_dbus_handler_wps_cancel - Cancel ongoing WPS configuration + * @message: Pointer to incoming dbus 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 + * or DBus error on WPS cancel failure + */ +DBusMessage * wpas_dbus_handler_wps_cancel(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + if (wpas_wps_cancel(wpa_s)) + return wpas_dbus_error_unknown_error(message, + "WPS cancel failed"); + + return NULL; +} + + +/** * wpas_dbus_getter_process_credentials - Check if credentials are processed * @message: Pointer to incoming dbus message * @wpa_s: %wpa_supplicant data structure @@ -358,6 +378,8 @@ dbus_bool_t wpas_dbus_setter_process_credentials(DBusMessageIter *iter, struct wpa_supplicant *wpa_s = user_data; dbus_bool_t process_credentials, old_pc; + if (!wpa_s->dbus_new_path) + return FALSE; if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_BOOLEAN, &process_credentials)) return FALSE; diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.c b/contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.c index 15b0901..45623f3 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.c @@ -46,8 +46,13 @@ 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(&entry_iter, error, user_data)) { + wpa_printf(MSG_INFO, + "dbus: %s dbus_interface=%s dbus_property=%s getter failed", + __func__, dsc->dbus_interface, + dsc->dbus_property); return FALSE; + } if (!dbus_message_iter_close_container(dict_iter, &entry_iter)) goto error; diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_introspect.c b/contrib/wpa/wpa_supplicant/dbus/dbus_new_introspect.c index 6209c67..fba57e6 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_introspect.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_introspect.c @@ -257,7 +257,7 @@ DBusMessage * wpa_dbus_introspect(DBusMessage *message, DBusMessage *reply; struct wpabuf *xml; - xml = wpabuf_alloc(10000); + xml = wpabuf_alloc(15000); if (xml == NULL) return NULL; diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_old.c b/contrib/wpa/wpa_supplicant/dbus/dbus_old.c index 45bb402..88227af 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_old.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_old.c @@ -383,7 +383,7 @@ void wpa_supplicant_dbus_notify_scan_results(struct wpa_supplicant *wpa_s) DBusMessage *_signal; /* Do nothing if the control interface is not turned on */ - if (iface == NULL) + if (iface == NULL || !wpa_s->dbus_path) return; _signal = dbus_message_new_signal(wpa_s->dbus_path, @@ -474,7 +474,7 @@ void wpa_supplicant_dbus_notify_scanning(struct wpa_supplicant *wpa_s) dbus_bool_t scanning = wpa_s->scanning ? TRUE : FALSE; /* Do nothing if the control interface is not turned on */ - if (iface == NULL) + if (iface == NULL || !wpa_s->dbus_path) return; _signal = dbus_message_new_signal(wpa_s->dbus_path, @@ -509,7 +509,7 @@ void wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s, if (wpa_s->global == NULL) return; iface = wpa_s->global->dbus; - if (iface == NULL) + if (iface == NULL || !wpa_s->dbus_path) return; _signal = dbus_message_new_signal(wpa_s->dbus_path, @@ -559,7 +559,7 @@ void wpa_supplicant_dbus_notify_certification(struct wpa_supplicant *wpa_s, if (wpa_s->global == NULL) return; iface = wpa_s->global->dbus; - if (iface == NULL) + if (iface == NULL || !wpa_s->dbus_path) return; _signal = dbus_message_new_signal(wpa_s->dbus_path, @@ -738,7 +738,7 @@ struct wpa_supplicant * wpa_supplicant_get_iface_by_dbus_path( struct wpa_supplicant *wpa_s; for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { - if (strcmp(wpa_s->dbus_path, path) == 0) + if (wpa_s->dbus_path && strcmp(wpa_s->dbus_path, path) == 0) return wpa_s; } 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 773ee8b..e8f62ef 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers.c @@ -166,7 +166,7 @@ DBusMessage * wpas_dbus_global_add_interface(DBusMessage *message, iface.bridge_ifname = bridge_ifname; /* Otherwise, have wpa_supplicant attach to it. */ wpa_s = wpa_supplicant_add_iface(global, &iface, NULL); - if (wpa_s) { + if (wpa_s && wpa_s->dbus_path) { const char *path = wpa_s->dbus_path; reply = dbus_message_new_method_return(message); @@ -262,7 +262,7 @@ DBusMessage * wpas_dbus_global_get_interface(DBusMessage *message, } wpa_s = wpa_supplicant_get_iface(global, ifname); - if (wpa_s == NULL) { + if (wpa_s == NULL || !wpa_s->dbus_path) { reply = wpas_dbus_new_invalid_iface_error(message); goto out; } @@ -354,6 +354,11 @@ DBusMessage * wpas_dbus_iface_scan_results(DBusMessage *message, DBusMessageIter sub_iter; struct wpa_bss *bss; + if (!wpa_s->dbus_path) + return dbus_message_new_error(message, + WPAS_ERROR_INTERNAL_ERROR, + "no D-Bus interface available"); + /* Create and initialize the return message */ reply = dbus_message_new_method_return(message); dbus_message_iter_init_append(reply, &iter); @@ -495,7 +500,7 @@ DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message, /* EAP methods */ eap_methods = eap_get_names_as_string_array(&num_items); if (eap_methods) { - dbus_bool_t success = FALSE; + dbus_bool_t success; size_t i = 0; success = wpa_dbus_dict_append_string_array( @@ -708,10 +713,11 @@ DBusMessage * wpas_dbus_iface_add_network(DBusMessage *message, struct wpa_supplicant *wpa_s) { DBusMessage *reply = NULL; - struct wpa_ssid *ssid; + struct wpa_ssid *ssid = NULL; char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *path = path_buf; - ssid = wpa_config_add_network(wpa_s->conf); + if (wpa_s->dbus_path) + ssid = wpa_config_add_network(wpa_s->conf); if (ssid == NULL) { reply = dbus_message_new_error( message, WPAS_ERROR_ADD_NETWORK_ERROR, @@ -769,7 +775,7 @@ DBusMessage * wpas_dbus_iface_remove_network(DBusMessage *message, } /* Ensure the network is actually a child of this interface */ - if (os_strcmp(iface, wpa_s->dbus_path) != 0) { + if (!wpa_s->dbus_path || os_strcmp(iface, wpa_s->dbus_path) != 0) { reply = wpas_dbus_new_invalid_network_error(message); goto out; } @@ -803,10 +809,10 @@ out: } -static const char const *dont_quote[] = { +static const char * const dont_quote[] = { "key_mgmt", "proto", "pairwise", "auth_alg", "group", "eap", "opensc_engine_path", "pkcs11_engine_path", "pkcs11_module_path", - "bssid", NULL + "bssid", "scan_freq", "freq_list", NULL }; @@ -878,7 +884,7 @@ DBusMessage * wpas_dbus_iface_set_network(DBusMessage *message, if (should_quote_opt(entry.key)) { size = os_strlen(entry.str_value); /* Zero-length option check */ - if (size <= 0) + if (size == 0) goto error; size += 3; /* For quotes and terminator */ value = os_zalloc(size); @@ -1020,7 +1026,7 @@ DBusMessage * wpas_dbus_iface_select_network(DBusMessage *message, goto out; } /* Ensure the object path really points to this interface */ - if (network == NULL || + if (network == NULL || !wpa_s->dbus_path || os_strcmp(iface_obj_path, wpa_s->dbus_path) != 0) { reply = wpas_dbus_new_invalid_network_error(message); goto out; diff --git a/contrib/wpa/wpa_supplicant/defconfig b/contrib/wpa/wpa_supplicant/defconfig index 7f627fd..01a8c2c 100644 --- a/contrib/wpa/wpa_supplicant/defconfig +++ b/contrib/wpa/wpa_supplicant/defconfig @@ -495,3 +495,12 @@ CONFIG_PEERKEY=y # # External password backend for testing purposes (developer use) #CONFIG_EXT_PASSWORD_TEST=y + +# Enable Fast Session Transfer (FST) +#CONFIG_FST=y + +# Enable CLI commands for FST testing +#CONFIG_FST_TEST=y + +# OS X builds. This is only for building eapol_test. +#CONFIG_OSX=y diff --git a/contrib/wpa/wpa_supplicant/driver_i.h b/contrib/wpa/wpa_supplicant/driver_i.h index 65b430d..73768c7 100644 --- a/contrib/wpa/wpa_supplicant/driver_i.h +++ b/contrib/wpa/wpa_supplicant/driver_i.h @@ -286,11 +286,13 @@ static inline int wpa_drv_set_country(struct wpa_supplicant *wpa_s, } static inline int wpa_drv_send_mlme(struct wpa_supplicant *wpa_s, - const u8 *data, size_t data_len, int noack) + const u8 *data, size_t data_len, int noack, + unsigned int freq) { if (wpa_s->driver->send_mlme) return wpa_s->driver->send_mlme(wpa_s->drv_priv, - data, data_len, noack); + data, data_len, noack, + freq); return -1; } @@ -503,13 +505,6 @@ static inline int wpa_drv_set_ap_wps_ie(struct wpa_supplicant *wpa_s, proberesp, assocresp); } -static inline int wpa_drv_shared_freq(struct wpa_supplicant *wpa_s) -{ - if (!wpa_s->driver->shared_freq) - return -1; - return wpa_s->driver->shared_freq(wpa_s->drv_priv); -} - static inline int wpa_drv_get_noa(struct wpa_supplicant *wpa_s, u8 *buf, size_t buf_len) { @@ -890,4 +885,31 @@ static inline int wpa_drv_disable_transmit_sa(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_MACSEC */ +static inline int wpa_drv_setband(struct wpa_supplicant *wpa_s, + enum set_band band) +{ + if (!wpa_s->driver->set_band) + return -1; + return wpa_s->driver->set_band(wpa_s->drv_priv, band); +} + +static inline int wpa_drv_get_pref_freq_list(struct wpa_supplicant *wpa_s, + enum wpa_driver_if_type if_type, + unsigned int *num, + unsigned int *freq_list) +{ + if (!wpa_s->driver->get_pref_freq_list) + return -1; + return wpa_s->driver->get_pref_freq_list(wpa_s->drv_priv, if_type, + num, freq_list); +} + +static inline int wpa_drv_set_prob_oper_freq(struct wpa_supplicant *wpa_s, + unsigned int freq) +{ + if (!wpa_s->driver->set_prob_oper_freq) + return 0; + return wpa_s->driver->set_prob_oper_freq(wpa_s->drv_priv, freq); +} + #endif /* DRIVER_I_H */ diff --git a/contrib/wpa/wpa_supplicant/eapol_test.c b/contrib/wpa/wpa_supplicant/eapol_test.c index 9b7af30..dce7d1f 100644 --- a/contrib/wpa/wpa_supplicant/eapol_test.c +++ b/contrib/wpa/wpa_supplicant/eapol_test.c @@ -30,7 +30,7 @@ #include "wpas_glue.h" -struct wpa_driver_ops *wpa_drivers[] = { NULL }; +const struct wpa_driver_ops *const wpa_drivers[] = { NULL }; struct extra_radius_attr { @@ -76,6 +76,9 @@ struct eapol_test_data { const char *pcsc_reader; const char *pcsc_pin; + + unsigned int ctrl_iface:1; + unsigned int id_req_sent:1; }; static struct eapol_test_data eapol_test; @@ -329,7 +332,11 @@ eapol_test_get_config_blob(void *ctx, const char *name) static void eapol_test_eapol_done_cb(void *ctx) { + struct eapol_test_data *e = ctx; + printf("WPA: EAPOL processing complete\n"); + wpa_supplicant_cancel_auth_timeout(e->wpa_s); + wpa_supplicant_set_state(e->wpa_s, WPA_COMPLETED); } @@ -407,6 +414,9 @@ static void eapol_sm_cb(struct eapol_sm *eapol, enum eapol_supp_result result, { struct eapol_test_data *e = ctx; printf("eapol_sm_cb: result=%d\n", result); + e->id_req_sent = 0; + if (e->ctrl_iface) + return; e->eapol_test_num_reauths--; if (e->eapol_test_num_reauths < 0) eloop_terminate(); @@ -552,11 +562,21 @@ static void eapol_test_set_anon_id(void *ctx, const u8 *id, size_t len) } +static enum wpa_states eapol_test_get_state(void *ctx) +{ + struct eapol_test_data *e = ctx; + struct wpa_supplicant *wpa_s = e->wpa_s; + + return wpa_s->wpa_state; +} + + static int test_eapol(struct eapol_test_data *e, struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { struct eapol_config eapol_conf; struct eapol_ctx *ctx; + struct wpa_sm_ctx *wctx; ctx = os_zalloc(sizeof(*ctx)); if (ctx == NULL) { @@ -590,6 +610,25 @@ static int test_eapol(struct eapol_test_data *e, struct wpa_supplicant *wpa_s, return -1; } + wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_NO_WPA; + wctx = os_zalloc(sizeof(*wctx)); + if (wctx == NULL) { + os_free(ctx); + return -1; + } + wctx->ctx = e; + wctx->msg_ctx = wpa_s; + wctx->get_state = eapol_test_get_state; + wpa_s->wpa = wpa_sm_init(wctx); + if (!wpa_s->wpa) { + os_free(ctx); + os_free(wctx); + return -1; + } + + if (!ssid) + return 0; + wpa_s->current_ssid = ssid; os_memset(&eapol_conf, 0, sizeof(eapol_conf)); eapol_conf.accept_802_1x_keys = 1; @@ -614,6 +653,8 @@ static void test_eapol_clean(struct eapol_test_data *e, { struct extra_radius_attr *p, *prev; + wpa_sm_deinit(wpa_s->wpa); + wpa_s->wpa = NULL; radius_client_deinit(e->radius); wpabuf_free(e->last_eap_radius); radius_msg_free(e->last_recv_radius); @@ -757,6 +798,8 @@ static void ieee802_1x_decapsulate_radius(struct eapol_test_data *e) break; case EAP_CODE_FAILURE: os_strlcpy(buf, "EAP Failure", sizeof(buf)); + if (e->ctrl_iface) + break; eloop_terminate(); break; default: @@ -901,25 +944,66 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, if ((hdr->code == RADIUS_CODE_ACCESS_ACCEPT && e->eapol_test_num_reauths < 0) || hdr->code == RADIUS_CODE_ACCESS_REJECT) { - eloop_terminate(); + if (!e->ctrl_iface) + eloop_terminate(); } return RADIUS_RX_QUEUED; } +static int driver_get_ssid(void *priv, u8 *ssid) +{ + ssid[0] = 0; + return 0; +} + + +static int driver_get_bssid(void *priv, u8 *bssid) +{ + struct eapol_test_data *e = priv; + + if (e->ctrl_iface && !e->id_req_sent) { + eloop_register_timeout(0, 0, send_eap_request_identity, + e->wpa_s, NULL); + e->id_req_sent = 1; + } + + os_memset(bssid, 0, ETH_ALEN); + bssid[5] = 1; + return 0; +} + + +static int driver_get_capa(void *priv, struct wpa_driver_capa *capa) +{ + os_memset(capa, 0, sizeof(*capa)); + capa->flags = WPA_DRIVER_FLAGS_WIRED; + return 0; +} + + +struct wpa_driver_ops eapol_test_drv_ops = { + .name = "test", + .get_ssid = driver_get_ssid, + .get_bssid = driver_get_bssid, + .get_capa = driver_get_capa, +}; + static void wpa_init_conf(struct eapol_test_data *e, struct wpa_supplicant *wpa_s, const char *authsrv, int port, const char *secret, - const char *cli_addr) + const char *cli_addr, const char *ifname) { struct hostapd_radius_server *as; int res; + wpa_s->driver = &eapol_test_drv_ops; + wpa_s->drv_priv = e; wpa_s->bssid[5] = 1; os_memcpy(wpa_s->own_addr, e->own_addr, ETH_ALEN); e->own_ip_addr.s_addr = htonl((127 << 24) | 1); - os_strlcpy(wpa_s->ifname, "test", sizeof(wpa_s->ifname)); + os_strlcpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname)); e->radius_conf = os_zalloc(sizeof(struct hostapd_radius_servers)); assert(e->radius_conf != NULL); @@ -938,13 +1022,12 @@ static void wpa_init_conf(struct eapol_test_data *e, *pos++ = a[3]; } #else /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */ - if (inet_aton(authsrv, &as->addr.u.v4) < 0) { + if (hostapd_parse_ip_addr(authsrv, &as->addr) < 0) { wpa_printf(MSG_ERROR, "Invalid IP address '%s'", authsrv); assert(0); } #endif /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */ - as->addr.af = AF_INET; as->port = port; as->shared_secret = (u8 *) os_strdup(secret); as->shared_secret_len = os_strlen(secret); @@ -1162,7 +1245,7 @@ static void usage(void) " [-M<client MAC address>] [-o<server cert file] \\\n" " [-N<attr spec>] [-R<PC/SC reader>] " "[-P<PC/SC PIN>] \\\n" - " [-A<client IP>]\n" + " [-A<client IP>] [-i<ifname>] [-T<ctrl_iface>]\n" "eapol_test scard\n" "eapol_test sim <PIN> <num triplets> [debug]\n" "\n"); @@ -1217,6 +1300,8 @@ int main(int argc, char *argv[]) int timeout = 30; char *pos; struct extra_radius_attr *p = NULL, *p1; + const char *ifname = "test"; + const char *ctrl_iface = NULL; if (os_program_init()) return -1; @@ -1232,7 +1317,7 @@ int main(int argc, char *argv[]) wpa_debug_show_keys = 1; for (;;) { - c = getopt(argc, argv, "a:A:c:C:eM:nN:o:p:P:r:R:s:St:W"); + c = getopt(argc, argv, "a:A:c:C:ei:M:nN:o:p:P:r:R:s:St:T:W"); if (c < 0) break; switch (c) { @@ -1251,6 +1336,9 @@ int main(int argc, char *argv[]) case 'e': eapol_test.req_eap_key_name = 1; break; + case 'i': + ifname = optarg; + break; case 'M': if (hwaddr_aton(optarg, eapol_test.own_addr)) { usage(); @@ -1291,6 +1379,10 @@ int main(int argc, char *argv[]) case 't': timeout = atoi(optarg); break; + case 'T': + ctrl_iface = optarg; + eapol_test.ctrl_iface = 1; + break; case 'W': wait_for_monitor++; break; @@ -1337,7 +1429,7 @@ int main(int argc, char *argv[]) &argv[optind + 1]); } - if (conf == NULL) { + if (conf == NULL && !ctrl_iface) { usage(); printf("Configuration file is required.\n"); return -1; @@ -1359,12 +1451,15 @@ int main(int argc, char *argv[]) eapol_test.wpa_s = &wpa_s; dl_list_init(&wpa_s.bss); dl_list_init(&wpa_s.bss_id); - wpa_s.conf = wpa_config_read(conf, NULL); + if (conf) + wpa_s.conf = wpa_config_read(conf, NULL); + else + wpa_s.conf = wpa_config_alloc_empty(ctrl_iface, NULL); if (wpa_s.conf == NULL) { printf("Failed to parse configuration file '%s'.\n", conf); return -1; } - if (wpa_s.conf->ssid == NULL) { + if (!ctrl_iface && wpa_s.conf->ssid == NULL) { printf("No networks defined.\n"); return -1; } @@ -1375,7 +1470,7 @@ int main(int argc, char *argv[]) } wpa_init_conf(&eapol_test, &wpa_s, as_addr, as_port, as_secret, - cli_addr); + cli_addr, ifname); wpa_s.ctrl_iface = wpa_supplicant_ctrl_iface_init(&wpa_s); if (wpa_s.ctrl_iface == NULL) { printf("Failed to initialize control interface '%s'.\n" @@ -1388,7 +1483,8 @@ int main(int argc, char *argv[]) wpa_s.conf->ctrl_interface); return -1; } - if (wpa_supplicant_scard_init(&wpa_s, wpa_s.conf->ssid)) + if (wpa_s.conf->ssid && + wpa_supplicant_scard_init(&wpa_s, wpa_s.conf->ssid)) return -1; if (test_eapol(&eapol_test, &wpa_s, wpa_s.conf->ssid)) @@ -1400,9 +1496,12 @@ int main(int argc, char *argv[]) if (wait_for_monitor) wpa_supplicant_ctrl_iface_wait(wpa_s.ctrl_iface); - eloop_register_timeout(timeout, 0, eapol_test_timeout, &eapol_test, - NULL); - eloop_register_timeout(0, 0, send_eap_request_identity, &wpa_s, NULL); + if (!ctrl_iface) { + eloop_register_timeout(timeout, 0, eapol_test_timeout, + &eapol_test, NULL); + eloop_register_timeout(0, 0, send_eap_request_identity, &wpa_s, + NULL); + } eloop_register_signal_terminate(eapol_test_terminate, &wpa_s); eloop_register_signal_reconfig(eapol_test_terminate, &wpa_s); eloop_run(); diff --git a/contrib/wpa/wpa_supplicant/eapol_test.py b/contrib/wpa/wpa_supplicant/eapol_test.py new file mode 100755 index 0000000..80e7dfc --- /dev/null +++ b/contrib/wpa/wpa_supplicant/eapol_test.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python2 +# +# eapol_test controller +# Copyright (c) 2015, Jouni Malinen <j@w1.fi> +# +# This software may be distributed under the terms of the BSD license. +# See README for more details. + +import argparse +import logging +import os +import Queue +import sys +import threading + +logger = logging.getLogger() +dir = os.path.dirname(os.path.realpath(sys.modules[__name__].__file__)) +sys.path.append(os.path.join(dir, '..', 'wpaspy')) +import wpaspy +wpas_ctrl = '/tmp/eapol_test' + +class eapol_test: + def __init__(self, ifname): + self.ifname = ifname + self.ctrl = wpaspy.Ctrl(os.path.join(wpas_ctrl, ifname)) + if "PONG" not in self.ctrl.request("PING"): + raise Exception("Failed to connect to eapol_test (%s)" % ifname) + self.mon = wpaspy.Ctrl(os.path.join(wpas_ctrl, ifname)) + self.mon.attach() + + def add_network(self): + id = self.request("ADD_NETWORK") + if "FAIL" in id: + raise Exception("ADD_NETWORK failed") + return int(id) + + def remove_network(self, id): + id = self.request("REMOVE_NETWORK " + str(id)) + if "FAIL" in id: + raise Exception("REMOVE_NETWORK failed") + return None + + def set_network(self, id, field, value): + res = self.request("SET_NETWORK " + str(id) + " " + field + " " + value) + if "FAIL" in res: + raise Exception("SET_NETWORK failed") + return None + + def set_network_quoted(self, id, field, value): + res = self.request("SET_NETWORK " + str(id) + " " + field + ' "' + value + '"') + if "FAIL" in res: + raise Exception("SET_NETWORK failed") + return None + + def request(self, cmd, timeout=10): + return self.ctrl.request(cmd, timeout=timeout) + + def wait_event(self, events, timeout=10): + start = os.times()[4] + while True: + while self.mon.pending(): + ev = self.mon.recv() + logger.debug(self.ifname + ": " + ev) + for event in events: + if event in ev: + return ev + now = os.times()[4] + remaining = start + timeout - now + if remaining <= 0: + break + if not self.mon.pending(timeout=remaining): + break + return None + +def run(ifname, count, no_fast_reauth, res): + et = eapol_test(ifname) + + et.request("AP_SCAN 0") + if no_fast_reauth: + et.request("SET fast_reauth 0") + else: + et.request("SET fast_reauth 1") + id = et.add_network() + et.set_network(id, "key_mgmt", "IEEE8021X") + et.set_network(id, "eapol_flags", "0") + et.set_network(id, "eap", "TLS") + et.set_network_quoted(id, "identity", "user") + et.set_network_quoted(id, "ca_cert", 'ca.pem') + et.set_network_quoted(id, "client_cert", 'client.pem') + et.set_network_quoted(id, "private_key", 'client.key') + et.set_network_quoted(id, "private_key_passwd", 'whatever') + et.set_network(id, "disabled", "0") + + fail = False + for i in range(count): + et.request("REASSOCIATE") + ev = et.wait_event(["CTRL-EVENT-CONNECTED", "CTRL-EVENT-EAP-FAILURE"]) + if ev is None or "CTRL-EVENT-CONNECTED" not in ev: + fail = True + break + + et.remove_network(id) + + if fail: + res.put("FAIL (%d OK)" % i) + else: + res.put("PASS %d" % (i + 1)) + +def main(): + parser = argparse.ArgumentParser(description='eapol_test controller') + parser.add_argument('--ctrl', help='control interface directory') + parser.add_argument('--num', help='number of processes') + parser.add_argument('--iter', help='number of iterations') + parser.add_argument('--no-fast-reauth', action='store_true', + dest='no_fast_reauth', + help='disable TLS session resumption') + args = parser.parse_args() + + num = int(args.num) + iter = int(args.iter) + if args.ctrl: + global wpas_ctrl + wpas_ctrl = args.ctrl + + t = {} + res = {} + for i in range(num): + res[i] = Queue.Queue() + t[i] = threading.Thread(target=run, args=(str(i), iter, + args.no_fast_reauth, res[i])) + for i in range(num): + t[i].start() + for i in range(num): + t[i].join() + try: + results = res[i].get(False) + except: + results = "N/A" + print "%d: %s" % (i, results) + +if __name__ == "__main__": + main() diff --git a/contrib/wpa/wpa_supplicant/events.c b/contrib/wpa/wpa_supplicant/events.c index d275ca4..3af1c7d 100644 --- a/contrib/wpa/wpa_supplicant/events.c +++ b/contrib/wpa/wpa_supplicant/events.c @@ -23,6 +23,7 @@ #include "eap_peer/eap.h" #include "ap/hostapd.h" #include "p2p/p2p.h" +#include "fst/fst.h" #include "wnm_sta.h" #include "notify.h" #include "common/ieee802_11_defs.h" @@ -71,6 +72,59 @@ static int wpas_temp_disabled(struct wpa_supplicant *wpa_s, } +/** + * wpas_reenabled_network_time - Time until first network is re-enabled + * @wpa_s: Pointer to wpa_supplicant data + * Returns: If all enabled networks are temporarily disabled, returns the time + * (in sec) until the first network is re-enabled. Otherwise returns 0. + * + * This function is used in case all enabled networks are temporarily disabled, + * in which case it returns the time (in sec) that the first network will be + * re-enabled. The function assumes that at least one network is enabled. + */ +static int wpas_reenabled_network_time(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid; + int disabled_for, res = 0; + +#ifdef CONFIG_INTERWORKING + if (wpa_s->conf->auto_interworking && wpa_s->conf->interworking && + wpa_s->conf->cred) + return 0; +#endif /* CONFIG_INTERWORKING */ + + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if (ssid->disabled) + continue; + + disabled_for = wpas_temp_disabled(wpa_s, ssid); + if (!disabled_for) + return 0; + + if (!res || disabled_for < res) + res = disabled_for; + } + + return res; +} + + +void wpas_network_reenabled(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + + if (wpa_s->disconnected || wpa_s->wpa_state != WPA_SCANNING) + return; + + wpa_dbg(wpa_s, MSG_DEBUG, + "Try to associate due to network getting re-enabled"); + if (wpa_supplicant_fast_associate(wpa_s) != 1) { + wpa_supplicant_cancel_sched_scan(wpa_s); + wpa_supplicant_req_scan(wpa_s, 0, 0); + } +} + + static struct wpa_bss * wpa_supplicant_get_new_bss( struct wpa_supplicant *wpa_s, const u8 *bssid) { @@ -105,11 +159,32 @@ static void wpa_supplicant_update_current_bss(struct wpa_supplicant *wpa_s) static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s) { struct wpa_ssid *ssid, *old_ssid; + u8 drv_ssid[SSID_MAX_LEN]; + size_t drv_ssid_len; int res; if (wpa_s->conf->ap_scan == 1 && wpa_s->current_ssid) { wpa_supplicant_update_current_bss(wpa_s); - return 0; + + if (wpa_s->current_ssid->ssid_len == 0) + return 0; /* current profile still in use */ + res = wpa_drv_get_ssid(wpa_s, drv_ssid); + if (res < 0) { + wpa_msg(wpa_s, MSG_INFO, + "Failed to read SSID from driver"); + return 0; /* try to use current profile */ + } + drv_ssid_len = res; + + if (drv_ssid_len == wpa_s->current_ssid->ssid_len && + os_memcmp(drv_ssid, wpa_s->current_ssid->ssid, + drv_ssid_len) == 0) + return 0; /* current profile still in use */ + + wpa_msg(wpa_s, MSG_DEBUG, + "Driver-initiated BSS selection changed the SSID to %s", + wpa_ssid_txt(drv_ssid, drv_ssid_len)); + /* continue selecting a new network profile */ } wpa_dbg(wpa_s, MSG_DEBUG, "Select network based on association " @@ -212,9 +287,6 @@ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s) os_memset(wpa_s->bssid, 0, ETH_ALEN); os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); sme_clear_on_disassoc(wpa_s); -#ifdef CONFIG_P2P - os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN); -#endif /* CONFIG_P2P */ wpa_s->current_bss = NULL; wpa_s->assoc_freq = 0; @@ -756,9 +828,9 @@ static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, osen = ie != NULL; wpa_dbg(wpa_s, MSG_DEBUG, "%d: " MACSTR " ssid='%s' " - "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d%s%s%s", + "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d freq=%d %s%s%s", i, MAC2STR(bss->bssid), wpa_ssid_txt(bss->ssid, bss->ssid_len), - wpa_ie_len, rsn_ie_len, bss->caps, bss->level, + wpa_ie_len, rsn_ie_len, bss->caps, bss->level, bss->freq, wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE) ? " wps" : "", (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) || wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) ? @@ -964,6 +1036,19 @@ static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, */ #endif /* CONFIG_P2P */ + if (os_reltime_before(&bss->last_update, &wpa_s->scan_min_time)) + { + struct os_reltime diff; + + os_reltime_sub(&wpa_s->scan_min_time, + &bss->last_update, &diff); + wpa_dbg(wpa_s, MSG_DEBUG, + " skip - scan result not recent enough (%u.%06u seconds too old)", + (unsigned int) diff.sec, + (unsigned int) diff.usec); + continue; + } + /* Matching configuration found */ return ssid; } @@ -1011,14 +1096,13 @@ struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s, struct wpa_bss *selected = NULL; int prio; struct wpa_ssid *next_ssid = NULL; + struct wpa_ssid *ssid; if (wpa_s->last_scan_res == NULL || wpa_s->last_scan_res_used == 0) return NULL; /* no scan results from last update */ if (wpa_s->next_ssid) { - struct wpa_ssid *ssid; - /* check that next_ssid is still valid */ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { if (ssid == wpa_s->next_ssid) @@ -1054,6 +1138,27 @@ struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s, break; } + ssid = *selected_ssid; + if (selected && ssid && ssid->mem_only_psk && !ssid->psk_set && + !ssid->passphrase && !ssid->ext_psk) { + const char *field_name, *txt = NULL; + + wpa_dbg(wpa_s, MSG_DEBUG, + "PSK/passphrase not yet available for the selected network"); + + wpas_notify_network_request(wpa_s, ssid, + WPA_CTRL_REQ_PSK_PASSPHRASE, NULL); + + field_name = wpa_supplicant_ctrl_req_to_string( + WPA_CTRL_REQ_PSK_PASSPHRASE, NULL, &txt); + if (field_name == NULL) + return NULL; + + wpas_send_ctrl_req(wpa_s, ssid, field_name, txt); + + selected = NULL; + } + return selected; } @@ -1085,6 +1190,7 @@ int wpa_supplicant_connect(struct wpa_supplicant *wpa_s, if (wpas_wps_scan_pbc_overlap(wpa_s, selected, ssid)) { wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_OVERLAP "PBC session overlap"); + wpas_notify_wps_event_pbc_overlap(wpa_s); #ifdef CONFIG_P2P if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT || wpa_s->p2p_in_provisioning) { @@ -1095,6 +1201,7 @@ int wpa_supplicant_connect(struct wpa_supplicant *wpa_s, #endif /* CONFIG_P2P */ #ifdef CONFIG_WPS + wpas_wps_pbc_overlap(wpa_s); wpas_wps_cancel(wpa_s); #endif /* CONFIG_WPS */ return -1; @@ -1192,7 +1299,9 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { struct wpa_bss *current_bss = NULL; +#ifndef CONFIG_NO_ROAMING int min_diff; +#endif /* CONFIG_NO_ROAMING */ if (wpa_s->reassociate) return 1; /* explicit request to reassociate */ @@ -1421,6 +1530,17 @@ static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s, { struct wpa_bss *selected; struct wpa_ssid *ssid = NULL; + int time_to_reenable = wpas_reenabled_network_time(wpa_s); + + if (time_to_reenable > 0) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Postpone network selection by %d seconds since all networks are disabled", + time_to_reenable); + eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL); + eloop_register_timeout(time_to_reenable, 0, + wpas_network_reenabled, wpa_s, NULL); + return 0; + } if (wpa_s->p2p_mgmt) return 0; /* no normal connection on p2p_mgmt interface */ @@ -1520,6 +1640,9 @@ static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s, if (wpa_supplicant_req_sched_scan(wpa_s)) wpa_supplicant_req_new_scan(wpa_s, timeout_sec, timeout_usec); + + wpa_msg_ctrl(wpa_s, MSG_INFO, + WPA_EVENT_NETWORK_NOT_FOUND); } } return 0; @@ -1577,7 +1700,7 @@ int wpa_supplicant_fast_associate(struct wpa_supplicant *wpa_s) #else /* CONFIG_NO_SCAN_PROCESSING */ struct os_reltime now; - if (wpa_s->last_scan_res_used <= 0) + if (wpa_s->last_scan_res_used == 0) return -1; os_get_reltime(&now); @@ -1889,6 +2012,19 @@ 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 " @@ -1932,6 +2068,7 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, { u8 bssid[ETH_ALEN]; int ft_completed; + int new_bss = 0; #ifdef CONFIG_AP if (wpa_s->ap_iface) { @@ -1946,6 +2083,8 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_AP */ + eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL); + ft_completed = wpa_ft_is_completed(wpa_s->wpa); if (data && wpa_supplicant_event_associnfo(wpa_s, data) < 0) return; @@ -1961,6 +2100,7 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, if (os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0) { wpa_dbg(wpa_s, MSG_DEBUG, "Associated to a new BSS: BSSID=" MACSTR, MAC2STR(bssid)); + new_bss = 1; random_add_randomness(bssid, ETH_ALEN); os_memcpy(wpa_s->bssid, bssid, ETH_ALEN); os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); @@ -1974,13 +2114,13 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, wpa_s, WLAN_REASON_DEAUTH_LEAVING); return; } + } - if (wpa_s->conf->ap_scan == 1 && - wpa_s->drv_flags & WPA_DRIVER_FLAGS_BSS_SELECTION) { - if (wpa_supplicant_assoc_update_ie(wpa_s) < 0) - wpa_msg(wpa_s, MSG_WARNING, - "WPA/RSN IEs not updated"); - } + if (wpa_s->conf->ap_scan == 1 && + wpa_s->drv_flags & WPA_DRIVER_FLAGS_BSS_SELECTION) { + if (wpa_supplicant_assoc_update_ie(wpa_s) < 0 && new_bss) + wpa_msg(wpa_s, MSG_WARNING, + "WPA/RSN IEs not updated"); } #ifdef CONFIG_SME @@ -2253,7 +2393,8 @@ static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s, "try to re-connect"); wpa_s->reassociate = 0; wpa_s->disconnected = 1; - wpa_supplicant_cancel_sched_scan(wpa_s); + if (!wpa_s->pno) + wpa_supplicant_cancel_sched_scan(wpa_s); } bssid = wpa_s->bssid; if (is_zero_ether_addr(bssid)) @@ -2443,6 +2584,21 @@ wpa_supplicant_event_interface_status(struct wpa_supplicant *wpa_s, wpa_msg(wpa_s, MSG_INFO, "Failed to initialize the " "driver after interface was added"); } + +#ifdef CONFIG_P2P + if (!wpa_s->global->p2p && + !wpa_s->global->p2p_disabled && + !wpa_s->conf->p2p_disabled && + (wpa_s->drv_flags & + WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) && + wpas_p2p_add_p2pdev_interface( + wpa_s, wpa_s->global->params.conf_p2p_dev) < 0) { + wpa_printf(MSG_INFO, + "P2P: Failed to enable P2P Device interface"); + /* Try to continue without. P2P will be disabled. */ + } +#endif /* CONFIG_P2P */ + break; case EVENT_INTERFACE_REMOVED: wpa_dbg(wpa_s, MSG_DEBUG, "Configured interface was removed"); @@ -2451,6 +2607,21 @@ wpa_supplicant_event_interface_status(struct wpa_supplicant *wpa_s, wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED); l2_packet_deinit(wpa_s->l2); wpa_s->l2 = NULL; + +#ifdef CONFIG_P2P + if (wpa_s->global->p2p && + wpa_s->global->p2p_init_wpa_s->parent == wpa_s && + (wpa_s->drv_flags & + WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Removing P2P Device interface"); + wpa_supplicant_remove_iface( + wpa_s->global, wpa_s->global->p2p_init_wpa_s, + 0); + wpa_s->global->p2p_init_wpa_s = NULL; + } +#endif /* CONFIG_P2P */ + #ifdef CONFIG_TERMINATE_ONLASTIF /* check if last interface */ if (!any_interfaces(wpa_s->global->ifaces)) @@ -2844,26 +3015,24 @@ static void wpa_supplicant_update_channel_list( if (wpa_s->drv_priv == NULL) return; /* Ignore event during drv initialization */ - free_hw_features(wpa_s); - wpa_s->hw.modes = wpa_drv_get_hw_feature_data( - wpa_s, &wpa_s->hw.num_modes, &wpa_s->hw.flags); - - wpas_p2p_update_channel_list(wpa_s); - - /* - * Check other interfaces to see if they share the same radio. If - * so, they get updated with this same hw mode info. - */ dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant, radio_list) { - if (ifs != wpa_s) { - wpa_printf(MSG_DEBUG, "%s: Updating hw mode", - ifs->ifname); - free_hw_features(ifs); - ifs->hw.modes = wpa_drv_get_hw_feature_data( - ifs, &ifs->hw.num_modes, &ifs->hw.flags); - } + wpa_printf(MSG_DEBUG, "%s: Updating hw mode", + ifs->ifname); + 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); } + + wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_DRIVER); } @@ -2964,6 +3133,13 @@ static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s, return; } +#ifdef CONFIG_FST + if (mgmt->u.action.category == WLAN_ACTION_FST && wpa_s->fst) { + fst_rx_action(wpa_s->fst, mgmt, len); + return; + } +#endif /* CONFIG_FST */ + wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid, category, payload, plen, freq); if (wpa_s->ifmsh) @@ -2974,9 +3150,6 @@ static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s, static void wpa_supplicant_notify_avoid_freq(struct wpa_supplicant *wpa_s, union wpa_event_data *event) { -#ifdef CONFIG_P2P - struct wpa_supplicant *ifs; -#endif /* CONFIG_P2P */ struct wpa_freq_range_list *list; char *str = NULL; @@ -2993,29 +3166,13 @@ static void wpa_supplicant_notify_avoid_freq(struct wpa_supplicant *wpa_s, __func__); } else { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Update channel list based on frequency avoid event"); - wpas_p2p_update_channel_list(wpa_s); - } - - for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) { - int freq; - if (!ifs->current_ssid || - !ifs->current_ssid->p2p_group || - (ifs->current_ssid->mode != WPAS_MODE_P2P_GO && - ifs->current_ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION)) - continue; - freq = ifs->current_ssid->frequency; - if (!freq_range_list_includes(list, freq)) { - wpa_dbg(ifs, MSG_DEBUG, "P2P GO operating frequency %d MHz in safe range", - freq); - continue; - } - - wpa_dbg(ifs, MSG_DEBUG, "P2P GO operating in unsafe frequency %d MHz", - freq); - /* TODO: Consider using CSA or removing the group within - * wpa_supplicant */ - wpa_msg(ifs, MSG_INFO, P2P_EVENT_REMOVE_AND_REFORM_GROUP); + /* + * The update channel flow will also take care of moving a GO + * from the unsafe frequency if needed. + */ + wpas_p2p_update_channel_list(wpa_s, + WPAS_P2P_CHANNEL_UPDATE_AVOID); } #endif /* CONFIG_P2P */ @@ -3047,6 +3204,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, union wpa_event_data *data) { struct wpa_supplicant *wpa_s = ctx; + int resched; if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED && event != EVENT_INTERFACE_ENABLED && @@ -3372,6 +3530,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, wpas_p2p_probe_req_rx( wpa_s, src, mgmt->da, mgmt->bssid, ie, ie_len, + data->rx_mgmt.freq, data->rx_mgmt.ssi_signal); break; } @@ -3443,6 +3602,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, data->rx_probe_req.bssid, data->rx_probe_req.ie, data->rx_probe_req.ie_len, + 0, data->rx_probe_req.ssi_signal); break; case EVENT_REMAIN_ON_CHANNEL: @@ -3613,6 +3773,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, case EVENT_SCHED_SCAN_STOPPED: wpa_s->pno = 0; wpa_s->sched_scanning = 0; + resched = wpa_s->scanning; wpa_supplicant_notify_scanning(wpa_s, 0); if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) @@ -3627,6 +3788,8 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, } else if (wpa_s->pno_sched_pending) { wpa_s->pno_sched_pending = 0; wpas_start_pno(wpa_s); + } else if (resched) { + wpa_supplicant_req_scan(wpa_s, 0, 0); } break; diff --git a/contrib/wpa/wpa_supplicant/hs20_supplicant.c b/contrib/wpa/wpa_supplicant/hs20_supplicant.c index b9cd681..a1afc85 100644 --- a/contrib/wpa/wpa_supplicant/hs20_supplicant.c +++ b/contrib/wpa/wpa_supplicant/hs20_supplicant.c @@ -46,7 +46,7 @@ struct osu_icon { struct osu_provider { u8 bssid[ETH_ALEN]; - u8 osu_ssid[32]; + u8 osu_ssid[SSID_MAX_LEN]; u8 osu_ssid_len; char server_uri[256]; u32 osu_methods; /* bit 0 = OMA-DM, bit 1 = SOAP-XML SPP */ @@ -188,14 +188,16 @@ int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes, struct wpa_bss *bss; int res; - freq = wpa_s->assoc_freq; bss = wpa_bss_get_bssid(wpa_s, dst); - if (bss) { - wpa_bss_anqp_unshare_alloc(bss); - freq = bss->freq; - } - if (freq <= 0) + if (!bss) { + wpa_printf(MSG_WARNING, + "ANQP: Cannot send query to unknown BSS " + MACSTR, MAC2STR(dst)); return -1; + } + + wpa_bss_anqp_unshare_alloc(bss); + freq = bss->freq; wpa_printf(MSG_DEBUG, "HS20: ANQP Query Request to " MACSTR " for " "subtypes 0x%x", MAC2STR(dst), stypes); @@ -822,7 +824,7 @@ void hs20_osu_icon_fetch(struct wpa_supplicant *wpa_s) continue; } osu_ssid_len = *pos++; - if (osu_ssid_len > 32) { + if (osu_ssid_len > SSID_MAX_LEN) { wpa_printf(MSG_DEBUG, "HS 2.0: Invalid OSU SSID " "Length %u", osu_ssid_len); continue; diff --git a/contrib/wpa/wpa_supplicant/ibss_rsn.c b/contrib/wpa/wpa_supplicant/ibss_rsn.c index d0ae135..d9d0ae7 100644 --- a/contrib/wpa/wpa_supplicant/ibss_rsn.c +++ b/contrib/wpa/wpa_supplicant/ibss_rsn.c @@ -571,6 +571,9 @@ int ibss_rsn_start(struct ibss_rsn *ibss_rsn, const u8 *addr) struct ibss_rsn_peer *peer; int res; + if (!ibss_rsn) + return -1; + /* if the peer already exists, exit immediately */ peer = ibss_rsn_get_peer(ibss_rsn, addr); if (peer) @@ -694,7 +697,8 @@ void ibss_rsn_deinit(struct ibss_rsn *ibss_rsn) ibss_rsn_free(prev); } - wpa_deinit(ibss_rsn->auth_group); + if (ibss_rsn->auth_group) + wpa_deinit(ibss_rsn->auth_group); os_free(ibss_rsn); } diff --git a/contrib/wpa/wpa_supplicant/interworking.c b/contrib/wpa/wpa_supplicant/interworking.c index 4a39665..fd47c179 100644 --- a/contrib/wpa/wpa_supplicant/interworking.c +++ b/contrib/wpa/wpa_supplicant/interworking.c @@ -2058,7 +2058,7 @@ static struct wpa_cred * interworking_credentials_available_helper( int *excluded) { struct wpa_cred *cred, *cred2; - int excluded1, excluded2; + int excluded1, excluded2 = 0; if (disallowed_bssid(wpa_s, bss->bssid) || disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) { @@ -2673,14 +2673,16 @@ int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, struct wpa_bss *bss; int res; - freq = wpa_s->assoc_freq; bss = wpa_bss_get_bssid(wpa_s, dst); - if (bss) { - wpa_bss_anqp_unshare_alloc(bss); - freq = bss->freq; - } - if (freq <= 0) + if (!bss) { + wpa_printf(MSG_WARNING, + "ANQP: Cannot send query to unknown BSS " + MACSTR, MAC2STR(dst)); return -1; + } + + wpa_bss_anqp_unshare_alloc(bss); + freq = bss->freq; wpa_msg(wpa_s, MSG_DEBUG, "ANQP: Query Request to " MACSTR " for %u id(s)", diff --git a/contrib/wpa/wpa_supplicant/main.c b/contrib/wpa/wpa_supplicant/main.c index 2282747..d5d47e1 100644 --- a/contrib/wpa/wpa_supplicant/main.c +++ b/contrib/wpa/wpa_supplicant/main.c @@ -12,6 +12,7 @@ #endif /* __linux__ */ #include "common.h" +#include "fst/fst.h" #include "wpa_supplicant_i.h" #include "driver_i.h" #include "p2p_supplicant.h" @@ -237,7 +238,7 @@ int main(int argc, char *argv[]) goto out; #ifdef CONFIG_P2P case 'm': - iface->conf_p2p_dev = optarg; + params.conf_p2p_dev = optarg; break; #endif /* CONFIG_P2P */ case 'o': @@ -288,7 +289,7 @@ int main(int argc, char *argv[]) if (iface == NULL) goto out; ifaces = iface; - iface = &ifaces[iface_count - 1]; + iface = &ifaces[iface_count - 1]; os_memset(iface, 0, sizeof(*iface)); break; default: @@ -309,6 +310,17 @@ int main(int argc, char *argv[]) "wpa_supplicant"); } + if (fst_global_init()) { + wpa_printf(MSG_ERROR, "Failed to initialize FST"); + exitcode = -1; + goto out; + } + +#if defined(CONFIG_FST) && defined(CONFIG_CTRL_IFACE) + if (!fst_global_add_ctrl(fst_ctrl_cli)) + wpa_printf(MSG_WARNING, "Failed to add CLI FST ctrl"); +#endif + for (i = 0; exitcode == 0 && i < iface_count; i++) { struct wpa_supplicant *wpa_s; @@ -334,6 +346,8 @@ int main(int argc, char *argv[]) wpa_supplicant_deinit(global); + fst_global_deinit(); + out: wpa_supplicant_fd_workaround(0); os_free(ifaces); diff --git a/contrib/wpa/wpa_supplicant/mesh.c b/contrib/wpa/wpa_supplicant/mesh.c index 33b4af3..77f708b 100644 --- a/contrib/wpa/wpa_supplicant/mesh.c +++ b/contrib/wpa/wpa_supplicant/mesh.c @@ -47,8 +47,8 @@ void wpa_supplicant_mesh_iface_deinit(struct wpa_supplicant *wpa_s, if (ifmsh->mconf) { mesh_mpm_deinit(wpa_s, ifmsh); - if (ifmsh->mconf->ies) { - ifmsh->mconf->ies = NULL; + if (ifmsh->mconf->rsn_ie) { + ifmsh->mconf->rsn_ie = NULL; /* We cannot free this struct * because wpa_authenticator on * hostapd side is also using it @@ -171,6 +171,8 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s, ifmsh->conf = conf; ifmsh->bss[0]->max_plinks = wpa_s->conf->max_peer_links; + ifmsh->bss[0]->dot11RSNASAERetransPeriod = + wpa_s->conf->dot11RSNASAERetransPeriod; os_strlcpy(bss->conf->iface, wpa_s->ifname, sizeof(bss->conf->iface)); mconf = mesh_config_create(ssid); @@ -350,8 +352,8 @@ int wpa_supplicant_join_mesh(struct wpa_supplicant *wpa_s, } if (wpa_s->ifmsh) { - params.ies = wpa_s->ifmsh->mconf->ies; - params.ie_len = wpa_s->ifmsh->mconf->ie_len; + 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; } @@ -453,22 +455,23 @@ static int mesh_attr_text(const u8 *ies, size_t ies_len, char *buf, char *end) ret = os_snprintf(pos, end - pos, "bss_basic_rate_set=%d", bss_basic_rate_set[0]); if (os_snprintf_error(end - pos, ret)) - return pos - buf; + goto fail; pos += ret; for (i = 1; i < bss_basic_rate_set_len; i++) { ret = os_snprintf(pos, end - pos, " %d", bss_basic_rate_set[i]); if (os_snprintf_error(end - pos, ret)) - return pos - buf; + goto fail; pos += ret; } ret = os_snprintf(pos, end - pos, "\n"); if (os_snprintf_error(end - pos, ret)) - return pos - buf; + goto fail; pos += ret; } +fail: os_free(bss_basic_rate_set); return pos - buf; diff --git a/contrib/wpa/wpa_supplicant/mesh_mpm.c b/contrib/wpa/wpa_supplicant/mesh_mpm.c index 1d6f2be..f81b88c8 100644 --- a/contrib/wpa/wpa_supplicant/mesh_mpm.c +++ b/contrib/wpa/wpa_supplicant/mesh_mpm.c @@ -239,6 +239,9 @@ static void mesh_mpm_send_plink_action(struct wpa_supplicant *wpa_s, 2 + 22; /* HT operation */ } #endif /* CONFIG_IEEE80211N */ + if (type != PLINK_CLOSE) + buf_len += conf->rsn_ie_len; /* RSN IE */ + buf = wpabuf_alloc(buf_len); if (!buf) return; @@ -262,6 +265,9 @@ static void mesh_mpm_send_plink_action(struct wpa_supplicant *wpa_s, pos = hostapd_eid_ext_supp_rates(bss, pos); wpabuf_put_data(buf, supp_rates, pos - supp_rates); + /* IE: RSN IE */ + wpabuf_put_data(buf, conf->rsn_ie, conf->rsn_ie_len); + /* IE: Mesh ID */ wpabuf_put_u8(buf, WLAN_EID_MESH_ID); wpabuf_put_u8(buf, conf->meshid_len); @@ -551,8 +557,7 @@ static struct sta_info * mesh_mpm_add_peer(struct wpa_supplicant *wpa_s, mesh_mpm_init_link(wpa_s, sta); #ifdef CONFIG_IEEE80211N - copy_sta_ht_capab(data, sta, elems->ht_capabilities, - elems->ht_capabilities_len); + copy_sta_ht_capab(data, sta, elems->ht_capabilities); update_ht_state(data, sta); #endif /* CONFIG_IEEE80211N */ diff --git a/contrib/wpa/wpa_supplicant/mesh_rsn.c b/contrib/wpa/wpa_supplicant/mesh_rsn.c index 936002d..747f1ae 100644 --- a/contrib/wpa/wpa_supplicant/mesh_rsn.c +++ b/contrib/wpa/wpa_supplicant/mesh_rsn.c @@ -190,7 +190,8 @@ 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)); - wpa_deinit(rsn->auth); + if (rsn->auth) + wpa_deinit(rsn->auth); } @@ -209,14 +210,15 @@ struct mesh_rsn *mesh_rsn_auth_init(struct wpa_supplicant *wpa_s, if (__mesh_rsn_auth_init(mesh_rsn, wpa_s->own_addr) < 0) { mesh_rsn_deinit(mesh_rsn); + os_free(mesh_rsn); return NULL; } bss->wpa_auth = mesh_rsn->auth; ie = wpa_auth_get_wpa_ie(mesh_rsn->auth, &ie_len); - conf->ies = (u8 *) ie; - conf->ie_len = ie_len; + conf->rsn_ie = (u8 *) ie; + conf->rsn_ie_len = ie_len; wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid); diff --git a/contrib/wpa/wpa_supplicant/notify.c b/contrib/wpa/wpa_supplicant/notify.c index ea7dbdb1..45d06bf 100644 --- a/contrib/wpa/wpa_supplicant/notify.c +++ b/contrib/wpa/wpa_supplicant/notify.c @@ -17,6 +17,7 @@ #include "dbus/dbus_old.h" #include "dbus/dbus_new.h" #include "rsn_supp/wpa.h" +#include "fst/fst.h" #include "driver_i.h" #include "scan.h" #include "p2p_supplicant.h" @@ -88,6 +89,16 @@ void wpas_notify_state_changed(struct wpa_supplicant *wpa_s, /* notify the new DBus API */ wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_STATE); +#ifdef CONFIG_FST + if (wpa_s->fst && !is_zero_ether_addr(wpa_s->bssid)) { + if (new_state == WPA_COMPLETED) + fst_notify_peer_connected(wpa_s->fst, wpa_s->bssid); + else if (old_state >= WPA_ASSOCIATED && + new_state < WPA_ASSOCIATED) + fst_notify_peer_disconnected(wpa_s->fst, wpa_s->bssid); + } +#endif /* CONFIG_FST */ + if (new_state == WPA_COMPLETED) wpas_p2p_notif_connected(wpa_s); else if (old_state >= WPA_ASSOCIATED && new_state < WPA_ASSOCIATED) @@ -268,6 +279,16 @@ void wpas_notify_wps_event_success(struct wpa_supplicant *wpa_s) #endif /* CONFIG_WPS */ } +void wpas_notify_wps_event_pbc_overlap(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->p2p_mgmt) + return; + +#ifdef CONFIG_WPS + wpas_dbus_signal_wps_event_pbc_overlap(wpa_s); +#endif /* CONFIG_WPS */ +} + void wpas_notify_network_added(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) @@ -307,14 +328,12 @@ void wpas_notify_persistent_group_removed(struct wpa_supplicant *wpa_s, void wpas_notify_network_removed(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { - if (wpa_s->p2p_mgmt) - return; - if (wpa_s->next_ssid == ssid) wpa_s->next_ssid = NULL; if (wpa_s->wpa) wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid); - if (!ssid->p2p_group && wpa_s->global->p2p_group_formation != wpa_s) + if (!ssid->p2p_group && wpa_s->global->p2p_group_formation != wpa_s && + !wpa_s->p2p_mgmt) wpas_dbus_unregister_network(wpa_s, ssid->id); if (network_is_persistent_group(ssid)) wpas_notify_persistent_group_removed(wpa_s, ssid); @@ -522,6 +541,13 @@ void wpas_notify_resume(struct wpa_global *global) #ifdef CONFIG_P2P +void wpas_notify_p2p_find_stopped(struct wpa_supplicant *wpa_s) +{ + /* Notify P2P find has stopped */ + wpas_dbus_signal_p2p_find_stopped(wpa_s); +} + + void wpas_notify_p2p_device_found(struct wpa_supplicant *wpa_s, const u8 *dev_addr, int new_device) { @@ -556,9 +582,9 @@ void wpas_notify_p2p_group_removed(struct wpa_supplicant *wpa_s, void wpas_notify_p2p_go_neg_req(struct wpa_supplicant *wpa_s, - const u8 *src, u16 dev_passwd_id) + const u8 *src, u16 dev_passwd_id, u8 go_intent) { - wpas_dbus_signal_p2p_go_neg_req(wpa_s, src, dev_passwd_id); + wpas_dbus_signal_p2p_go_neg_req(wpa_s, src, dev_passwd_id, go_intent); } @@ -631,12 +657,30 @@ void wpas_notify_p2p_group_started(struct wpa_supplicant *wpa_s, } +void wpas_notify_p2p_group_formation_failure(struct wpa_supplicant *wpa_s, + const char *reason) +{ + /* Notify a group formation failed */ + wpas_dbus_signal_p2p_group_formation_failure(wpa_s, reason); +} + + void wpas_notify_p2p_wps_failed(struct wpa_supplicant *wpa_s, struct wps_event_fail *fail) { wpas_dbus_signal_p2p_wps_failed(wpa_s, fail); } + +void wpas_notify_p2p_invitation_received(struct wpa_supplicant *wpa_s, + const u8 *sa, const u8 *go_dev_addr, + const u8 *bssid, int id, int op_freq) +{ + /* Notify a P2P Invitation Request */ + wpas_dbus_signal_p2p_invitation_received(wpa_s, sa, go_dev_addr, bssid, + id, op_freq); +} + #endif /* CONFIG_P2P */ @@ -775,10 +819,12 @@ void wpas_notify_network_type_changed(struct wpa_supplicant *wpa_s, ssid->disabled = 0; wpas_dbus_unregister_network(wpa_s, ssid->id); ssid->disabled = 2; + ssid->p2p_persistent_group = 1; wpas_dbus_register_persistent_group(wpa_s, ssid); } else { /* Changed from persistent group to normal network profile */ wpas_dbus_unregister_persistent_group(wpa_s, ssid->id); + ssid->p2p_persistent_group = 0; wpas_dbus_register_network(wpa_s, ssid); } #endif /* CONFIG_P2P */ diff --git a/contrib/wpa/wpa_supplicant/notify.h b/contrib/wpa/wpa_supplicant/notify.h index b268332..d9f0f5a 100644 --- a/contrib/wpa/wpa_supplicant/notify.h +++ b/contrib/wpa/wpa_supplicant/notify.h @@ -45,6 +45,7 @@ void wpas_notify_wps_event_m2d(struct wpa_supplicant *wpa_s, void wpas_notify_wps_event_fail(struct wpa_supplicant *wpa_s, struct wps_event_fail *fail); void wpas_notify_wps_event_success(struct wpa_supplicant *wpa_s); +void wpas_notify_wps_event_pbc_overlap(struct wpa_supplicant *wpa_s); void wpas_notify_network_added(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); void wpas_notify_network_removed(struct wpa_supplicant *wpa_s, @@ -84,6 +85,7 @@ void wpas_notify_resume(struct wpa_global *global); void wpas_notify_sta_authorized(struct wpa_supplicant *wpa_s, const u8 *mac_addr, int authorized, const u8 *p2p_dev_addr); +void wpas_notify_p2p_find_stopped(struct wpa_supplicant *wpa_s); void wpas_notify_p2p_device_found(struct wpa_supplicant *wpa_s, const u8 *dev_addr, int new_device); void wpas_notify_p2p_device_lost(struct wpa_supplicant *wpa_s, @@ -92,7 +94,7 @@ void wpas_notify_p2p_group_removed(struct wpa_supplicant *wpa_s, const struct wpa_ssid *ssid, const char *role); void wpas_notify_p2p_go_neg_req(struct wpa_supplicant *wpa_s, - const u8 *src, u16 dev_passwd_id); + const u8 *src, u16 dev_passwd_id, u8 go_intent); void wpas_notify_p2p_go_neg_completed(struct wpa_supplicant *wpa_s, struct p2p_go_neg_results *res); void wpas_notify_p2p_invitation_result(struct wpa_supplicant *wpa_s, @@ -112,6 +114,8 @@ 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, int client); +void wpas_notify_p2p_group_formation_failure(struct wpa_supplicant *wpa_s, + const char *reason); void wpas_notify_persistent_group_added(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); void wpas_notify_persistent_group_removed(struct wpa_supplicant *wpa_s, @@ -133,5 +137,8 @@ void wpas_notify_network_bssid_set_changed(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); void wpas_notify_network_type_changed(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); +void wpas_notify_p2p_invitation_received(struct wpa_supplicant *wpa_s, + const u8 *sa, const u8 *go_dev_addr, + const u8 *bssid, int id, int op_freq); #endif /* NOTIFY_H */ diff --git a/contrib/wpa/wpa_supplicant/p2p_supplicant.c b/contrib/wpa/wpa_supplicant/p2p_supplicant.c index b200ca0..78bdd08 100644 --- a/contrib/wpa/wpa_supplicant/p2p_supplicant.c +++ b/contrib/wpa/wpa_supplicant/p2p_supplicant.c @@ -47,6 +47,12 @@ #define P2P_AUTO_PD_SCAN_ATTEMPTS 5 +/** + * Defines time interval in seconds when a GO needs to evacuate a frequency that + * it is currently using, but is no longer valid for P2P use cases. + */ +#define P2P_GO_FREQ_CHANGE_TIME 5 + #ifndef P2P_MAX_CLIENT_IDLE /* * How many seconds to try to reconnect to the GO when connection in P2P client @@ -85,6 +91,12 @@ #define P2P_MGMT_DEVICE_PREFIX "p2p-dev-" +/* + * How many seconds to wait to re-attempt to move GOs, in case previous attempt + * was not possible. + */ +#define P2P_RECONSIDER_GO_MOVE_DELAY 30 + enum p2p_group_removal_reason { P2P_GROUP_REMOVAL_UNKNOWN, P2P_GROUP_REMOVAL_SILENT, @@ -94,7 +106,8 @@ enum p2p_group_removal_reason { P2P_GROUP_REMOVAL_UNAVAILABLE, P2P_GROUP_REMOVAL_GO_ENDING_SESSION, P2P_GROUP_REMOVAL_PSK_FAILURE, - P2P_GROUP_REMOVAL_FREQ_CONFLICT + P2P_GROUP_REMOVAL_FREQ_CONFLICT, + P2P_GROUP_REMOVAL_GO_LEAVE_CHANNEL }; @@ -126,6 +139,18 @@ static void wpas_p2p_psk_failure_removal(void *eloop_ctx, void *timeout_ctx); static void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s); static int wpas_p2p_add_group_interface(struct wpa_supplicant *wpa_s, enum wpa_driver_if_type type); +static void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s, + int already_deleted); +static void wpas_p2p_optimize_listen_channel(struct wpa_supplicant *wpa_s, + struct wpa_used_freq_data *freqs, + unsigned int num); +static void wpas_p2p_move_go(void *eloop_ctx, void *timeout_ctx); +static int wpas_p2p_go_is_peer_freq(struct wpa_supplicant *wpa_s, int freq); +static void +wpas_p2p_consider_moving_gos(struct wpa_supplicant *wpa_s, + struct wpa_used_freq_data *freqs, unsigned int num, + enum wpas_p2p_channel_update_trig trig); +static void wpas_p2p_reconsider_moving_go(void *eloop_ctx, void *timeout_ctx); /* @@ -191,7 +216,11 @@ static void wpas_p2p_set_own_freq_preference(struct wpa_supplicant *wpa_s, { if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return; - if (wpa_s->parent->conf->p2p_ignore_shared_freq && + + /* Use the wpa_s used to control the P2P Device operation */ + wpa_s = wpa_s->global->p2p_init_wpa_s; + + if (wpa_s->conf->p2p_ignore_shared_freq && freq > 0 && wpa_s->num_multichan_concurrent > 1 && wpas_p2p_num_unused_channels(wpa_s) > 0) { wpa_printf(MSG_DEBUG, "P2P: Ignore own channel preference %d MHz due to p2p_ignore_shared_freq=1 configuration", @@ -600,7 +629,7 @@ static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role) */ go_wpa_s = wpas_p2p_get_go_group(wpa_s); persistent_go = wpas_p2p_get_persistent_go(wpa_s); - p2p_no_group_iface = wpa_s->conf->p2p_no_group_iface; + p2p_no_group_iface = !wpas_p2p_create_iface(wpa_s); wpa_printf(MSG_DEBUG, "P2P: GO(iface)=%p persistent(ssid)=%p", go_wpa_s, persistent_go); @@ -866,9 +895,12 @@ static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group formation " "timeout"); wpa_s->p2p_in_provisioning = 0; + wpas_p2p_group_formation_failed(wpa_s, 1); } wpa_s->p2p_in_invitation = 0; + eloop_cancel_timeout(wpas_p2p_move_go, wpa_s, NULL); + eloop_cancel_timeout(wpas_p2p_reconsider_moving_go, wpa_s, NULL); /* * Make sure wait for the first client does not remain active after the @@ -940,6 +972,8 @@ static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, else wpa_drv_deinit_p2p_cli(wpa_s); + os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN); + return 0; } @@ -1109,13 +1143,14 @@ static void wpas_p2p_add_persistent_group_client(struct wpa_supplicant *wpa_s, u8 *n; size_t i; int found = 0; + struct wpa_supplicant *p2p_wpa_s = wpa_s->global->p2p_init_wpa_s; ssid = wpa_s->current_ssid; if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GO || !ssid->p2p_persistent_group) return; - for (s = wpa_s->parent->conf->ssid; s; s = s->next) { + for (s = p2p_wpa_s->conf->ssid; s; s = s->next) { if (s->disabled != 2 || s->mode != WPAS_MODE_P2P_GO) continue; @@ -1174,8 +1209,8 @@ static void wpas_p2p_add_persistent_group_client(struct wpa_supplicant *wpa_s, 0xff, ETH_ALEN); } - if (wpa_s->parent->conf->update_config && - wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf)) + if (p2p_wpa_s->conf->update_config && + wpa_config_write(p2p_wpa_s->confname, p2p_wpa_s->conf)) wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration"); } @@ -1226,7 +1261,7 @@ static void wpas_p2p_group_started(struct wpa_supplicant *wpa_s, static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s, - int success) + int success, int already_deleted) { struct wpa_ssid *ssid; int client; @@ -1251,6 +1286,9 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s, if (!success) { wpa_msg_global(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_FORMATION_FAILURE); + wpas_notify_p2p_group_formation_failure(wpa_s, ""); + if (already_deleted) + return; wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_FORMATION_FAILED); return; @@ -1677,14 +1715,22 @@ static void p2p_go_configured(void *ctx, void *data) params->persistent_group, ""); wpa_s->group_formation_reported = 1; - if (wpa_s->parent->p2ps_join_addr_valid) { - wpa_dbg(wpa_s, MSG_DEBUG, - "P2PS: Setting default PIN for " MACSTR, - MAC2STR(wpa_s->parent->p2ps_join_addr)); - wpa_supplicant_ap_wps_pin(wpa_s, - wpa_s->parent->p2ps_join_addr, - "12345670", NULL, 0, 0); - wpa_s->parent->p2ps_join_addr_valid = 0; + if (wpa_s->parent->p2ps_method_config_any) { + if (is_zero_ether_addr(wpa_s->parent->p2ps_join_addr)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2PS: Setting default PIN for ANY"); + wpa_supplicant_ap_wps_pin(wpa_s, NULL, + "12345670", NULL, 0, + 0); + } else { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2PS: Setting default PIN for " MACSTR, + MAC2STR(wpa_s->parent->p2ps_join_addr)); + wpa_supplicant_ap_wps_pin( + wpa_s, wpa_s->parent->p2ps_join_addr, + "12345670", NULL, 0, 0); + } + wpa_s->parent->p2ps_method_config_any = 0; } os_get_reltime(&wpa_s->global->p2p_go_wait_client); @@ -1771,6 +1817,7 @@ static void wpas_start_wps_go(struct wpa_supplicant *wpa_s, wpa_s->show_group_started = 0; wpa_s->p2p_go_group_formation_completed = 0; wpa_s->group_formation_reported = 0; + os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN); wpa_config_set_network_defaults(ssid); ssid->temporary = 1; @@ -1853,6 +1900,7 @@ static void wpas_p2p_clone_config(struct wpa_supplicant *dst, d->num_sec_device_types = s->num_sec_device_types; d->p2p_group_idle = s->p2p_group_idle; + d->p2p_go_freq_change_policy = s->p2p_go_freq_change_policy; d->p2p_intra_bss = s->p2p_intra_bss; d->persistent_reconnect = s->persistent_reconnect; d->max_num_sta = s->max_num_sta; @@ -1869,6 +1917,7 @@ static void wpas_p2p_clone_config(struct wpa_supplicant *dst, d->wps_nfc_dh_privkey = wpabuf_dup(s->wps_nfc_dh_privkey); d->wps_nfc_dh_pubkey = wpabuf_dup(s->wps_nfc_dh_pubkey); } + d->p2p_cli_probe = s->p2p_cli_probe; } @@ -2014,17 +2063,18 @@ static void wpas_p2p_group_formation_timeout(void *eloop_ctx, { struct wpa_supplicant *wpa_s = eloop_ctx; wpa_printf(MSG_DEBUG, "P2P: Group Formation timed out"); - wpas_p2p_group_formation_failed(wpa_s); + wpas_p2p_group_formation_failed(wpa_s, 0); } -void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s) +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); if (wpa_s->global->p2p) p2p_group_formation_failed(wpa_s->global->p2p); - wpas_group_formation_completed(wpa_s, 0); + wpas_group_formation_completed(wpa_s, 0, already_deleted); } @@ -2070,6 +2120,11 @@ static void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res) return; } + if (!res->role_go) { + /* Inform driver of the operating channel of GO. */ + wpa_drv_set_prob_oper_freq(wpa_s, res->freq); + } + if (wpa_s->p2p_go_ht40) res->ht40 = 1; if (wpa_s->p2p_go_vht) @@ -2105,7 +2160,7 @@ static void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res) wpas_p2p_remove_pending_group_interface(wpa_s); eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL); - wpas_p2p_group_formation_failed(wpa_s); + wpas_p2p_group_formation_failed(wpa_s, 1); return; } if (group_wpa_s != wpa_s) { @@ -2117,18 +2172,22 @@ static void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res) wpa_s->pending_interface_name[0] = '\0'; group_wpa_s->p2p_in_provisioning = 1; - if (res->role_go) + if (res->role_go) { wpas_start_wps_go(group_wpa_s, res, 1); - else + } 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; - if (res->role_go) + if (res->role_go) { wpas_start_wps_go(wpa_s, res, 1); - else + } else { + os_get_reltime(&wpa_s->scan_min_time); wpas_start_wps_enrollee(ctx, res); + } } wpa_s->p2p_long_listen = 0; @@ -2141,13 +2200,15 @@ static void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res) } -static void wpas_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id) +static void wpas_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id, + u8 go_intent) { struct wpa_supplicant *wpa_s = ctx; wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_REQUEST MACSTR - " dev_passwd_id=%u", MAC2STR(src), dev_passwd_id); + " dev_passwd_id=%u go_intent=%u", MAC2STR(src), + dev_passwd_id, go_intent); - wpas_notify_p2p_go_neg_req(wpa_s, src, dev_passwd_id); + wpas_notify_p2p_go_neg_req(wpa_s, src, dev_passwd_id, go_intent); } @@ -2248,6 +2309,7 @@ static void wpas_find_stopped(void *ctx) { struct wpa_supplicant *wpa_s = ctx; wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_FIND_STOPPED); + wpas_notify_p2p_find_stopped(wpa_s); } @@ -2376,1236 +2438,24 @@ static void wpas_stop_listen(void *ctx) wpa_s->roc_waiting_drv_freq = 0; } wpa_drv_set_ap_wps_ie(wpa_s, NULL, NULL, NULL); - wpa_drv_probe_req_report(wpa_s, 0); - wpas_p2p_listen_work_done(wpa_s); -} - - -static int wpas_send_probe_resp(void *ctx, const struct wpabuf *buf) -{ - struct wpa_supplicant *wpa_s = ctx; - return wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1); -} - - -/* - * DNS Header section is used only to calculate compression pointers, so the - * contents of this data does not matter, but the length needs to be reserved - * in the virtual packet. - */ -#define DNS_HEADER_LEN 12 - -/* - * 27-octet in-memory packet from P2P specification containing two implied - * queries for _tcp.lcoal. PTR IN and _udp.local. PTR IN - */ -#define P2P_SD_IN_MEMORY_LEN 27 - -static int p2p_sd_dns_uncompress_label(char **upos, char *uend, u8 *start, - u8 **spos, const u8 *end) -{ - while (*spos < end) { - u8 val = ((*spos)[0] & 0xc0) >> 6; - int len; - - if (val == 1 || val == 2) { - /* These are reserved values in RFC 1035 */ - wpa_printf(MSG_DEBUG, "P2P: Invalid domain name " - "sequence starting with 0x%x", val); - return -1; - } - - if (val == 3) { - u16 offset; - u8 *spos_tmp; - - /* Offset */ - if (*spos + 2 > end) { - wpa_printf(MSG_DEBUG, "P2P: No room for full " - "DNS offset field"); - return -1; - } - - offset = (((*spos)[0] & 0x3f) << 8) | (*spos)[1]; - if (offset >= *spos - start) { - wpa_printf(MSG_DEBUG, "P2P: Invalid DNS " - "pointer offset %u", offset); - return -1; - } - - (*spos) += 2; - spos_tmp = start + offset; - return p2p_sd_dns_uncompress_label(upos, uend, start, - &spos_tmp, - *spos - 2); - } - - /* Label */ - len = (*spos)[0] & 0x3f; - if (len == 0) - return 0; - - (*spos)++; - if (*spos + len > end) { - wpa_printf(MSG_DEBUG, "P2P: Invalid domain name " - "sequence - no room for label with length " - "%u", len); - return -1; - } - - if (*upos + len + 2 > uend) - return -2; - - os_memcpy(*upos, *spos, len); - *spos += len; - *upos += len; - (*upos)[0] = '.'; - (*upos)++; - (*upos)[0] = '\0'; - } - - return 0; -} - - -/* Uncompress domain names per RFC 1035 using the P2P SD in-memory packet. - * Returns -1 on parsing error (invalid input sequence), -2 if output buffer is - * not large enough */ -static int p2p_sd_dns_uncompress(char *buf, size_t buf_len, const u8 *msg, - size_t msg_len, size_t offset) -{ - /* 27-octet in-memory packet from P2P specification */ - const char *prefix = "\x04_tcp\x05local\x00\x00\x0C\x00\x01" - "\x04_udp\xC0\x11\x00\x0C\x00\x01"; - u8 *tmp, *end, *spos; - char *upos, *uend; - int ret = 0; - - if (buf_len < 2) - return -1; - if (offset > msg_len) - return -1; - - tmp = os_malloc(DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN + msg_len); - if (tmp == NULL) - return -1; - spos = tmp + DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN; - end = spos + msg_len; - spos += offset; - - os_memset(tmp, 0, DNS_HEADER_LEN); - os_memcpy(tmp + DNS_HEADER_LEN, prefix, P2P_SD_IN_MEMORY_LEN); - os_memcpy(tmp + DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN, msg, msg_len); - - upos = buf; - uend = buf + buf_len; - - ret = p2p_sd_dns_uncompress_label(&upos, uend, tmp, &spos, end); - if (ret) { - os_free(tmp); - return ret; - } - - if (upos == buf) { - upos[0] = '.'; - upos[1] = '\0'; - } else if (upos[-1] == '.') - upos[-1] = '\0'; - - os_free(tmp); - return 0; -} - - -static struct p2p_srv_bonjour * -wpas_p2p_service_get_bonjour(struct wpa_supplicant *wpa_s, - const struct wpabuf *query) -{ - struct p2p_srv_bonjour *bsrv; - size_t len; - - len = wpabuf_len(query); - dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour, - struct p2p_srv_bonjour, list) { - if (len == wpabuf_len(bsrv->query) && - os_memcmp(wpabuf_head(query), wpabuf_head(bsrv->query), - len) == 0) - return bsrv; - } - return NULL; -} - - -static struct p2p_srv_upnp * -wpas_p2p_service_get_upnp(struct wpa_supplicant *wpa_s, u8 version, - const char *service) -{ - struct p2p_srv_upnp *usrv; - - dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp, - struct p2p_srv_upnp, list) { - if (version == usrv->version && - os_strcmp(service, usrv->service) == 0) - return usrv; - } - return NULL; -} - - -static void wpas_sd_add_empty(struct wpabuf *resp, u8 srv_proto, - u8 srv_trans_id, u8 status) -{ - u8 *len_pos; - - if (wpabuf_tailroom(resp) < 5) - return; - - /* Length (to be filled) */ - len_pos = wpabuf_put(resp, 2); - wpabuf_put_u8(resp, srv_proto); - wpabuf_put_u8(resp, srv_trans_id); - /* Status Code */ - wpabuf_put_u8(resp, status); - /* Response Data: empty */ - WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2); -} - - -static void wpas_sd_add_proto_not_avail(struct wpabuf *resp, u8 srv_proto, - u8 srv_trans_id) -{ - wpas_sd_add_empty(resp, srv_proto, srv_trans_id, - P2P_SD_PROTO_NOT_AVAILABLE); -} - - -static void wpas_sd_add_bad_request(struct wpabuf *resp, u8 srv_proto, - u8 srv_trans_id) -{ - wpas_sd_add_empty(resp, srv_proto, srv_trans_id, P2P_SD_BAD_REQUEST); -} - - -static void wpas_sd_add_not_found(struct wpabuf *resp, u8 srv_proto, - u8 srv_trans_id) -{ - wpas_sd_add_empty(resp, srv_proto, srv_trans_id, - P2P_SD_REQUESTED_INFO_NOT_AVAILABLE); -} - - -static void wpas_sd_all_bonjour(struct wpa_supplicant *wpa_s, - struct wpabuf *resp, u8 srv_trans_id) -{ - struct p2p_srv_bonjour *bsrv; - u8 *len_pos; - - wpa_printf(MSG_DEBUG, "P2P: SD Request for all Bonjour services"); - - if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) { - wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available"); - return; - } - - dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour, - struct p2p_srv_bonjour, list) { - if (wpabuf_tailroom(resp) < - 5 + wpabuf_len(bsrv->query) + wpabuf_len(bsrv->resp)) - return; - /* Length (to be filled) */ - len_pos = wpabuf_put(resp, 2); - wpabuf_put_u8(resp, P2P_SERV_BONJOUR); - wpabuf_put_u8(resp, srv_trans_id); - /* Status Code */ - wpabuf_put_u8(resp, P2P_SD_SUCCESS); - wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service", - wpabuf_head(bsrv->resp), - wpabuf_len(bsrv->resp)); - /* Response Data */ - wpabuf_put_buf(resp, bsrv->query); /* Key */ - wpabuf_put_buf(resp, bsrv->resp); /* Value */ - WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - - 2); - } -} - - -static int match_bonjour_query(struct p2p_srv_bonjour *bsrv, const u8 *query, - size_t query_len) -{ - char str_rx[256], str_srv[256]; - - if (query_len < 3 || wpabuf_len(bsrv->query) < 3) - return 0; /* Too short to include DNS Type and Version */ - if (os_memcmp(query + query_len - 3, - wpabuf_head_u8(bsrv->query) + wpabuf_len(bsrv->query) - 3, - 3) != 0) - return 0; /* Mismatch in DNS Type or Version */ - if (query_len == wpabuf_len(bsrv->query) && - os_memcmp(query, wpabuf_head(bsrv->query), query_len - 3) == 0) - return 1; /* Binary match */ - - if (p2p_sd_dns_uncompress(str_rx, sizeof(str_rx), query, query_len - 3, - 0)) - return 0; /* Failed to uncompress query */ - if (p2p_sd_dns_uncompress(str_srv, sizeof(str_srv), - wpabuf_head(bsrv->query), - wpabuf_len(bsrv->query) - 3, 0)) - return 0; /* Failed to uncompress service */ - - return os_strcmp(str_rx, str_srv) == 0; -} - - -static void wpas_sd_req_bonjour(struct wpa_supplicant *wpa_s, - struct wpabuf *resp, u8 srv_trans_id, - const u8 *query, size_t query_len) -{ - struct p2p_srv_bonjour *bsrv; - u8 *len_pos; - int matches = 0; - - wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for Bonjour", - query, query_len); - if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) { - wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available"); - wpas_sd_add_proto_not_avail(resp, P2P_SERV_BONJOUR, - srv_trans_id); - return; - } - - if (query_len == 0) { - wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id); - return; - } - - dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour, - struct p2p_srv_bonjour, list) { - if (!match_bonjour_query(bsrv, query, query_len)) - continue; - - if (wpabuf_tailroom(resp) < - 5 + query_len + wpabuf_len(bsrv->resp)) - return; - - matches++; - - /* Length (to be filled) */ - len_pos = wpabuf_put(resp, 2); - wpabuf_put_u8(resp, P2P_SERV_BONJOUR); - wpabuf_put_u8(resp, srv_trans_id); - - /* Status Code */ - wpabuf_put_u8(resp, P2P_SD_SUCCESS); - wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service", - wpabuf_head(bsrv->resp), - wpabuf_len(bsrv->resp)); - - /* Response Data */ - wpabuf_put_data(resp, query, query_len); /* Key */ - wpabuf_put_buf(resp, bsrv->resp); /* Value */ - - WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2); - } - - if (matches == 0) { - wpa_printf(MSG_DEBUG, "P2P: Requested Bonjour service not " - "available"); - if (wpabuf_tailroom(resp) < 5) - return; - - /* Length (to be filled) */ - len_pos = wpabuf_put(resp, 2); - wpabuf_put_u8(resp, P2P_SERV_BONJOUR); - wpabuf_put_u8(resp, srv_trans_id); - - /* Status Code */ - wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE); - /* Response Data: empty */ - WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - - 2); - } -} - - -static void wpas_sd_all_upnp(struct wpa_supplicant *wpa_s, - struct wpabuf *resp, u8 srv_trans_id) -{ - struct p2p_srv_upnp *usrv; - u8 *len_pos; - - wpa_printf(MSG_DEBUG, "P2P: SD Request for all UPnP services"); - - if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) { - wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available"); - return; - } - - dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp, - struct p2p_srv_upnp, list) { - if (wpabuf_tailroom(resp) < 5 + 1 + os_strlen(usrv->service)) - return; - - /* Length (to be filled) */ - len_pos = wpabuf_put(resp, 2); - wpabuf_put_u8(resp, P2P_SERV_UPNP); - wpabuf_put_u8(resp, srv_trans_id); - - /* Status Code */ - wpabuf_put_u8(resp, P2P_SD_SUCCESS); - /* Response Data */ - wpabuf_put_u8(resp, usrv->version); - wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s", - usrv->service); - wpabuf_put_str(resp, usrv->service); - WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - - 2); - } -} - - -static void wpas_sd_req_upnp(struct wpa_supplicant *wpa_s, - struct wpabuf *resp, u8 srv_trans_id, - const u8 *query, size_t query_len) -{ - struct p2p_srv_upnp *usrv; - u8 *len_pos; - u8 version; - char *str; - int count = 0; - - wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for UPnP", - query, query_len); - - if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) { - wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available"); - wpas_sd_add_proto_not_avail(resp, P2P_SERV_UPNP, - srv_trans_id); - return; - } - - if (query_len == 0) { - wpas_sd_all_upnp(wpa_s, resp, srv_trans_id); - return; - } - - if (wpabuf_tailroom(resp) < 5) - return; - - /* Length (to be filled) */ - len_pos = wpabuf_put(resp, 2); - wpabuf_put_u8(resp, P2P_SERV_UPNP); - wpabuf_put_u8(resp, srv_trans_id); - - version = query[0]; - str = os_malloc(query_len); - if (str == NULL) - return; - os_memcpy(str, query + 1, query_len - 1); - str[query_len - 1] = '\0'; - - dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp, - struct p2p_srv_upnp, list) { - if (version != usrv->version) - continue; - - if (os_strcmp(str, "ssdp:all") != 0 && - os_strstr(usrv->service, str) == NULL) - continue; - - if (wpabuf_tailroom(resp) < 2) - break; - if (count == 0) { - /* Status Code */ - wpabuf_put_u8(resp, P2P_SD_SUCCESS); - /* Response Data */ - wpabuf_put_u8(resp, version); - } else - wpabuf_put_u8(resp, ','); - - count++; - - wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s", - usrv->service); - if (wpabuf_tailroom(resp) < os_strlen(usrv->service)) - break; - wpabuf_put_str(resp, usrv->service); - } - os_free(str); - - if (count == 0) { - wpa_printf(MSG_DEBUG, "P2P: Requested UPnP service not " - "available"); - /* Status Code */ - wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE); - /* Response Data: empty */ - } - - WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2); -} - - -#ifdef CONFIG_WIFI_DISPLAY -static void wpas_sd_req_wfd(struct wpa_supplicant *wpa_s, - struct wpabuf *resp, u8 srv_trans_id, - const u8 *query, size_t query_len) -{ - const u8 *pos; - u8 role; - u8 *len_pos; - - wpa_hexdump(MSG_DEBUG, "P2P: SD Request for WFD", query, query_len); - - if (!wpa_s->global->wifi_display) { - wpa_printf(MSG_DEBUG, "P2P: WFD protocol not available"); - wpas_sd_add_proto_not_avail(resp, P2P_SERV_WIFI_DISPLAY, - srv_trans_id); - return; - } - - if (query_len < 1) { - wpa_printf(MSG_DEBUG, "P2P: Missing WFD Requested Device " - "Role"); - return; - } - - if (wpabuf_tailroom(resp) < 5) - return; - - pos = query; - role = *pos++; - wpa_printf(MSG_DEBUG, "P2P: WSD for device role 0x%x", role); - - /* TODO: role specific handling */ - - /* Length (to be filled) */ - len_pos = wpabuf_put(resp, 2); - wpabuf_put_u8(resp, P2P_SERV_WIFI_DISPLAY); - wpabuf_put_u8(resp, srv_trans_id); - wpabuf_put_u8(resp, P2P_SD_SUCCESS); /* Status Code */ - - while (pos < query + query_len) { - if (*pos < MAX_WFD_SUBELEMS && - wpa_s->global->wfd_subelem[*pos] && - wpabuf_tailroom(resp) >= - wpabuf_len(wpa_s->global->wfd_subelem[*pos])) { - wpa_printf(MSG_DEBUG, "P2P: Add WSD response " - "subelement %u", *pos); - wpabuf_put_buf(resp, wpa_s->global->wfd_subelem[*pos]); - } - pos++; - } - - WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2); -} -#endif /* CONFIG_WIFI_DISPLAY */ - - -static int find_p2ps_substr(struct p2ps_advertisement *adv_data, - const u8 *needle, size_t needle_len) -{ - const u8 *haystack = (const u8 *) adv_data->svc_info; - size_t haystack_len, i; - - /* Allow search term to be empty */ - if (!needle || !needle_len) - return 1; - - if (!haystack) - return 0; - - haystack_len = os_strlen(adv_data->svc_info); - for (i = 0; i < haystack_len; i++) { - if (haystack_len - i < needle_len) - break; - if (os_memcmp(haystack + i, needle, needle_len) == 0) - return 1; - } - - return 0; -} - - -static void wpas_sd_req_asp(struct wpa_supplicant *wpa_s, - struct wpabuf *resp, u8 srv_trans_id, - const u8 *query, size_t query_len) -{ - struct p2ps_advertisement *adv_data; - const u8 *svc = &query[1]; - const u8 *info = NULL; - size_t svc_len = query[0]; - size_t info_len = 0; - int prefix = 0; - u8 *count_pos = NULL; - u8 *len_pos = NULL; - - wpa_hexdump(MSG_DEBUG, "P2P: SD Request for ASP", query, query_len); - - if (!wpa_s->global->p2p) { - wpa_printf(MSG_DEBUG, "P2P: ASP protocol not available"); - wpas_sd_add_proto_not_avail(resp, P2P_SERV_P2PS, srv_trans_id); - return; - } - - /* Info block is optional */ - if (svc_len + 1 < query_len) { - info = &svc[svc_len]; - info_len = *info++; - } - - /* Range check length of svc string and info block */ - if (svc_len + (info_len ? info_len + 2 : 1) > query_len) { - wpa_printf(MSG_DEBUG, "P2P: ASP bad request"); - wpas_sd_add_bad_request(resp, P2P_SERV_P2PS, srv_trans_id); - return; - } - /* Detect and correct for prefix search */ - if (svc_len && svc[svc_len - 1] == '*') { - prefix = 1; - svc_len--; - } - - for (adv_data = p2p_get_p2ps_adv_list(wpa_s->global->p2p); - adv_data; adv_data = adv_data->next) { - /* If not a prefix match, reject length mismatches */ - if (!prefix && svc_len != os_strlen(adv_data->svc_name)) - continue; - - /* Search each service for request */ - if (os_memcmp(adv_data->svc_name, svc, svc_len) == 0 && - find_p2ps_substr(adv_data, info, info_len)) { - size_t len = os_strlen(adv_data->svc_name); - size_t svc_info_len = 0; - - if (adv_data->svc_info) - svc_info_len = os_strlen(adv_data->svc_info); - - if (len > 0xff || svc_info_len > 0xffff) - return; - - /* Length & Count to be filled as we go */ - if (!len_pos && !count_pos) { - if (wpabuf_tailroom(resp) < - len + svc_info_len + 16) - return; - - len_pos = wpabuf_put(resp, 2); - wpabuf_put_u8(resp, P2P_SERV_P2PS); - wpabuf_put_u8(resp, srv_trans_id); - /* Status Code */ - wpabuf_put_u8(resp, P2P_SD_SUCCESS); - count_pos = wpabuf_put(resp, 1); - *count_pos = 0; - } else if (wpabuf_tailroom(resp) < - len + svc_info_len + 10) - return; - - if (svc_info_len) { - wpa_printf(MSG_DEBUG, - "P2P: Add Svc: %s info: %s", - adv_data->svc_name, - adv_data->svc_info); - } else { - wpa_printf(MSG_DEBUG, "P2P: Add Svc: %s", - adv_data->svc_name); - } - - /* Advertisement ID */ - wpabuf_put_le32(resp, adv_data->id); - - /* Config Methods */ - wpabuf_put_be16(resp, adv_data->config_methods); - - /* Service Name */ - wpabuf_put_u8(resp, (u8) len); - wpabuf_put_data(resp, adv_data->svc_name, len); - - /* Service State */ - wpabuf_put_u8(resp, adv_data->state); - - /* Service Information */ - wpabuf_put_le16(resp, (u16) svc_info_len); - wpabuf_put_data(resp, adv_data->svc_info, svc_info_len); - - /* Update length and count */ - (*count_pos)++; - WPA_PUT_LE16(len_pos, - (u8 *) wpabuf_put(resp, 0) - len_pos - 2); - } - } - - /* Return error if no matching svc found */ - if (count_pos == NULL) { - wpa_printf(MSG_DEBUG, "P2P: ASP service not found"); - wpas_sd_add_not_found(resp, P2P_SERV_P2PS, srv_trans_id); - } -} - - -static void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token, - u16 update_indic, const u8 *tlvs, size_t tlvs_len) -{ - struct wpa_supplicant *wpa_s = ctx; - const u8 *pos = tlvs; - const u8 *end = tlvs + tlvs_len; - const u8 *tlv_end; - u16 slen; - struct wpabuf *resp; - u8 srv_proto, srv_trans_id; - size_t buf_len; - char *buf; - - wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Request TLVs", - tlvs, tlvs_len); - buf_len = 2 * tlvs_len + 1; - buf = os_malloc(buf_len); - if (buf) { - wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len); - wpa_msg_ctrl(wpa_s, MSG_INFO, P2P_EVENT_SERV_DISC_REQ "%d " - MACSTR " %u %u %s", - freq, MAC2STR(sa), dialog_token, update_indic, - buf); - os_free(buf); - } - - if (wpa_s->p2p_sd_over_ctrl_iface) { - wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token, - update_indic, tlvs, tlvs_len); - return; /* to be processed by an external program */ - } - - resp = wpabuf_alloc(10000); - if (resp == NULL) - return; - - while (pos + 1 < end) { - wpa_printf(MSG_DEBUG, "P2P: Service Request TLV"); - slen = WPA_GET_LE16(pos); - pos += 2; - if (pos + slen > end || slen < 2) { - wpa_printf(MSG_DEBUG, "P2P: Unexpected Query Data " - "length"); - wpabuf_free(resp); - return; - } - tlv_end = pos + slen; - - srv_proto = *pos++; - wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u", - srv_proto); - srv_trans_id = *pos++; - wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u", - srv_trans_id); - - wpa_hexdump(MSG_MSGDUMP, "P2P: Query Data", - pos, tlv_end - pos); - - - if (wpa_s->force_long_sd) { - wpa_printf(MSG_DEBUG, "P2P: SD test - force long " - "response"); - wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id); - wpas_sd_all_upnp(wpa_s, resp, srv_trans_id); - goto done; - } - - switch (srv_proto) { - case P2P_SERV_ALL_SERVICES: - wpa_printf(MSG_DEBUG, "P2P: Service Discovery Request " - "for all services"); - if (dl_list_empty(&wpa_s->global->p2p_srv_upnp) && - dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) { - wpa_printf(MSG_DEBUG, "P2P: No service " - "discovery protocols available"); - wpas_sd_add_proto_not_avail( - resp, P2P_SERV_ALL_SERVICES, - srv_trans_id); - break; - } - wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id); - wpas_sd_all_upnp(wpa_s, resp, srv_trans_id); - break; - case P2P_SERV_BONJOUR: - wpas_sd_req_bonjour(wpa_s, resp, srv_trans_id, - pos, tlv_end - pos); - break; - case P2P_SERV_UPNP: - wpas_sd_req_upnp(wpa_s, resp, srv_trans_id, - pos, tlv_end - pos); - break; -#ifdef CONFIG_WIFI_DISPLAY - case P2P_SERV_WIFI_DISPLAY: - wpas_sd_req_wfd(wpa_s, resp, srv_trans_id, - pos, tlv_end - pos); - break; -#endif /* CONFIG_WIFI_DISPLAY */ - case P2P_SERV_P2PS: - wpas_sd_req_asp(wpa_s, resp, srv_trans_id, - pos, tlv_end - pos); - break; - default: - wpa_printf(MSG_DEBUG, "P2P: Unavailable service " - "protocol %u", srv_proto); - wpas_sd_add_proto_not_avail(resp, srv_proto, - srv_trans_id); - break; - } - - pos = tlv_end; - } - -done: - wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token, - update_indic, tlvs, tlvs_len); - - wpas_p2p_sd_response(wpa_s, freq, sa, dialog_token, resp); - - wpabuf_free(resp); -} - - -static void wpas_sd_p2ps_serv_response(struct wpa_supplicant *wpa_s, - const u8 *sa, u8 srv_trans_id, - const u8 *pos, const u8 *tlv_end) -{ - u8 left = *pos++; - u32 adv_id; - u8 svc_status; - u16 config_methods; - char svc_str[256]; - - while (left-- && pos < tlv_end) { - char *buf = NULL; - size_t buf_len; - u8 svc_len; - - /* Sanity check fixed length+svc_str */ - if (pos + 6 >= tlv_end) - break; - svc_len = pos[6]; - if (pos + svc_len + 10 > tlv_end) - break; - - /* Advertisement ID */ - adv_id = WPA_GET_LE32(pos); - pos += sizeof(u32); - - /* Config Methods */ - config_methods = WPA_GET_BE16(pos); - pos += sizeof(u16); - - /* Service Name */ - pos++; /* svc_len */ - os_memcpy(svc_str, pos, svc_len); - svc_str[svc_len] = '\0'; - pos += svc_len; - - /* Service Status */ - svc_status = *pos++; - - /* Service Information Length */ - buf_len = WPA_GET_LE16(pos); - pos += sizeof(u16); - - /* Sanity check buffer length */ - if (buf_len > (unsigned int) (tlv_end - pos)) - break; - - if (buf_len) { - buf = os_zalloc(2 * buf_len + 1); - if (buf) { - utf8_escape((const char *) pos, buf_len, buf, - 2 * buf_len + 1); - } - } - - pos += buf_len; + /* + * Don't cancel Probe Request RX reporting for a connected P2P Client + * handling Probe Request frames. + */ + if (!wpa_s->p2p_cli_probe) + wpa_drv_probe_req_report(wpa_s, 0); - if (buf) { - wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_SERV_ASP_RESP - MACSTR " %x %x %x %x %s '%s'", - MAC2STR(sa), srv_trans_id, adv_id, - svc_status, config_methods, svc_str, - buf); - os_free(buf); - } else { - wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_SERV_ASP_RESP - MACSTR " %x %x %x %x %s", - MAC2STR(sa), srv_trans_id, adv_id, - svc_status, config_methods, svc_str); - } - } + wpas_p2p_listen_work_done(wpa_s); } -static void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic, - const u8 *tlvs, size_t tlvs_len) +static int wpas_send_probe_resp(void *ctx, const struct wpabuf *buf, + unsigned int freq) { struct wpa_supplicant *wpa_s = ctx; - const u8 *pos = tlvs; - const u8 *end = tlvs + tlvs_len; - const u8 *tlv_end; - u16 slen; - size_t buf_len; - char *buf; - - wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Response TLVs", - tlvs, tlvs_len); - if (tlvs_len > 1500) { - /* TODO: better way for handling this */ - wpa_msg_ctrl(wpa_s, MSG_INFO, - P2P_EVENT_SERV_DISC_RESP MACSTR - " %u <long response: %u bytes>", - MAC2STR(sa), update_indic, - (unsigned int) tlvs_len); - } else { - buf_len = 2 * tlvs_len + 1; - buf = os_malloc(buf_len); - if (buf) { - wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len); - wpa_msg_ctrl(wpa_s, MSG_INFO, - P2P_EVENT_SERV_DISC_RESP MACSTR " %u %s", - MAC2STR(sa), update_indic, buf); - os_free(buf); - } - } - - while (pos < end) { - 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) { - wpa_printf(MSG_DEBUG, "P2P: Unexpected Response Data " - "length"); - return; - } - tlv_end = pos + slen; - - srv_proto = *pos++; - wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u", - srv_proto); - srv_trans_id = *pos++; - wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u", - srv_trans_id); - status = *pos++; - wpa_printf(MSG_DEBUG, "P2P: Status Code ID %u", - status); - - wpa_hexdump(MSG_MSGDUMP, "P2P: Response Data", - pos, tlv_end - pos); - - if (srv_proto == P2P_SERV_P2PS && pos < tlv_end) { - wpas_sd_p2ps_serv_response(wpa_s, sa, srv_trans_id, - pos, tlv_end); - } - - pos = tlv_end; - } - - wpas_notify_p2p_sd_response(wpa_s, sa, update_indic, tlvs, tlvs_len); -} - - -u64 wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst, - const struct wpabuf *tlvs) -{ - if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) - return 0; - return (uintptr_t) p2p_sd_request(wpa_s->global->p2p, dst, tlvs); -} - - -u64 wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst, - u8 version, const char *query) -{ - struct wpabuf *tlvs; - u64 ret; - - tlvs = wpabuf_alloc(2 + 1 + 1 + 1 + os_strlen(query)); - if (tlvs == NULL) - return 0; - wpabuf_put_le16(tlvs, 1 + 1 + 1 + os_strlen(query)); - wpabuf_put_u8(tlvs, P2P_SERV_UPNP); /* Service Protocol Type */ - wpabuf_put_u8(tlvs, 1); /* Service Transaction ID */ - wpabuf_put_u8(tlvs, version); - wpabuf_put_str(tlvs, query); - ret = wpas_p2p_sd_request(wpa_s, dst, tlvs); - wpabuf_free(tlvs); - return ret; -} - - -u64 wpas_p2p_sd_request_asp(struct wpa_supplicant *wpa_s, const u8 *dst, u8 id, - const char *svc_str, const char *info_substr) -{ - struct wpabuf *tlvs; - size_t plen, svc_len, substr_len = 0; - u64 ret; - - svc_len = os_strlen(svc_str); - if (info_substr) - substr_len = os_strlen(info_substr); - - if (svc_len > 0xff || substr_len > 0xff) - return 0; - - plen = 1 + 1 + 1 + svc_len + 1 + substr_len; - tlvs = wpabuf_alloc(2 + plen); - if (tlvs == NULL) - return 0; - - wpabuf_put_le16(tlvs, plen); - wpabuf_put_u8(tlvs, P2P_SERV_P2PS); - wpabuf_put_u8(tlvs, id); /* Service Transaction ID */ - wpabuf_put_u8(tlvs, (u8) svc_len); /* Service String Length */ - wpabuf_put_data(tlvs, svc_str, svc_len); - wpabuf_put_u8(tlvs, (u8) substr_len); /* Info Substring Length */ - wpabuf_put_data(tlvs, info_substr, substr_len); - ret = wpas_p2p_sd_request(wpa_s, dst, tlvs); - wpabuf_free(tlvs); - - return ret; -} - - -#ifdef CONFIG_WIFI_DISPLAY - -static u64 wpas_p2p_sd_request_wfd(struct wpa_supplicant *wpa_s, const u8 *dst, - const struct wpabuf *tlvs) -{ - if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) - return 0; - return (uintptr_t) p2p_sd_request_wfd(wpa_s->global->p2p, dst, tlvs); -} - - -#define MAX_WFD_SD_SUBELEMS 20 - -static void wfd_add_sd_req_role(struct wpabuf *tlvs, u8 id, u8 role, - const char *subelems) -{ - u8 *len; - const char *pos; - int val; - int count = 0; - - len = wpabuf_put(tlvs, 2); - wpabuf_put_u8(tlvs, P2P_SERV_WIFI_DISPLAY); /* Service Protocol Type */ - wpabuf_put_u8(tlvs, id); /* Service Transaction ID */ - - wpabuf_put_u8(tlvs, role); - - pos = subelems; - while (*pos) { - val = atoi(pos); - if (val >= 0 && val < 256) { - wpabuf_put_u8(tlvs, val); - count++; - if (count == MAX_WFD_SD_SUBELEMS) - break; - } - pos = os_strchr(pos + 1, ','); - if (pos == NULL) - break; - pos++; - } - - WPA_PUT_LE16(len, (u8 *) wpabuf_put(tlvs, 0) - len - 2); -} - - -u64 wpas_p2p_sd_request_wifi_display(struct wpa_supplicant *wpa_s, - const u8 *dst, const char *role) -{ - struct wpabuf *tlvs; - u64 ret; - const char *subelems; - u8 id = 1; - - subelems = os_strchr(role, ' '); - if (subelems == NULL) - return 0; - subelems++; - - tlvs = wpabuf_alloc(4 * (2 + 1 + 1 + 1 + MAX_WFD_SD_SUBELEMS)); - if (tlvs == NULL) - return 0; - - if (os_strstr(role, "[source]")) - wfd_add_sd_req_role(tlvs, id++, 0x00, subelems); - if (os_strstr(role, "[pri-sink]")) - wfd_add_sd_req_role(tlvs, id++, 0x01, subelems); - if (os_strstr(role, "[sec-sink]")) - wfd_add_sd_req_role(tlvs, id++, 0x02, subelems); - if (os_strstr(role, "[source+sink]")) - wfd_add_sd_req_role(tlvs, id++, 0x03, subelems); - - ret = wpas_p2p_sd_request_wfd(wpa_s, dst, tlvs); - wpabuf_free(tlvs); - return ret; -} - -#endif /* CONFIG_WIFI_DISPLAY */ - - -int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, u64 req) -{ - if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) - return -1; - return p2p_sd_cancel_request(wpa_s->global->p2p, - (void *) (uintptr_t) req); -} - - -void wpas_p2p_sd_response(struct wpa_supplicant *wpa_s, int freq, - const u8 *dst, u8 dialog_token, - const struct wpabuf *resp_tlvs) -{ - if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) - return; - p2p_sd_response(wpa_s->global->p2p, freq, dst, dialog_token, - resp_tlvs); -} - - -void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s) -{ - if (wpa_s->global->p2p) - p2p_sd_service_update(wpa_s->global->p2p); -} - - -static void wpas_p2p_srv_bonjour_free(struct p2p_srv_bonjour *bsrv) -{ - dl_list_del(&bsrv->list); - wpabuf_free(bsrv->query); - wpabuf_free(bsrv->resp); - os_free(bsrv); -} - - -static void wpas_p2p_srv_upnp_free(struct p2p_srv_upnp *usrv) -{ - dl_list_del(&usrv->list); - os_free(usrv->service); - os_free(usrv); -} - - -void wpas_p2p_service_flush(struct wpa_supplicant *wpa_s) -{ - struct p2p_srv_bonjour *bsrv, *bn; - struct p2p_srv_upnp *usrv, *un; - - dl_list_for_each_safe(bsrv, bn, &wpa_s->global->p2p_srv_bonjour, - struct p2p_srv_bonjour, list) - wpas_p2p_srv_bonjour_free(bsrv); - - dl_list_for_each_safe(usrv, un, &wpa_s->global->p2p_srv_upnp, - struct p2p_srv_upnp, list) - wpas_p2p_srv_upnp_free(usrv); - - wpas_p2p_sd_service_update(wpa_s); -} - - -int wpas_p2p_service_p2ps_id_exists(struct wpa_supplicant *wpa_s, u32 adv_id) -{ - if (adv_id == 0) - return 1; - - if (p2p_service_p2ps_id(wpa_s->global->p2p, adv_id)) - return 1; - - return 0; -} - - -int wpas_p2p_service_del_asp(struct wpa_supplicant *wpa_s, u32 adv_id) -{ - return p2p_service_del_asp(wpa_s->global->p2p, adv_id); -} - - -int wpas_p2p_service_add_asp(struct wpa_supplicant *wpa_s, - int auto_accept, u32 adv_id, - const char *adv_str, u8 svc_state, - u16 config_methods, const char *svc_info) -{ - return p2p_service_add_asp(wpa_s->global->p2p, auto_accept, adv_id, - adv_str, svc_state, config_methods, - svc_info); -} - - -int wpas_p2p_service_add_bonjour(struct wpa_supplicant *wpa_s, - struct wpabuf *query, struct wpabuf *resp) -{ - struct p2p_srv_bonjour *bsrv; - - bsrv = os_zalloc(sizeof(*bsrv)); - if (bsrv == NULL) - return -1; - bsrv->query = query; - bsrv->resp = resp; - dl_list_add(&wpa_s->global->p2p_srv_bonjour, &bsrv->list); - - wpas_p2p_sd_service_update(wpa_s); - return 0; -} - - -int wpas_p2p_service_del_bonjour(struct wpa_supplicant *wpa_s, - const struct wpabuf *query) -{ - struct p2p_srv_bonjour *bsrv; - - bsrv = wpas_p2p_service_get_bonjour(wpa_s, query); - if (bsrv == NULL) - return -1; - wpas_p2p_srv_bonjour_free(bsrv); - wpas_p2p_sd_service_update(wpa_s); - return 0; -} - - -int wpas_p2p_service_add_upnp(struct wpa_supplicant *wpa_s, u8 version, - const char *service) -{ - struct p2p_srv_upnp *usrv; - - if (wpas_p2p_service_get_upnp(wpa_s, version, service)) - return 0; /* Already listed */ - usrv = os_zalloc(sizeof(*usrv)); - if (usrv == NULL) - return -1; - usrv->version = version; - usrv->service = os_strdup(service); - if (usrv->service == NULL) { - os_free(usrv); - return -1; - } - dl_list_add(&wpa_s->global->p2p_srv_upnp, &usrv->list); - - wpas_p2p_sd_service_update(wpa_s); - return 0; -} - - -int wpas_p2p_service_del_upnp(struct wpa_supplicant *wpa_s, u8 version, - const char *service) -{ - struct p2p_srv_upnp *usrv; - - usrv = wpas_p2p_service_get_upnp(wpa_s, version, service); - if (usrv == NULL) - return -1; - wpas_p2p_srv_upnp_free(usrv); - wpas_p2p_sd_service_update(wpa_s); - return 0; + return wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1, + freq); } @@ -3776,12 +2626,62 @@ static void wpas_prov_disc_fail(void *ctx, const u8 *peer, } -static int freq_included(const struct p2p_channels *channels, unsigned int freq) +static int freq_included(struct wpa_supplicant *wpa_s, + const struct p2p_channels *channels, + unsigned int freq) { - if (channels == NULL) - return 1; /* Assume no restrictions */ - return p2p_channels_includes_freq(channels, freq); + if ((channels == NULL || p2p_channels_includes_freq(channels, freq)) && + wpas_p2p_go_is_peer_freq(wpa_s, freq)) + return 1; + return 0; +} + + +static void wpas_p2p_go_update_common_freqs(struct wpa_supplicant *wpa_s) +{ + unsigned int num = P2P_MAX_CHANNELS; + int *common_freqs; + int ret; + + p2p_go_dump_common_freqs(wpa_s); + common_freqs = os_calloc(num, sizeof(int)); + if (!common_freqs) + return; + + ret = p2p_group_get_common_freqs(wpa_s->p2p_group, common_freqs, &num); + if (ret < 0) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Failed to get group common freqs"); + os_free(common_freqs); + return; + } + os_free(wpa_s->p2p_group_common_freqs); + wpa_s->p2p_group_common_freqs = common_freqs; + wpa_s->p2p_group_common_freqs_num = num; + p2p_go_dump_common_freqs(wpa_s); +} + + +/* + * Check if the given frequency is one of the possible operating frequencies + * set after the completion of the GO Negotiation. + */ +static int wpas_p2p_go_is_peer_freq(struct wpa_supplicant *wpa_s, int freq) +{ + unsigned int i; + + p2p_go_dump_common_freqs(wpa_s); + + /* assume no restrictions */ + if (!wpa_s->p2p_group_common_freqs_num) + return 1; + + for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) { + if (wpa_s->p2p_group_common_freqs[i] == freq) + return 1; + } + return 0; } @@ -3957,7 +2857,7 @@ accept_inv: "running a GO but we are capable of MCC, " "figure out the best channel to use"); *force_freq = 0; - } else if (!freq_included(channels, *force_freq)) { + } else if (!freq_included(wpa_s, channels, *force_freq)) { /* We are the GO, and *force_freq is not in the * intersection */ wpa_printf(MSG_DEBUG, "P2P: Forced GO freq %d MHz not " @@ -3995,7 +2895,8 @@ static void wpas_invitation_received(void *ctx, const u8 *sa, const u8 *bssid, int go = s->mode == WPAS_MODE_P2P_GO; wpas_p2p_group_add_persistent( wpa_s, s, go, 0, op_freq, 0, 0, NULL, - go ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0); + go ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0, + 1); } else if (bssid) { wpa_s->user_initiated_pd = 0; wpas_p2p_join(wpa_s, bssid, go_dev_addr, @@ -4026,6 +2927,8 @@ static void wpas_invitation_received(void *ctx, const u8 *sa, const u8 *bssid, " unknown-network", MAC2STR(sa), MAC2STR(go_dev_addr)); } + wpas_notify_p2p_invitation_received(wpa_s, sa, go_dev_addr, + bssid, 0, op_freq); return; } @@ -4038,6 +2941,8 @@ static void wpas_invitation_received(void *ctx, const u8 *sa, const u8 *bssid, "sa=" MACSTR " persistent=%d", MAC2STR(sa), s->id); } + wpas_notify_p2p_invitation_received(wpa_s, sa, go_dev_addr, bssid, + s->id, op_freq); } @@ -4046,6 +2951,7 @@ static void wpas_remove_persistent_peer(struct wpa_supplicant *wpa_s, const u8 *peer, int inv) { size_t i; + struct wpa_supplicant *p2p_wpa_s = wpa_s->global->p2p_init_wpa_s; if (ssid == NULL) return; @@ -4075,8 +2981,8 @@ static void wpas_remove_persistent_peer(struct wpa_supplicant *wpa_s, ssid->p2p_client_list + (i + 1) * 2 * ETH_ALEN, (ssid->num_p2p_clients - i - 1) * 2 * ETH_ALEN); ssid->num_p2p_clients--; - if (wpa_s->parent->conf->update_config && - wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf)) + if (p2p_wpa_s->conf->update_config && + wpa_config_write(p2p_wpa_s->confname, p2p_wpa_s->conf)) wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration"); } @@ -4163,10 +3069,10 @@ static void wpas_invitation_result(void *ctx, int status, const u8 *bssid, os_sleep(0, 50000); if (neg_freq > 0 && ssid->mode == WPAS_MODE_P2P_GO && - freq_included(channels, neg_freq)) + freq_included(wpa_s, channels, neg_freq)) freq = neg_freq; else if (peer_oper_freq > 0 && ssid->mode != WPAS_MODE_P2P_GO && - freq_included(channels, peer_oper_freq)) + freq_included(wpa_s, channels, peer_oper_freq)) freq = peer_oper_freq; else freq = 0; @@ -4181,7 +3087,7 @@ static void wpas_invitation_result(void *ctx, int status, const u8 *bssid, channels, ssid->mode == WPAS_MODE_P2P_GO ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : - 0); + 0, 1); } @@ -4320,7 +3226,7 @@ struct p2p_oper_class_map { enum { BW20, BW40PLUS, BW40MINUS, BW80, BW2160 } bw; }; -static struct p2p_oper_class_map op_class[] = { +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 }, @@ -4328,6 +3234,7 @@ static struct p2p_oper_class_map op_class[] = { #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 }, @@ -4453,7 +3360,7 @@ static int wpas_p2p_setup_channels(struct wpa_supplicant *wpa_s, cla = cli_cla = 0; for (op = 0; op_class[op].op_class; op++) { - struct p2p_oper_class_map *o = &op_class[op]; + const struct p2p_oper_class_map *o = &op_class[op]; u8 ch; struct p2p_reg_class *reg = NULL, *cli_reg = NULL; @@ -4512,12 +3419,13 @@ int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s, enum chan_allowed ret; for (op = 0; op_class[op].op_class; op++) { - struct p2p_oper_class_map *o = &op_class[op]; + const struct p2p_oper_class_map *o = &op_class[op]; u8 ch; for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) { if (o->mode != HOSTAPD_MODE_IEEE80211A || - o->bw == BW20 || ch != channel) + (o->bw != BW40PLUS && o->bw != BW40MINUS) || + ch != channel) continue; ret = wpas_p2p_verify_channel(wpa_s, mode, ch, o->bw); if (ret == ALLOWED) @@ -4581,12 +3489,7 @@ struct wpa_supplicant * wpas_get_p2p_client_iface(struct wpa_supplicant *wpa_s, { for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) { struct wpa_ssid *ssid = wpa_s->current_ssid; - if (ssid == NULL) - continue; - if (ssid->mode != WPAS_MODE_INFRA) - continue; - if (wpa_s->wpa_state != WPA_COMPLETED && - wpa_s->wpa_state != WPA_GROUP_HANDSHAKE) + if (ssid && (ssid->mode != WPAS_MODE_INFRA || !ssid->p2p_group)) continue; if (os_memcmp(wpa_s->go_dev_addr, peer_dev_addr, ETH_ALEN) == 0) return wpa_s; @@ -4667,14 +3570,12 @@ int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s, iface.confname = wpa_s->confname; iface.ctrl_interface = wpa_s->conf->ctrl_interface; } - iface.conf_p2p_dev = NULL; p2pdev_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface, wpa_s); if (!p2pdev_wpa_s) { wpa_printf(MSG_DEBUG, "P2P: Failed to add P2P Device interface"); return -1; } - wpa_s->p2p_dev = p2pdev_wpa_s; wpa_s->pending_interface_name[0] = '\0'; return 0; @@ -4705,7 +3606,8 @@ static void wpas_presence_resp(void *ctx, const u8 *src, u8 status, static int wpas_get_persistent_group(void *ctx, const u8 *addr, const u8 *ssid, size_t ssid_len, u8 *go_dev_addr, - u8 *ret_ssid, size_t *ret_ssid_len) + u8 *ret_ssid, size_t *ret_ssid_len, + u8 *intended_iface_addr) { struct wpa_supplicant *wpa_s = ctx; struct wpa_ssid *s; @@ -4715,6 +3617,19 @@ static int wpas_get_persistent_group(void *ctx, const u8 *addr, const u8 *ssid, os_memcpy(ret_ssid, s->ssid, s->ssid_len); *ret_ssid_len = s->ssid_len; os_memcpy(go_dev_addr, s->bssid, ETH_ALEN); + + if (s->mode != WPAS_MODE_P2P_GO) { + os_memset(intended_iface_addr, 0, ETH_ALEN); + } else if (wpas_p2p_create_iface(wpa_s)) { + if (wpas_p2p_add_group_interface(wpa_s, WPA_IF_P2P_GO)) + return 0; + + os_memcpy(intended_iface_addr, + wpa_s->pending_interface_addr, ETH_ALEN); + } else { + os_memcpy(intended_iface_addr, wpa_s->own_addr, + ETH_ALEN); + } return 1; } @@ -4729,17 +3644,24 @@ static int wpas_get_go_info(void *ctx, u8 *intended_addr, struct wpa_ssid *s; u8 bssid[ETH_ALEN]; + /* + * group_iface will be set to 1 only if a dedicated interface for P2P + * role is required. First, we try to reuse an active GO. However, + * if it is not present, we will try to reactivate an existing + * persistent group and set group_iface to 1, so the caller will know + * that the pending interface should be used. + */ + *group_iface = 0; s = wpas_p2p_group_go_ssid(wpa_s, bssid); if (!s) { 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); + else + return 0; } - *group_iface = wpas_p2p_create_iface(wpa_s); - if (!s) - return 0; - os_memcpy(intended_addr, bssid, ETH_ALEN); os_memcpy(ssid, s->ssid, s->ssid_len); *ssid_len = s->ssid_len; @@ -4793,19 +3715,49 @@ static int wpas_remove_stale_groups(void *ctx, const u8 *peer, const u8 *go, } +static void wpas_p2ps_get_feat_cap_str(char *buf, size_t buf_len, + const u8 *feat_cap, size_t feat_cap_len) +{ + static const char pref[] = " feature_cap="; + int ret; + + buf[0] = '\0'; + + /* + * We expect a feature capability to contain at least one byte to be + * reported. The string buffer provided by the caller function is + * expected to be big enough to contain all bytes of the attribute for + * known specifications. This function truncates the reported bytes if + * the feature capability data exceeds the string buffer size. + */ + if (!feat_cap || !feat_cap_len || buf_len < sizeof(pref) + 2) + return; + + os_memcpy(buf, pref, sizeof(pref)); + ret = wpa_snprintf_hex(&buf[sizeof(pref) - 1], + buf_len - sizeof(pref) + 1, + feat_cap, feat_cap_len); + + if (ret != (2 * (int) feat_cap_len)) + wpa_printf(MSG_WARNING, "P2PS feature_cap bytes truncated"); +} + + static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, const u8 *adv_mac, const u8 *ses_mac, const u8 *grp_mac, u32 adv_id, u32 ses_id, u8 conncap, int passwd_id, const u8 *persist_ssid, size_t persist_ssid_size, int response_done, - int prov_start, const char *session_info) + int prov_start, const char *session_info, + const u8 *feat_cap, size_t feat_cap_len) { struct wpa_supplicant *wpa_s = ctx; u8 mac[ETH_ALEN]; struct wpa_ssid *persistent_go, *stale, *s; int save_config = 0; struct wpa_supplicant *go_wpa_s; + char feat_cap_str[256]; if (!dev) return; @@ -4818,6 +3770,9 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, if (!grp_mac) grp_mac = mac; + wpas_p2ps_get_feat_cap_str(feat_cap_str, sizeof(feat_cap_str), + feat_cap, feat_cap_len); + if (prov_start) { if (session_info == NULL) { wpa_msg_global(wpa_s, MSG_INFO, @@ -4825,22 +3780,22 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, " adv_id=%x conncap=%x" " adv_mac=" MACSTR " session=%x mac=" MACSTR - " dev_passwd_id=%d", + " dev_passwd_id=%d%s", MAC2STR(dev), adv_id, conncap, MAC2STR(adv_mac), ses_id, MAC2STR(ses_mac), - passwd_id); + passwd_id, feat_cap_str); } else { wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_P2PS_PROVISION_START MACSTR " adv_id=%x conncap=%x" " adv_mac=" MACSTR " session=%x mac=" MACSTR - " dev_passwd_id=%d info='%s'", + " dev_passwd_id=%d info='%s'%s", MAC2STR(dev), adv_id, conncap, MAC2STR(adv_mac), ses_id, MAC2STR(ses_mac), - passwd_id, session_info); + passwd_id, session_info, feat_cap_str); } return; } @@ -4862,16 +3817,24 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, P2P_EVENT_P2PS_PROVISION_DONE MACSTR " status=%d" " adv_id=%x adv_mac=" MACSTR - " session=%x mac=" MACSTR, + " session=%x mac=" MACSTR "%s", MAC2STR(dev), status, adv_id, MAC2STR(adv_mac), - ses_id, MAC2STR(ses_mac)); + ses_id, MAC2STR(ses_mac), feat_cap_str); return; } /* Clean up stale persistent groups with this device */ 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)) { + wpa_dbg(wpa_s, MSG_ERROR, + "P2P: Peer device is a GO in a persistent group, but it did not provide the intended MAC address"); + return; + } + for (;;) { stale = wpas_p2p_get_persistent(wpa_s, dev, NULL, 0); if (!stale) @@ -4927,29 +3890,39 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, " status=%d" " adv_id=%x adv_mac=" MACSTR " session=%x mac=" MACSTR - " persist=%d", + " persist=%d%s", MAC2STR(dev), status, adv_id, MAC2STR(adv_mac), - ses_id, MAC2STR(ses_mac), s->id); + ses_id, MAC2STR(ses_mac), s->id, feat_cap_str); return; } if (conncap == P2PS_SETUP_GROUP_OWNER) { - const char *go_ifname = NULL; + /* + * We need to copy the interface name. Simply saving a + * pointer isn't enough, since if we use pending_interface_name + * it will be overwritten when the group is added. + */ + char go_ifname[100]; + + go_ifname[0] = '\0'; if (!go_wpa_s) { wpa_s->global->pending_p2ps_group = 1; - if (wpa_s->conf->p2p_no_group_iface) - go_ifname = wpa_s->ifname; + if (!wpas_p2p_create_iface(wpa_s)) + os_memcpy(go_ifname, wpa_s->ifname, + sizeof(go_ifname)); else if (wpa_s->pending_interface_name[0]) - go_ifname = wpa_s->pending_interface_name; + os_memcpy(go_ifname, + wpa_s->pending_interface_name, + sizeof(go_ifname)); - if (!go_ifname) { + if (!go_ifname[0]) { wpas_p2ps_prov_complete( wpa_s, P2P_SC_FAIL_UNKNOWN_GROUP, dev, adv_mac, ses_mac, - NULL, adv_id, ses_id, 0, 0, - NULL, 0, 0, 0, NULL); + grp_mac, adv_id, ses_id, 0, 0, + NULL, 0, 0, 0, NULL, NULL, 0); return; } @@ -4961,30 +3934,37 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, persistent_go->mode == WPAS_MODE_P2P_GO ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : - 0); + 0, 0); } else if (response_done) { wpas_p2p_group_add(wpa_s, 1, 0, 0, 0); } if (passwd_id == DEV_PW_P2PS_DEFAULT) { - os_memcpy(wpa_s->p2ps_join_addr, dev, ETH_ALEN); - wpa_s->p2ps_join_addr_valid = 1; - wpa_dbg(wpa_s, MSG_DEBUG, - "P2PS: Saving PIN for " MACSTR, - MAC2STR(dev)); + os_memcpy(wpa_s->p2ps_join_addr, grp_mac, + ETH_ALEN); + wpa_s->p2ps_method_config_any = 1; } } else if (passwd_id == DEV_PW_P2PS_DEFAULT) { - go_ifname = go_wpa_s->ifname; - - wpa_dbg(go_wpa_s, MSG_DEBUG, - "P2P: Setting PIN-1 For " MACSTR, MAC2STR(dev)); - wpa_supplicant_ap_wps_pin(go_wpa_s, dev, "12345670", - NULL, 0, 0); + os_memcpy(go_ifname, go_wpa_s->ifname, + sizeof(go_ifname)); + + if (is_zero_ether_addr(grp_mac)) { + wpa_dbg(go_wpa_s, MSG_DEBUG, + "P2P: Setting PIN-1 for ANY"); + wpa_supplicant_ap_wps_pin(go_wpa_s, NULL, + "12345670", NULL, 0, + 0); + } else { + wpa_dbg(go_wpa_s, MSG_DEBUG, + "P2P: Setting PIN-1 for " MACSTR, + MAC2STR(grp_mac)); + wpa_supplicant_ap_wps_pin(go_wpa_s, grp_mac, + "12345670", NULL, 0, + 0); + } - os_memcpy(wpa_s->p2ps_join_addr, dev, ETH_ALEN); - wpa_s->p2ps_join_addr_valid = 1; - wpa_dbg(wpa_s, MSG_DEBUG, - "P2PS: Saving PIN for " MACSTR, MAC2STR(dev)); + os_memcpy(wpa_s->p2ps_join_addr, grp_mac, ETH_ALEN); + wpa_s->p2ps_method_config_any = 1; } wpa_msg_global(wpa_s, MSG_INFO, @@ -4992,11 +3972,11 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, " status=%d conncap=%x" " adv_id=%x adv_mac=" MACSTR " session=%x mac=" MACSTR - " dev_passwd_id=%d go=%s", + " dev_passwd_id=%d go=%s%s", MAC2STR(dev), status, conncap, adv_id, MAC2STR(adv_mac), ses_id, MAC2STR(ses_mac), - passwd_id, go_ifname); + passwd_id, go_ifname, feat_cap_str); return; } @@ -5014,22 +3994,22 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, " status=%d conncap=%x" " adv_id=%x adv_mac=" MACSTR " session=%x mac=" MACSTR - " dev_passwd_id=%d join=" MACSTR, + " dev_passwd_id=%d join=" MACSTR "%s", MAC2STR(dev), status, conncap, adv_id, MAC2STR(adv_mac), ses_id, MAC2STR(ses_mac), - passwd_id, MAC2STR(grp_mac)); + passwd_id, MAC2STR(grp_mac), feat_cap_str); } else { 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", + " dev_passwd_id=%d%s", MAC2STR(dev), status, conncap, adv_id, MAC2STR(adv_mac), ses_id, MAC2STR(ses_mac), - passwd_id); + passwd_id, feat_cap_str); } } @@ -5059,7 +4039,7 @@ static int wpas_prov_disc_resp_cb(void *ctx) wpas_p2p_group_add_persistent( wpa_s, persistent_go, 0, 0, 0, 0, 0, NULL, persistent_go->mode == WPAS_MODE_P2P_GO ? - P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0); + P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0, 0); } else { wpas_p2p_group_add(wpa_s, 1, 0, 0, 0); } @@ -5068,6 +4048,17 @@ static int wpas_prov_disc_resp_cb(void *ctx) } +static int wpas_p2p_get_pref_freq_list(void *ctx, int go, + unsigned int *len, + unsigned int *freq_list) +{ + struct wpa_supplicant *wpa_s = ctx; + + return wpa_drv_get_pref_freq_list(wpa_s, go ? WPA_IF_P2P_GO : + WPA_IF_P2P_CLIENT, len, freq_list); +} + + /** * wpas_p2p_init - Initialize P2P module for %wpa_supplicant * @global: Pointer to global data from wpa_supplicant_init() @@ -5121,6 +4112,7 @@ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s) p2p.p2ps_prov_complete = wpas_p2ps_prov_complete; p2p.prov_disc_resp_cb = wpas_prov_disc_resp_cb; p2p.p2ps_group_capability = p2ps_group_capability; + p2p.get_pref_freq_list = wpas_p2p_get_pref_freq_list; os_memcpy(wpa_s->global->p2p_dev_addr, wpa_s->own_addr, ETH_ALEN); os_memcpy(p2p.dev_addr, wpa_s->global->p2p_dev_addr, ETH_ALEN); @@ -5152,9 +4144,9 @@ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s) */ if (p2p_config_get_random_social(&p2p, &p2p.reg_class, &p2p.channel) != 0) { - wpa_printf(MSG_ERROR, - "P2P: Failed to select random social channel as listen channel"); - return -1; + wpa_printf(MSG_INFO, + "P2P: No social channels supported by the driver - do not enable P2P"); + return 0; } p2p.channel_forced = 0; } @@ -5425,6 +4417,7 @@ static void wpas_p2p_check_join_scan_limit(struct wpa_supplicant *wpa_s) } wpa_msg_global(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_FORMATION_FAILURE); + wpas_notify_p2p_group_formation_failure(wpa_s, ""); } } @@ -5623,10 +4616,25 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, wpa_s->pending_join_iface_addr); } if (bss) { + u8 dev_addr[ETH_ALEN]; + freq = bss->freq; wpa_printf(MSG_DEBUG, "P2P: Target GO operating frequency " "from BSS table: %d MHz (SSID %s)", freq, wpa_ssid_txt(bss->ssid, bss->ssid_len)); + if (p2p_parse_dev_addr((const u8 *) (bss + 1), bss->ie_len, + dev_addr) == 0 && + os_memcmp(wpa_s->pending_join_dev_addr, + wpa_s->pending_join_iface_addr, ETH_ALEN) == 0 && + os_memcmp(dev_addr, wpa_s->pending_join_dev_addr, + ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, + "P2P: Update target GO device address based on BSS entry: " MACSTR " (was " MACSTR ")", + MAC2STR(dev_addr), + MAC2STR(wpa_s->pending_join_dev_addr)); + os_memcpy(wpa_s->pending_join_dev_addr, dev_addr, + ETH_ALEN); + } } if (freq > 0) { u16 method; @@ -5635,6 +4643,8 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, wpa_msg_global(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_FORMATION_FAILURE "reason=FREQ_CONFLICT"); + wpas_notify_p2p_group_formation_failure( + wpa_s, "FREQ_CONFLICT"); return; } @@ -5654,6 +4664,9 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, case WPS_PBC: method = WPS_CONFIG_PUSHBUTTON; break; + case WPS_P2PS: + method = WPS_CONFIG_P2PS; + break; default: method = 0; break; @@ -5896,11 +4909,16 @@ static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s, int freq, static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq, - int *force_freq, int *pref_freq, int go) + int *force_freq, int *pref_freq, int go, + unsigned int *pref_freq_list, + unsigned int *num_pref_freq) { struct wpa_used_freq_data *freqs; int res, best_freq, num_unused; - unsigned int freq_in_use = 0, num, i; + unsigned int freq_in_use = 0, num, i, max_pref_freq; + + max_pref_freq = *num_pref_freq; + *num_pref_freq = 0; freqs = os_calloc(wpa_s->num_multichan_concurrent, sizeof(struct wpa_used_freq_data)); @@ -5965,6 +4983,47 @@ static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq, best_freq = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num); + if (!wpa_s->conf->num_p2p_pref_chan && *pref_freq == 0) { + enum wpa_driver_if_type iface_type; + + if (go) + iface_type = WPA_IF_P2P_GO; + else + iface_type = WPA_IF_P2P_CLIENT; + + wpa_printf(MSG_DEBUG, "P2P: best_freq=%d, go=%d", + best_freq, go); + + res = wpa_drv_get_pref_freq_list(wpa_s, iface_type, + &max_pref_freq, + pref_freq_list); + if (!res && max_pref_freq > 0) { + *num_pref_freq = max_pref_freq; + i = 0; + while (wpas_p2p_disallowed_freq(wpa_s->global, + pref_freq_list[i]) && + i < *num_pref_freq) { + wpa_printf(MSG_DEBUG, + "P2P: preferred_freq_list[%d]=%d is disallowed", + i, pref_freq_list[i]); + i++; + } + if (i != *num_pref_freq) { + best_freq = pref_freq_list[i]; + wpa_printf(MSG_DEBUG, + "P2P: Using preferred_freq_list[%d]=%d", + i, best_freq); + } else { + wpa_printf(MSG_DEBUG, + "P2P: All driver preferred frequencies are disallowed for P2P use"); + *num_pref_freq = 0; + } + } else { + wpa_printf(MSG_DEBUG, + "P2P: No preferred frequency list available"); + } + } + /* We have a candidate frequency to use */ if (best_freq > 0) { if (*pref_freq == 0 && num_unused > 0) { @@ -6029,6 +5088,7 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, enum wpa_driver_if_type iftype; const u8 *if_addr; struct wpa_ssid *ssid = NULL; + unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size; if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; @@ -6045,6 +5105,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->p2ps_method_config_any = 0; if (go_intent < 0) go_intent = wpa_s->conf->p2p_go_intent; @@ -6105,13 +5166,16 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, return ret; } + size = P2P_MAX_PREF_CHANNELS; res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq, - go_intent == 15); + go_intent == 15, pref_freq_list, &size); if (res) return res; wpas_p2p_set_own_freq_preference(wpa_s, force_freq ? force_freq : pref_freq); + p2p_set_own_pref_freq_list(wpa_s->global->p2p, pref_freq_list, size); + wpa_s->create_p2p_iface = wpas_p2p_create_iface(wpa_s); if (wpa_s->create_p2p_iface) { @@ -6126,8 +5190,10 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, } if_addr = wpa_s->pending_interface_addr; - } else + } else { if_addr = wpa_s->own_addr; + os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN); + } if (auth) { if (wpas_p2p_auth_go_neg(wpa_s, peer_addr, wps_method, @@ -6272,6 +5338,38 @@ static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq) { unsigned int r; + if (!wpa_s->conf->num_p2p_pref_chan && !freq) { + unsigned int i, size = P2P_MAX_PREF_CHANNELS; + unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS]; + int res; + + res = wpa_drv_get_pref_freq_list(wpa_s, WPA_IF_P2P_GO, + &size, pref_freq_list); + if (!res && size > 0) { + i = 0; + while (wpas_p2p_disallowed_freq(wpa_s->global, + pref_freq_list[i]) && + i < size) { + wpa_printf(MSG_DEBUG, + "P2P: preferred_freq_list[%d]=%d is disallowed", + i, pref_freq_list[i]); + i++; + } + if (i != size) { + freq = pref_freq_list[i]; + wpa_printf(MSG_DEBUG, + "P2P: Using preferred_freq_list[%d]=%d", + i, freq); + } else { + wpa_printf(MSG_DEBUG, + "P2P: All driver preferred frequencies are disallowed for P2P use"); + } + } else { + wpa_printf(MSG_DEBUG, + "P2P: No preferred frequency list available"); + } + } + if (freq == 2) { wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 2.4 GHz " "band"); @@ -6335,30 +5433,45 @@ static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq) } -static int wpas_p2p_select_freq_no_pref(struct wpa_supplicant *wpa_s, - struct p2p_go_neg_results *params, - const struct p2p_channels *channels) +static int wpas_p2p_supported_freq_go(struct wpa_supplicant *wpa_s, + const struct p2p_channels *channels, + int freq) +{ + if (!wpas_p2p_disallowed_freq(wpa_s->global, freq) && + p2p_supported_freq_go(wpa_s->global->p2p, freq) && + freq_included(wpa_s, channels, freq)) + return 1; + return 0; +} + + +static void wpas_p2p_select_go_freq_no_pref(struct wpa_supplicant *wpa_s, + struct p2p_go_neg_results *params, + const struct p2p_channels *channels) { unsigned int i, r; /* first try some random selection of the social channels */ if (os_get_random((u8 *) &r, sizeof(r)) < 0) - return -1; + return; for (i = 0; i < 3; i++) { params->freq = 2412 + ((r + i) % 3) * 25; - if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) && - freq_included(channels, params->freq) && - p2p_supported_freq(wpa_s->global->p2p, params->freq)) + if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq)) goto out; } - /* try all channels in reg. class 81 */ + /* try all other channels in operating class 81 */ for (i = 0; i < 11; i++) { params->freq = 2412 + i * 5; - if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) && - freq_included(channels, params->freq) && - p2p_supported_freq(wpa_s->global->p2p, params->freq)) + + /* skip social channels; covered in the previous loop */ + if (params->freq == 2412 || + params->freq == 2437 || + params->freq == 2462) + continue; + + if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq)) goto out; } @@ -6366,7 +5479,7 @@ static int wpas_p2p_select_freq_no_pref(struct wpa_supplicant *wpa_s, for (i = 0; i < 4; i++) { params->freq = 5180 + i * 20; if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) && - freq_included(channels, params->freq) && + freq_included(wpa_s, channels, params->freq) && p2p_supported_freq(wpa_s->global->p2p, params->freq)) goto out; } @@ -6375,7 +5488,7 @@ static int wpas_p2p_select_freq_no_pref(struct wpa_supplicant *wpa_s, for (i = 0; i < 4; i++) { params->freq = 5745 + i * 20; if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) && - freq_included(channels, params->freq) && + freq_included(wpa_s, channels, params->freq) && p2p_supported_freq(wpa_s->global->p2p, params->freq)) goto out; } @@ -6383,7 +5496,7 @@ static int wpas_p2p_select_freq_no_pref(struct wpa_supplicant *wpa_s, /* try social channel class 180 channel 2 */ params->freq = 58320 + 1 * 2160; if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) && - freq_included(channels, params->freq) && + freq_included(wpa_s, channels, params->freq) && p2p_supported_freq(wpa_s->global->p2p, params->freq)) goto out; @@ -6391,17 +5504,17 @@ static int wpas_p2p_select_freq_no_pref(struct wpa_supplicant *wpa_s, for (i = 0; i < 4; i++) { params->freq = 58320 + i * 2160; if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) && - freq_included(channels, params->freq) && + freq_included(wpa_s, channels, params->freq) && p2p_supported_freq(wpa_s->global->p2p, params->freq)) goto out; } + params->freq = 0; wpa_printf(MSG_DEBUG, "P2P: No 2.4, 5, or 60 GHz channel allowed"); - return -1; + return; out: wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz (no preference known)", params->freq); - return 0; } @@ -6411,129 +5524,186 @@ static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s, const struct p2p_channels *channels) { struct wpa_used_freq_data *freqs; - unsigned int pref_freq, cand_freq; + unsigned int cand; unsigned int num, i; 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__); + + 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); + + /* try using the forced freq */ if (freq) { - if (!freq_included(channels, freq)) { - wpa_printf(MSG_DEBUG, "P2P: Forced GO freq %d MHz not " - "accepted", freq); - return -1; + if (!wpas_p2p_supported_freq_go(wpa_s, channels, freq)) { + wpa_printf(MSG_DEBUG, + "P2P: Forced GO freq %d MHz not accepted", + freq); + goto fail; + } + + for (i = 0; i < num; i++) { + if (freqs[i].freq == freq) { + wpa_printf(MSG_DEBUG, + "P2P: forced freq (%d MHz) is also shared", + freq); + params->freq = freq; + goto success; + } + } + + if (wpas_p2p_num_unused_channels(wpa_s) <= 0) { + wpa_printf(MSG_DEBUG, + "P2P: Cannot force GO on freq (%d MHz) as all the channels are in use", + freq); + goto fail; } - wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on forced " - "frequency %d MHz", freq); + + wpa_printf(MSG_DEBUG, + "P2P: force GO freq (%d MHz) on a free channel", + freq); params->freq = freq; - } else if (wpa_s->conf->p2p_oper_reg_class == 81 && - wpa_s->conf->p2p_oper_channel >= 1 && - wpa_s->conf->p2p_oper_channel <= 11 && - freq_included(channels, - 2407 + 5 * wpa_s->conf->p2p_oper_channel)) { + goto success; + } + + /* consider using one of the shared frequencies */ + if (num) { + 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); + params->freq = cand; + goto success; + } + + /* try using one of the shared freqs */ + for (i = 0; i < num; i++) { + if (wpas_p2p_supported_freq_go(wpa_s, channels, + freqs[i].freq)) { + wpa_printf(MSG_DEBUG, + "P2P: Use shared freq (%d MHz) for GO", + freq); + params->freq = freqs[i].freq; + goto success; + } + } + } + + if (wpas_p2p_num_unused_channels(wpa_s) <= 0) { + wpa_printf(MSG_DEBUG, + "P2P: Cannot force GO on any of the channels we are already using"); + goto fail; + } + + /* try using the setting from the configuration file */ + if (wpa_s->conf->p2p_oper_reg_class == 81 && + wpa_s->conf->p2p_oper_channel >= 1 && + wpa_s->conf->p2p_oper_channel <= 11 && + wpas_p2p_supported_freq_go( + wpa_s, channels, + 2407 + 5 * wpa_s->conf->p2p_oper_channel)) { params->freq = 2407 + 5 * wpa_s->conf->p2p_oper_channel; wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured " "frequency %d MHz", params->freq); - } else if ((wpa_s->conf->p2p_oper_reg_class == 115 || - wpa_s->conf->p2p_oper_reg_class == 116 || - wpa_s->conf->p2p_oper_reg_class == 117 || - wpa_s->conf->p2p_oper_reg_class == 124 || - wpa_s->conf->p2p_oper_reg_class == 126 || - wpa_s->conf->p2p_oper_reg_class == 127) && - freq_included(channels, - 5000 + 5 * wpa_s->conf->p2p_oper_channel)) { + goto success; + } + + if ((wpa_s->conf->p2p_oper_reg_class == 115 || + wpa_s->conf->p2p_oper_reg_class == 116 || + wpa_s->conf->p2p_oper_reg_class == 117 || + wpa_s->conf->p2p_oper_reg_class == 124 || + wpa_s->conf->p2p_oper_reg_class == 125 || + wpa_s->conf->p2p_oper_reg_class == 126 || + wpa_s->conf->p2p_oper_reg_class == 127) && + wpas_p2p_supported_freq_go(wpa_s, channels, + 5000 + + 5 * wpa_s->conf->p2p_oper_channel)) { params->freq = 5000 + 5 * wpa_s->conf->p2p_oper_channel; wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured " "frequency %d MHz", params->freq); - } else if (wpa_s->conf->p2p_oper_channel == 0 && - wpa_s->best_overall_freq > 0 && - p2p_supported_freq_go(wpa_s->global->p2p, - wpa_s->best_overall_freq) && - freq_included(channels, wpa_s->best_overall_freq)) { + goto success; + } + + /* Try using best channels */ + if (wpa_s->conf->p2p_oper_channel == 0 && + wpa_s->best_overall_freq > 0 && + wpas_p2p_supported_freq_go(wpa_s, channels, + wpa_s->best_overall_freq)) { params->freq = wpa_s->best_overall_freq; wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best overall " "channel %d MHz", params->freq); - } else if (wpa_s->conf->p2p_oper_channel == 0 && - wpa_s->best_24_freq > 0 && - p2p_supported_freq_go(wpa_s->global->p2p, - wpa_s->best_24_freq) && - freq_included(channels, wpa_s->best_24_freq)) { + goto success; + } + + if (wpa_s->conf->p2p_oper_channel == 0 && + wpa_s->best_24_freq > 0 && + wpas_p2p_supported_freq_go(wpa_s, channels, + wpa_s->best_24_freq)) { params->freq = wpa_s->best_24_freq; wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 2.4 GHz " "channel %d MHz", params->freq); - } else if (wpa_s->conf->p2p_oper_channel == 0 && - wpa_s->best_5_freq > 0 && - p2p_supported_freq_go(wpa_s->global->p2p, - wpa_s->best_5_freq) && - freq_included(channels, wpa_s->best_5_freq)) { + goto success; + } + + if (wpa_s->conf->p2p_oper_channel == 0 && + wpa_s->best_5_freq > 0 && + wpas_p2p_supported_freq_go(wpa_s, channels, + wpa_s->best_5_freq)) { params->freq = wpa_s->best_5_freq; wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 5 GHz " "channel %d MHz", params->freq); - } else if ((pref_freq = p2p_get_pref_freq(wpa_s->global->p2p, - channels))) { - params->freq = pref_freq; + goto success; + } + + /* try using preferred channels */ + cand = p2p_get_pref_freq(wpa_s->global->p2p, channels); + if (cand && wpas_p2p_supported_freq_go(wpa_s, channels, cand)) { + params->freq = cand; wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz from preferred " "channels", params->freq); - } else { - /* no preference, select some channel */ - if (wpas_p2p_select_freq_no_pref(wpa_s, params, channels) < 0) - return -1; + goto success; } - 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); - - cand_freq = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num); - - /* First try the best used frequency if possible */ - if (!freq && cand_freq > 0 && freq_included(channels, cand_freq)) { - params->freq = cand_freq; - } else if (!freq) { - /* Try any of the used frequencies */ - for (i = 0; i < num; i++) { - if (freq_included(channels, freqs[i].freq)) { - wpa_printf(MSG_DEBUG, "P2P: Force GO on a channel we are already using (%u MHz)", - freqs[i].freq); - params->freq = freqs[i].freq; - break; + /* Try using one of the group common freqs */ + if (wpa_s->p2p_group_common_freqs) { + for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) { + cand = wpa_s->p2p_group_common_freqs[i]; + if (wpas_p2p_supported_freq_go(wpa_s, channels, cand)) { + params->freq = cand; + wpa_printf(MSG_DEBUG, + "P2P: Use freq %d MHz common with the peer", + params->freq); + goto success; } } + } - if (i == num) { - if (wpas_p2p_num_unused_channels(wpa_s) <= 0) { - wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on any of the channels we are already using"); - os_free(freqs); - return -1; - } else { - wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on any of the channels we are already using. Use one of the free channels"); - } - } - } else { - for (i = 0; i < num; i++) { - if (freqs[i].freq == freq) - break; - } + /* no preference, select some channel */ + wpas_p2p_select_go_freq_no_pref(wpa_s, params, channels); - if (i == num) { - if (wpas_p2p_num_unused_channels(wpa_s) <= 0) { - if (freq) - wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on freq (%u MHz) as all the channels are in use", freq); - os_free(freqs); - return -1; - } else { - wpa_printf(MSG_DEBUG, "P2P: Use one of the free channels"); - } - } + if (params->freq == 0) { + wpa_printf(MSG_DEBUG, "P2P: did not find a freq for GO use"); + goto fail; } +success: os_free(freqs); return 0; +fail: + os_free(freqs); + return -1; } @@ -6636,13 +5806,15 @@ int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group, static int wpas_start_p2p_client(struct wpa_supplicant *wpa_s, struct wpa_ssid *params, int addr_allocated, - int freq) + int freq, int force_scan) { struct wpa_ssid *ssid; wpa_s = wpas_p2p_get_group_iface(wpa_s, addr_allocated, 0); if (wpa_s == NULL) return -1; + if (force_scan) + os_get_reltime(&wpa_s->scan_min_time); wpa_s->p2p_last_4way_hs_fail = NULL; wpa_supplicant_ap_deinit(wpa_s); @@ -6650,6 +5822,7 @@ static int wpas_start_p2p_client(struct wpa_supplicant *wpa_s, ssid = wpa_config_add_network(wpa_s->conf); if (ssid == NULL) return -1; + os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN); wpa_config_set_network_defaults(ssid); ssid->temporary = 1; ssid->proto = WPA_PROTO_RSN; @@ -6691,7 +5864,7 @@ 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 connection_timeout) + int connection_timeout, int force_scan) { struct p2p_go_neg_results params; int go = 0, freq; @@ -6703,6 +5876,23 @@ int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, go == (ssid->mode == WPAS_MODE_P2P_GO)) { wpa_printf(MSG_DEBUG, "P2P: Requested persistent group is " "already running"); + if (go == 0 && + eloop_cancel_timeout(wpas_p2p_group_formation_timeout, + wpa_s->parent, NULL)) { + /* + * This can happen if Invitation Response frame was lost + * and the peer (GO of a persistent group) tries to + * invite us again. Reschedule the timeout to avoid + * terminating the wait for the connection too early + * since we now know that the peer is still trying to + * invite us instead of having already started the GO. + */ + wpa_printf(MSG_DEBUG, + "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); + } return 0; } @@ -6722,21 +5912,30 @@ int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, } else { freq = wpas_p2p_select_go_freq(wpa_s, neg_freq); if (freq < 0 || - (freq > 0 && !freq_included(channels, freq))) + (freq > 0 && !freq_included(wpa_s, channels, freq))) freq = 0; } - } else { + } else if (ssid->mode == WPAS_MODE_INFRA) { freq = neg_freq; - if (freq < 0 || - (freq > 0 && !freq_included(channels, freq))) - freq = 0; - } - - if (ssid->mode == WPAS_MODE_INFRA) - return wpas_start_p2p_client(wpa_s, ssid, addr_allocated, freq); + if (freq <= 0 || !freq_included(wpa_s, channels, freq)) { + struct os_reltime now; + struct wpa_bss *bss = + wpa_bss_get_p2p_dev_addr(wpa_s, ssid->bssid); + + os_get_reltime(&now); + if (bss && + !os_reltime_expired(&now, &bss->last_update, 5) && + freq_included(wpa_s, channels, bss->freq)) + freq = bss->freq; + else + freq = 0; + } - if (ssid->mode != WPAS_MODE_P2P_GO) + return wpas_start_p2p_client(wpa_s, ssid, addr_allocated, freq, + force_scan); + } else { return -1; + } if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq, ht40, vht, channels)) return -1; @@ -6909,7 +6108,7 @@ void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr, } if (wpa_s->global->p2p) p2p_wps_success_cb(wpa_s->global->p2p, peer_addr); - wpas_group_formation_completed(wpa_s, 1); + wpas_group_formation_completed(wpa_s, 1, 0); } @@ -7163,7 +6362,11 @@ int wpas_p2p_assoc_req_ie(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, if (wpa_s->global->p2p_disabled) return -1; - if (wpa_s->conf->p2p_disabled) + /* + * Advertize mandatory cross connection capability even on + * p2p_disabled=1 interface when associating with a P2P Manager WLAN AP. + */ + if (wpa_s->conf->p2p_disabled && p2p_group) return -1; if (wpa_s->global->p2p == NULL) return -1; @@ -7181,7 +6384,8 @@ int wpas_p2p_assoc_req_ie(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr, const u8 *dst, const u8 *bssid, - const u8 *ie, size_t ie_len, int ssi_signal) + const u8 *ie, size_t ie_len, + unsigned int rx_freq, int ssi_signal) { if (wpa_s->global->p2p_disabled) return 0; @@ -7189,7 +6393,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)) { + ie, ie_len, rx_freq)) { case P2P_PREQ_NOT_P2P: wpas_notify_preq(wpa_s, addr, dst, bssid, ie, ie_len, ssi_signal); @@ -7263,6 +6467,7 @@ int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr, int force_freq = 0; int res; int no_pref_freq_given = pref_freq == 0; + unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size; wpa_s->global->p2p_invite_group = NULL; if (peer_addr) @@ -7296,10 +6501,13 @@ int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr, } wpa_s->pending_invite_ssid_id = ssid->id; + size = P2P_MAX_PREF_CHANNELS; res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq, - role == P2P_INVITE_ROLE_GO); + role == P2P_INVITE_ROLE_GO, + 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; @@ -7336,6 +6544,7 @@ int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname, int persistent; int freq = 0, force_freq = 0, pref_freq = 0; int res; + unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size; wpa_s->p2p_persistent_go_freq = 0; wpa_s->p2p_go_ht40 = 0; @@ -7387,8 +6596,10 @@ int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname, if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; + size = P2P_MAX_PREF_CHANNELS; res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq, - role == P2P_INVITE_ROLE_ACTIVE_GO); + role == P2P_INVITE_ROLE_ACTIVE_GO, + pref_freq_list, &size); if (res) return res; wpas_p2p_set_own_freq_preference(wpa_s, force_freq); @@ -7921,7 +7132,7 @@ int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s) "session overlap"); if (wpa_s != wpa_s->parent) wpa_msg_ctrl(wpa_s->parent, MSG_INFO, WPS_EVENT_OVERLAP); - wpas_p2p_group_formation_failed(wpa_s); + wpas_p2p_group_formation_failed(wpa_s, 0); return 1; } @@ -7933,14 +7144,22 @@ void wpas_p2p_pbc_overlap_cb(void *eloop_ctx, void *timeout_ctx) } -void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s) +void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s, + enum wpas_p2p_channel_update_trig trig) { struct p2p_channels chan, cli_chan; - struct wpa_supplicant *ifs; + struct wpa_used_freq_data *freqs = NULL; + unsigned int num = wpa_s->num_multichan_concurrent; if (wpa_s->global == NULL || wpa_s->global->p2p == NULL) return; + freqs = os_calloc(num, sizeof(struct wpa_used_freq_data)); + if (!freqs) + return; + + num = get_shared_radio_freqs_data(wpa_s, freqs, num); + os_memset(&chan, 0, sizeof(chan)); os_memset(&cli_chan, 0, sizeof(cli_chan)); if (wpas_p2p_setup_channels(wpa_s, &chan, &cli_chan)) { @@ -7951,27 +7170,17 @@ void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s) p2p_update_channel_list(wpa_s->global->p2p, &chan, &cli_chan); - for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) { - int freq; - if (!ifs->current_ssid || - !ifs->current_ssid->p2p_group || - (ifs->current_ssid->mode != WPAS_MODE_P2P_GO && - ifs->current_ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION)) - continue; - freq = ifs->current_ssid->frequency; - if (freq_included(&chan, freq)) { - wpa_dbg(ifs, MSG_DEBUG, - "P2P GO operating frequency %d MHz in valid range", - freq); - continue; - } + wpas_p2p_optimize_listen_channel(wpa_s, freqs, num); - wpa_dbg(ifs, MSG_DEBUG, - "P2P GO operating in invalid frequency %d MHz", freq); - /* TODO: Consider using CSA or removing the group within - * wpa_supplicant */ - wpa_msg(ifs, MSG_INFO, P2P_EVENT_REMOVE_AND_REFORM_GROUP); - } + /* + * The used frequencies map changed, so it is possible that a GO is + * using a channel that is no longer valid for P2P use. It is also + * possible that due to policy consideration, it would be preferable to + * move it to a frequency already used by other station interfaces. + */ + wpas_p2p_consider_moving_gos(wpa_s, freqs, num, trig); + + os_free(freqs); } @@ -8031,7 +7240,7 @@ int wpas_p2p_cancel(struct wpa_supplicant *wpa_s) eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->parent, NULL); if (wpa_s->p2p_in_provisioning) { - wpas_group_formation_completed(wpa_s, 0); + wpas_group_formation_completed(wpa_s, 0, 0); break; } wpas_p2p_group_delete(wpa_s, @@ -8041,7 +7250,7 @@ int wpas_p2p_cancel(struct wpa_supplicant *wpa_s) wpa_printf(MSG_DEBUG, "P2P: Interface %s in invitation found - cancelling", wpa_s->ifname); found = 1; - wpas_p2p_group_formation_failed(wpa_s); + wpas_p2p_group_formation_failed(wpa_s, 0); } } @@ -8237,7 +7446,7 @@ void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s, */ if (wpa_s->global->p2p) p2p_wps_success_cb(wpa_s->global->p2p, addr); - wpas_group_formation_completed(wpa_s, 1); + wpas_group_formation_completed(wpa_s, 1, 0); } } if (!wpa_s->p2p_go_group_formation_completed) { @@ -8262,7 +7471,7 @@ static int wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s, if (wpa_s->global->p2p_group_formation) group = wpa_s->global->p2p_group_formation; - wpa_s = wpa_s->parent; + wpa_s = wpa_s->global->p2p_init_wpa_s; offchannel_send_action_done(wpa_s); if (group_added) ret = wpas_p2p_group_delete(group, P2P_GROUP_REMOVAL_SILENT); @@ -8517,16 +7726,17 @@ void wpas_p2p_remove_client(struct wpa_supplicant *wpa_s, const u8 *peer, { struct wpa_ssid *s; struct wpa_supplicant *w; + struct wpa_supplicant *p2p_wpa_s = wpa_s->global->p2p_init_wpa_s; wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Remove client " MACSTR, MAC2STR(peer)); /* Remove from any persistent group */ - for (s = wpa_s->parent->conf->ssid; s; s = s->next) { + for (s = p2p_wpa_s->conf->ssid; s; s = s->next) { if (s->disabled != 2 || s->mode != WPAS_MODE_P2P_GO) continue; if (!iface_addr) - wpas_remove_persistent_peer(wpa_s, s, peer, 0); - wpas_p2p_remove_psk(wpa_s->parent, s, peer, iface_addr); + wpas_remove_persistent_peer(p2p_wpa_s, s, peer, 0); + wpas_p2p_remove_psk(p2p_wpa_s, s, peer, iface_addr); } /* Remove from any operating group */ @@ -9230,6 +8440,16 @@ static void wpas_p2p_optimize_listen_channel(struct wpa_supplicant *wpa_s, u8 curr_chan, cand, chan; unsigned int i; + /* + * If possible, optimize the Listen channel to be a channel that is + * already used by one of the other interfaces. + */ + if (!wpa_s->conf->p2p_optimize_listen_chan) + return; + + if (!wpa_s->current_ssid || wpa_s->wpa_state != WPA_COMPLETED) + return; + curr_chan = p2p_get_listen_channel(wpa_s->global->p2p); for (i = 0, cand = 0; i < num; i++) { ieee80211_freq_to_chan(freqs[i].freq, &chan); @@ -9251,35 +8471,245 @@ static void wpas_p2p_optimize_listen_channel(struct wpa_supplicant *wpa_s, } -void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s) +static int wpas_p2p_move_go_csa(struct wpa_supplicant *wpa_s) { - struct wpa_used_freq_data *freqs; - unsigned int num = wpa_s->num_multichan_concurrent; + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP_CSA)) { + wpa_dbg(wpa_s, MSG_DEBUG, "CSA is not enabled"); + return -1; + } - if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + /* TODO: Add CSA support */ + wpa_dbg(wpa_s, MSG_DEBUG, "Moving GO with CSA is not implemented"); + return -1; +} + + +static void wpas_p2p_move_go_no_csa(struct wpa_supplicant *wpa_s) +{ + struct p2p_go_neg_results params; + struct wpa_ssid *current_ssid = wpa_s->current_ssid; + + wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_REMOVE_AND_REFORM_GROUP); + + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Move GO from freq=%d MHz", + current_ssid->frequency); + + /* Stop the AP functionality */ + /* TODO: Should do this in a way that does not indicated to possible + * P2P Clients in the group that the group is terminated. */ + wpa_supplicant_ap_deinit(wpa_s); + + /* Reselect the GO frequency */ + if (wpas_p2p_init_go_params(wpa_s, ¶ms, 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); return; + } + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: New freq selected for the GO (%u MHz)", + params.freq); - /* - * If possible, optimize the Listen channel to be a channel that is - * already used by one of the other interfaces. - */ - if (!wpa_s->conf->p2p_optimize_listen_chan) + if (params.freq && + !p2p_supported_freq_go(wpa_s->global->p2p, params.freq)) { + wpa_printf(MSG_DEBUG, + "P2P: Selected freq (%u MHz) is not valid for P2P", + params.freq); + wpas_p2p_group_delete(wpa_s, + P2P_GROUP_REMOVAL_GO_LEAVE_CHANNEL); return; + } - if (!wpa_s->current_ssid || wpa_s->wpa_state != WPA_COMPLETED) + /* Update the frequency */ + current_ssid->frequency = params.freq; + wpa_s->connect_without_scan = current_ssid; + wpa_s->reassociate = 1; + wpa_s->disconnected = 0; + wpa_supplicant_req_scan(wpa_s, 0, 0); +} + + +static void wpas_p2p_move_go(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + + if (!wpa_s->ap_iface || !wpa_s->current_ssid) return; + wpas_p2p_go_update_common_freqs(wpa_s); + + /* + * First, try a channel switch flow. If it is not supported or fails, + * take down the GO and bring it up again. + */ + if (wpas_p2p_move_go_csa(wpa_s) < 0) + wpas_p2p_move_go_no_csa(wpa_s); +} + + +static void wpas_p2p_reconsider_moving_go(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct wpa_used_freq_data *freqs = NULL; + unsigned int num = wpa_s->num_multichan_concurrent; + freqs = os_calloc(num, sizeof(struct wpa_used_freq_data)); if (!freqs) return; num = get_shared_radio_freqs_data(wpa_s, freqs, num); - wpas_p2p_optimize_listen_channel(wpa_s, freqs, num); + /* Previous attempt to move a GO was not possible -- try again. */ + wpas_p2p_consider_moving_gos(wpa_s, freqs, num, + WPAS_P2P_CHANNEL_UPDATE_ANY); + os_free(freqs); } +/* + * Consider moving a GO from its currently used frequency: + * 1. It is possible that due to regulatory consideration the frequency + * can no longer be used and there is a need to evacuate the GO. + * 2. It is possible that due to MCC considerations, it would be preferable + * to move the GO to a channel that is currently used by some other + * station interface. + * + * In case a frequency that became invalid is once again valid, cancel a + * previously initiated GO frequency change. + */ +static void wpas_p2p_consider_moving_one_go(struct wpa_supplicant *wpa_s, + struct wpa_used_freq_data *freqs, + unsigned int num) +{ + unsigned int i, invalid_freq = 0, policy_move = 0, flags = 0; + unsigned int timeout; + int freq; + + wpas_p2p_go_update_common_freqs(wpa_s); + + freq = wpa_s->current_ssid->frequency; + for (i = 0, invalid_freq = 0; i < num; i++) { + if (freqs[i].freq == freq) { + flags = freqs[i].flags; + + /* The channel is invalid, must change it */ + if (!p2p_supported_freq_go(wpa_s->global->p2p, freq)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Freq=%d MHz no longer valid for GO", + freq); + invalid_freq = 1; + } + } else if (freqs[i].flags == 0) { + /* Freq is not used by any other station interface */ + continue; + } else if (!p2p_supported_freq(wpa_s->global->p2p, + freqs[i].freq)) { + /* Freq is not valid for P2P use cases */ + continue; + } else if (wpa_s->conf->p2p_go_freq_change_policy == + P2P_GO_FREQ_MOVE_SCM) { + policy_move = 1; + } else if (wpa_s->conf->p2p_go_freq_change_policy == + P2P_GO_FREQ_MOVE_SCM_PEER_SUPPORTS && + wpas_p2p_go_is_peer_freq(wpa_s, freqs[i].freq)) { + policy_move = 1; + } + } + + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: GO move: invalid_freq=%u, policy_move=%u, flags=0x%X", + invalid_freq, policy_move, flags); + + /* + * The channel is valid, or we are going to have a policy move, so + * cancel timeout. + */ + if (!invalid_freq || policy_move) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Cancel a GO move from freq=%d MHz", freq); + eloop_cancel_timeout(wpas_p2p_move_go, wpa_s, NULL); + + if (wpas_p2p_in_progress(wpa_s)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: GO move: policy CS is not allowed - setting timeout to re-consider GO move"); + eloop_cancel_timeout(wpas_p2p_reconsider_moving_go, + wpa_s, NULL); + eloop_register_timeout(P2P_RECONSIDER_GO_MOVE_DELAY, 0, + wpas_p2p_reconsider_moving_go, + wpa_s, NULL); + return; + } + } + + if (!invalid_freq && (!policy_move || flags != 0)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Not initiating a GO frequency change"); + return; + } + + if (invalid_freq && !wpas_p2p_disallowed_freq(wpa_s->global, freq)) + timeout = P2P_GO_FREQ_CHANGE_TIME; + else + timeout = 0; + + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Move GO from freq=%d MHz in %d secs", + freq, timeout); + eloop_cancel_timeout(wpas_p2p_move_go, wpa_s, NULL); + eloop_register_timeout(timeout, 0, wpas_p2p_move_go, wpa_s, NULL); +} + + +static void wpas_p2p_consider_moving_gos(struct wpa_supplicant *wpa_s, + struct wpa_used_freq_data *freqs, + unsigned int num, + enum wpas_p2p_channel_update_trig trig) +{ + struct wpa_supplicant *ifs; + + eloop_cancel_timeout(wpas_p2p_reconsider_moving_go, ELOOP_ALL_CTX, + NULL); + + /* + * Travers all the radio interfaces, and for each GO interface, check + * if there is a need to move the GO from the frequency it is using, + * or in case the frequency is valid again, cancel the evacuation flow. + */ + dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant, + radio_list) { + if (ifs->current_ssid == NULL || + ifs->current_ssid->mode != WPAS_MODE_P2P_GO) + continue; + + /* + * The GO was just started or completed channel switch, no need + * to move it. + */ + if (wpa_s == ifs && + (trig == WPAS_P2P_CHANNEL_UPDATE_STATE_CHANGE || + trig == WPAS_P2P_CHANNEL_UPDATE_CS)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: GO move - schedule re-consideration"); + eloop_register_timeout(P2P_RECONSIDER_GO_MOVE_DELAY, 0, + wpas_p2p_reconsider_moving_go, + wpa_s, NULL); + continue; + } + + wpas_p2p_consider_moving_one_go(ifs, freqs, num); + } +} + + +void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return; + + wpas_p2p_update_channel_list(wpa_s, + WPAS_P2P_CHANNEL_UPDATE_STATE_CHANGE); +} + + void wpas_p2p_deinit_iface(struct wpa_supplicant *wpa_s) { if (wpa_s == wpa_s->global->p2p_init_wpa_s && wpa_s->global->p2p) { diff --git a/contrib/wpa/wpa_supplicant/p2p_supplicant.h b/contrib/wpa/wpa_supplicant/p2p_supplicant.h index b786178..56e6834 100644 --- a/contrib/wpa/wpa_supplicant/p2p_supplicant.h +++ b/contrib/wpa/wpa_supplicant/p2p_supplicant.h @@ -17,6 +17,15 @@ struct p2p_channels; struct wps_event_fail; struct p2ps_provision; +enum wpas_p2p_channel_update_trig { + WPAS_P2P_CHANNEL_UPDATE_ANY, + WPAS_P2P_CHANNEL_UPDATE_DRIVER, + WPAS_P2P_CHANNEL_UPDATE_STATE_CHANGE, + WPAS_P2P_CHANNEL_UPDATE_AVOID, + WPAS_P2P_CHANNEL_UPDATE_DISALLOW, + WPAS_P2P_CHANNEL_UPDATE_CS, +}; + int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s, const char *conf_p2p_dev); struct wpa_supplicant * wpas_get_p2p_go_iface(struct wpa_supplicant *wpa_s, @@ -36,7 +45,7 @@ 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 connection_timeout); + int connection_timeout, int force_scan); struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); enum wpas_p2p_prov_disc_use { @@ -66,7 +75,6 @@ int wpas_p2p_listen_start(struct wpa_supplicant *wpa_s, unsigned int timeout); int wpas_p2p_assoc_req_ie(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, u8 *buf, size_t len, int p2p_group); void wpas_p2p_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ies); -void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s); u64 wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst, const struct wpabuf *tlvs); u64 wpas_p2p_sd_request_asp(struct wpa_supplicant *wpa_s, const u8 *dst, u8 id, @@ -91,9 +99,15 @@ int wpas_p2p_service_del_upnp(struct wpa_supplicant *wpa_s, u8 version, const char *service); int wpas_p2p_service_add_asp(struct wpa_supplicant *wpa_s, int auto_accept, u32 adv_id, const char *adv_str, u8 svc_state, - u16 config_methods, const char *svc_info); + u16 config_methods, const char *svc_info, + const u8 *cpt_priority); int wpas_p2p_service_del_asp(struct wpa_supplicant *wpa_s, u32 adv_id); +void wpas_p2p_service_flush_asp(struct wpa_supplicant *wpa_s); int wpas_p2p_service_p2ps_id_exists(struct wpa_supplicant *wpa_s, u32 adv_id); +void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token, + u16 update_indic, const u8 *tlvs, size_t tlvs_len); +void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic, + const u8 *tlvs, size_t tlvs_len); 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, @@ -153,10 +167,13 @@ void wpas_p2p_update_config(struct wpa_supplicant *wpa_s); int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr, const u8 *dst, const u8 *bssid, const u8 *ie, size_t ie_len, - int ssi_signal); + unsigned int rx_freq, int ssi_signal); void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr, int registrar); -void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s); + +void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s, + enum wpas_p2p_channel_update_trig trig); + void wpas_p2p_update_best_channels(struct wpa_supplicant *wpa_s, int freq_24, int freq_5, int freq_overall); void wpas_p2p_rx_action(struct wpa_supplicant *wpa_s, const u8 *da, @@ -207,7 +224,7 @@ static inline int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr, const u8 *dst, const u8 *bssid, const u8 *ie, size_t ie_len, - int ssi_signal) + unsigned int rx_freq, int ssi_signal) { return 0; } @@ -217,7 +234,9 @@ static inline void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, { } -static inline void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s) +static inline void +wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s, + enum wpas_p2p_channel_update_trig trig) { } diff --git a/contrib/wpa/wpa_supplicant/p2p_supplicant_sd.c b/contrib/wpa/wpa_supplicant/p2p_supplicant_sd.c new file mode 100644 index 0000000..fc07b07 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/p2p_supplicant_sd.c @@ -0,0 +1,1273 @@ +/* + * wpa_supplicant - P2P service discovery + * Copyright (c) 2009-2010, Atheros Communications + * Copyright (c) 2010-2014, Jouni Malinen <j@w1.fi> + * + * 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 "p2p/p2p.h" +#include "wpa_supplicant_i.h" +#include "notify.h" +#include "p2p_supplicant.h" + + +/* + * DNS Header section is used only to calculate compression pointers, so the + * contents of this data does not matter, but the length needs to be reserved + * in the virtual packet. + */ +#define DNS_HEADER_LEN 12 + +/* + * 27-octet in-memory packet from P2P specification containing two implied + * queries for _tcp.lcoal. PTR IN and _udp.local. PTR IN + */ +#define P2P_SD_IN_MEMORY_LEN 27 + +static int p2p_sd_dns_uncompress_label(char **upos, char *uend, u8 *start, + u8 **spos, const u8 *end) +{ + while (*spos < end) { + u8 val = ((*spos)[0] & 0xc0) >> 6; + int len; + + if (val == 1 || val == 2) { + /* These are reserved values in RFC 1035 */ + wpa_printf(MSG_DEBUG, "P2P: Invalid domain name " + "sequence starting with 0x%x", val); + return -1; + } + + if (val == 3) { + u16 offset; + u8 *spos_tmp; + + /* Offset */ + if (*spos + 2 > end) { + wpa_printf(MSG_DEBUG, "P2P: No room for full " + "DNS offset field"); + return -1; + } + + offset = (((*spos)[0] & 0x3f) << 8) | (*spos)[1]; + if (offset >= *spos - start) { + wpa_printf(MSG_DEBUG, "P2P: Invalid DNS " + "pointer offset %u", offset); + return -1; + } + + (*spos) += 2; + spos_tmp = start + offset; + return p2p_sd_dns_uncompress_label(upos, uend, start, + &spos_tmp, + *spos - 2); + } + + /* Label */ + len = (*spos)[0] & 0x3f; + if (len == 0) + return 0; + + (*spos)++; + if (*spos + len > end) { + wpa_printf(MSG_DEBUG, "P2P: Invalid domain name " + "sequence - no room for label with length " + "%u", len); + return -1; + } + + if (*upos + len + 2 > uend) + return -2; + + os_memcpy(*upos, *spos, len); + *spos += len; + *upos += len; + (*upos)[0] = '.'; + (*upos)++; + (*upos)[0] = '\0'; + } + + return 0; +} + + +/* Uncompress domain names per RFC 1035 using the P2P SD in-memory packet. + * Returns -1 on parsing error (invalid input sequence), -2 if output buffer is + * not large enough */ +static int p2p_sd_dns_uncompress(char *buf, size_t buf_len, const u8 *msg, + size_t msg_len, size_t offset) +{ + /* 27-octet in-memory packet from P2P specification */ + const char *prefix = "\x04_tcp\x05local\x00\x00\x0C\x00\x01" + "\x04_udp\xC0\x11\x00\x0C\x00\x01"; + u8 *tmp, *end, *spos; + char *upos, *uend; + int ret = 0; + + if (buf_len < 2) + return -1; + if (offset > msg_len) + return -1; + + tmp = os_malloc(DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN + msg_len); + if (tmp == NULL) + return -1; + spos = tmp + DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN; + end = spos + msg_len; + spos += offset; + + os_memset(tmp, 0, DNS_HEADER_LEN); + os_memcpy(tmp + DNS_HEADER_LEN, prefix, P2P_SD_IN_MEMORY_LEN); + os_memcpy(tmp + DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN, msg, msg_len); + + upos = buf; + uend = buf + buf_len; + + ret = p2p_sd_dns_uncompress_label(&upos, uend, tmp, &spos, end); + if (ret) { + os_free(tmp); + return ret; + } + + if (upos == buf) { + upos[0] = '.'; + upos[1] = '\0'; + } else if (upos[-1] == '.') + upos[-1] = '\0'; + + os_free(tmp); + return 0; +} + + +static struct p2p_srv_bonjour * +wpas_p2p_service_get_bonjour(struct wpa_supplicant *wpa_s, + const struct wpabuf *query) +{ + struct p2p_srv_bonjour *bsrv; + size_t len; + + len = wpabuf_len(query); + dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour, + struct p2p_srv_bonjour, list) { + if (len == wpabuf_len(bsrv->query) && + os_memcmp(wpabuf_head(query), wpabuf_head(bsrv->query), + len) == 0) + return bsrv; + } + return NULL; +} + + +static struct p2p_srv_upnp * +wpas_p2p_service_get_upnp(struct wpa_supplicant *wpa_s, u8 version, + const char *service) +{ + struct p2p_srv_upnp *usrv; + + dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp, + struct p2p_srv_upnp, list) { + if (version == usrv->version && + os_strcmp(service, usrv->service) == 0) + return usrv; + } + return NULL; +} + + +static void wpas_sd_add_empty(struct wpabuf *resp, u8 srv_proto, + u8 srv_trans_id, u8 status) +{ + u8 *len_pos; + + if (wpabuf_tailroom(resp) < 5) + return; + + /* Length (to be filled) */ + len_pos = wpabuf_put(resp, 2); + wpabuf_put_u8(resp, srv_proto); + wpabuf_put_u8(resp, srv_trans_id); + /* Status Code */ + wpabuf_put_u8(resp, status); + /* Response Data: empty */ + WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2); +} + + +static void wpas_sd_add_proto_not_avail(struct wpabuf *resp, u8 srv_proto, + u8 srv_trans_id) +{ + wpas_sd_add_empty(resp, srv_proto, srv_trans_id, + P2P_SD_PROTO_NOT_AVAILABLE); +} + + +static void wpas_sd_add_bad_request(struct wpabuf *resp, u8 srv_proto, + u8 srv_trans_id) +{ + wpas_sd_add_empty(resp, srv_proto, srv_trans_id, P2P_SD_BAD_REQUEST); +} + + +static void wpas_sd_add_not_found(struct wpabuf *resp, u8 srv_proto, + u8 srv_trans_id) +{ + wpas_sd_add_empty(resp, srv_proto, srv_trans_id, + P2P_SD_REQUESTED_INFO_NOT_AVAILABLE); +} + + +static void wpas_sd_all_bonjour(struct wpa_supplicant *wpa_s, + struct wpabuf *resp, u8 srv_trans_id) +{ + struct p2p_srv_bonjour *bsrv; + u8 *len_pos; + + wpa_printf(MSG_DEBUG, "P2P: SD Request for all Bonjour services"); + + if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) { + wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available"); + return; + } + + dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour, + struct p2p_srv_bonjour, list) { + if (wpabuf_tailroom(resp) < + 5 + wpabuf_len(bsrv->query) + wpabuf_len(bsrv->resp)) + return; + /* Length (to be filled) */ + len_pos = wpabuf_put(resp, 2); + wpabuf_put_u8(resp, P2P_SERV_BONJOUR); + wpabuf_put_u8(resp, srv_trans_id); + /* Status Code */ + wpabuf_put_u8(resp, P2P_SD_SUCCESS); + wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service", + wpabuf_head(bsrv->resp), + wpabuf_len(bsrv->resp)); + /* Response Data */ + wpabuf_put_buf(resp, bsrv->query); /* Key */ + wpabuf_put_buf(resp, bsrv->resp); /* Value */ + WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - + 2); + } +} + + +static int match_bonjour_query(struct p2p_srv_bonjour *bsrv, const u8 *query, + size_t query_len) +{ + char str_rx[256], str_srv[256]; + + if (query_len < 3 || wpabuf_len(bsrv->query) < 3) + return 0; /* Too short to include DNS Type and Version */ + if (os_memcmp(query + query_len - 3, + wpabuf_head_u8(bsrv->query) + wpabuf_len(bsrv->query) - 3, + 3) != 0) + return 0; /* Mismatch in DNS Type or Version */ + if (query_len == wpabuf_len(bsrv->query) && + os_memcmp(query, wpabuf_head(bsrv->query), query_len - 3) == 0) + return 1; /* Binary match */ + + if (p2p_sd_dns_uncompress(str_rx, sizeof(str_rx), query, query_len - 3, + 0)) + return 0; /* Failed to uncompress query */ + if (p2p_sd_dns_uncompress(str_srv, sizeof(str_srv), + wpabuf_head(bsrv->query), + wpabuf_len(bsrv->query) - 3, 0)) + return 0; /* Failed to uncompress service */ + + return os_strcmp(str_rx, str_srv) == 0; +} + + +static void wpas_sd_req_bonjour(struct wpa_supplicant *wpa_s, + struct wpabuf *resp, u8 srv_trans_id, + const u8 *query, size_t query_len) +{ + struct p2p_srv_bonjour *bsrv; + u8 *len_pos; + int matches = 0; + + wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for Bonjour", + query, query_len); + if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) { + wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available"); + wpas_sd_add_proto_not_avail(resp, P2P_SERV_BONJOUR, + srv_trans_id); + return; + } + + if (query_len == 0) { + wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id); + return; + } + + dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour, + struct p2p_srv_bonjour, list) { + if (!match_bonjour_query(bsrv, query, query_len)) + continue; + + if (wpabuf_tailroom(resp) < + 5 + query_len + wpabuf_len(bsrv->resp)) + return; + + matches++; + + /* Length (to be filled) */ + len_pos = wpabuf_put(resp, 2); + wpabuf_put_u8(resp, P2P_SERV_BONJOUR); + wpabuf_put_u8(resp, srv_trans_id); + + /* Status Code */ + wpabuf_put_u8(resp, P2P_SD_SUCCESS); + wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service", + wpabuf_head(bsrv->resp), + wpabuf_len(bsrv->resp)); + + /* Response Data */ + wpabuf_put_data(resp, query, query_len); /* Key */ + wpabuf_put_buf(resp, bsrv->resp); /* Value */ + + WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2); + } + + if (matches == 0) { + wpa_printf(MSG_DEBUG, "P2P: Requested Bonjour service not " + "available"); + if (wpabuf_tailroom(resp) < 5) + return; + + /* Length (to be filled) */ + len_pos = wpabuf_put(resp, 2); + wpabuf_put_u8(resp, P2P_SERV_BONJOUR); + wpabuf_put_u8(resp, srv_trans_id); + + /* Status Code */ + wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE); + /* Response Data: empty */ + WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - + 2); + } +} + + +static void wpas_sd_all_upnp(struct wpa_supplicant *wpa_s, + struct wpabuf *resp, u8 srv_trans_id) +{ + struct p2p_srv_upnp *usrv; + u8 *len_pos; + + wpa_printf(MSG_DEBUG, "P2P: SD Request for all UPnP services"); + + if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) { + wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available"); + return; + } + + dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp, + struct p2p_srv_upnp, list) { + if (wpabuf_tailroom(resp) < 5 + 1 + os_strlen(usrv->service)) + return; + + /* Length (to be filled) */ + len_pos = wpabuf_put(resp, 2); + wpabuf_put_u8(resp, P2P_SERV_UPNP); + wpabuf_put_u8(resp, srv_trans_id); + + /* Status Code */ + wpabuf_put_u8(resp, P2P_SD_SUCCESS); + /* Response Data */ + wpabuf_put_u8(resp, usrv->version); + wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s", + usrv->service); + wpabuf_put_str(resp, usrv->service); + WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - + 2); + } +} + + +static void wpas_sd_req_upnp(struct wpa_supplicant *wpa_s, + struct wpabuf *resp, u8 srv_trans_id, + const u8 *query, size_t query_len) +{ + struct p2p_srv_upnp *usrv; + u8 *len_pos; + u8 version; + char *str; + int count = 0; + + wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for UPnP", + query, query_len); + + if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) { + wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available"); + wpas_sd_add_proto_not_avail(resp, P2P_SERV_UPNP, + srv_trans_id); + return; + } + + if (query_len == 0) { + wpas_sd_all_upnp(wpa_s, resp, srv_trans_id); + return; + } + + if (wpabuf_tailroom(resp) < 5) + return; + + /* Length (to be filled) */ + len_pos = wpabuf_put(resp, 2); + wpabuf_put_u8(resp, P2P_SERV_UPNP); + wpabuf_put_u8(resp, srv_trans_id); + + version = query[0]; + str = os_malloc(query_len); + if (str == NULL) + return; + os_memcpy(str, query + 1, query_len - 1); + str[query_len - 1] = '\0'; + + dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp, + struct p2p_srv_upnp, list) { + if (version != usrv->version) + continue; + + if (os_strcmp(str, "ssdp:all") != 0 && + os_strstr(usrv->service, str) == NULL) + continue; + + if (wpabuf_tailroom(resp) < 2) + break; + if (count == 0) { + /* Status Code */ + wpabuf_put_u8(resp, P2P_SD_SUCCESS); + /* Response Data */ + wpabuf_put_u8(resp, version); + } else + wpabuf_put_u8(resp, ','); + + count++; + + wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s", + usrv->service); + if (wpabuf_tailroom(resp) < os_strlen(usrv->service)) + break; + wpabuf_put_str(resp, usrv->service); + } + os_free(str); + + if (count == 0) { + wpa_printf(MSG_DEBUG, "P2P: Requested UPnP service not " + "available"); + /* Status Code */ + wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE); + /* Response Data: empty */ + } + + WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2); +} + + +#ifdef CONFIG_WIFI_DISPLAY +static void wpas_sd_req_wfd(struct wpa_supplicant *wpa_s, + struct wpabuf *resp, u8 srv_trans_id, + const u8 *query, size_t query_len) +{ + const u8 *pos; + u8 role; + u8 *len_pos; + + wpa_hexdump(MSG_DEBUG, "P2P: SD Request for WFD", query, query_len); + + if (!wpa_s->global->wifi_display) { + wpa_printf(MSG_DEBUG, "P2P: WFD protocol not available"); + wpas_sd_add_proto_not_avail(resp, P2P_SERV_WIFI_DISPLAY, + srv_trans_id); + return; + } + + if (query_len < 1) { + wpa_printf(MSG_DEBUG, "P2P: Missing WFD Requested Device " + "Role"); + return; + } + + if (wpabuf_tailroom(resp) < 5) + return; + + pos = query; + role = *pos++; + wpa_printf(MSG_DEBUG, "P2P: WSD for device role 0x%x", role); + + /* TODO: role specific handling */ + + /* Length (to be filled) */ + len_pos = wpabuf_put(resp, 2); + wpabuf_put_u8(resp, P2P_SERV_WIFI_DISPLAY); + wpabuf_put_u8(resp, srv_trans_id); + wpabuf_put_u8(resp, P2P_SD_SUCCESS); /* Status Code */ + + while (pos < query + query_len) { + if (*pos < MAX_WFD_SUBELEMS && + wpa_s->global->wfd_subelem[*pos] && + wpabuf_tailroom(resp) >= + wpabuf_len(wpa_s->global->wfd_subelem[*pos])) { + wpa_printf(MSG_DEBUG, "P2P: Add WSD response " + "subelement %u", *pos); + wpabuf_put_buf(resp, wpa_s->global->wfd_subelem[*pos]); + } + pos++; + } + + WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2); +} +#endif /* CONFIG_WIFI_DISPLAY */ + + +static int find_p2ps_substr(struct p2ps_advertisement *adv_data, + const u8 *needle, size_t needle_len) +{ + const u8 *haystack = (const u8 *) adv_data->svc_info; + size_t haystack_len, i; + + /* Allow search term to be empty */ + if (!needle || !needle_len) + return 1; + + if (!haystack) + return 0; + + haystack_len = os_strlen(adv_data->svc_info); + for (i = 0; i < haystack_len; i++) { + if (haystack_len - i < needle_len) + break; + if (os_memcmp(haystack + i, needle, needle_len) == 0) + return 1; + } + + return 0; +} + + +static void wpas_sd_req_asp(struct wpa_supplicant *wpa_s, + struct wpabuf *resp, u8 srv_trans_id, + const u8 *query, size_t query_len) +{ + struct p2ps_advertisement *adv_data; + const u8 *svc = &query[1]; + const u8 *info = NULL; + size_t svc_len = query[0]; + size_t info_len = 0; + int prefix = 0; + u8 *count_pos = NULL; + u8 *len_pos = NULL; + + wpa_hexdump(MSG_DEBUG, "P2P: SD Request for ASP", query, query_len); + + if (!wpa_s->global->p2p) { + wpa_printf(MSG_DEBUG, "P2P: ASP protocol not available"); + wpas_sd_add_proto_not_avail(resp, P2P_SERV_P2PS, srv_trans_id); + return; + } + + /* Info block is optional */ + if (svc_len + 1 < query_len) { + info = &svc[svc_len]; + info_len = *info++; + } + + /* Range check length of svc string and info block */ + if (svc_len + (info_len ? info_len + 2 : 1) > query_len) { + wpa_printf(MSG_DEBUG, "P2P: ASP bad request"); + wpas_sd_add_bad_request(resp, P2P_SERV_P2PS, srv_trans_id); + return; + } + + /* Detect and correct for prefix search */ + if (svc_len && svc[svc_len - 1] == '*') { + prefix = 1; + svc_len--; + } + + for (adv_data = p2p_get_p2ps_adv_list(wpa_s->global->p2p); + adv_data; adv_data = adv_data->next) { + /* If not a prefix match, reject length mismatches */ + if (!prefix && svc_len != os_strlen(adv_data->svc_name)) + continue; + + /* Search each service for request */ + if (os_memcmp(adv_data->svc_name, svc, svc_len) == 0 && + find_p2ps_substr(adv_data, info, info_len)) { + size_t len = os_strlen(adv_data->svc_name); + size_t svc_info_len = 0; + + if (adv_data->svc_info) + svc_info_len = os_strlen(adv_data->svc_info); + + if (len > 0xff || svc_info_len > 0xffff) + return; + + /* Length & Count to be filled as we go */ + if (!len_pos && !count_pos) { + if (wpabuf_tailroom(resp) < + len + svc_info_len + 16) + return; + + len_pos = wpabuf_put(resp, 2); + wpabuf_put_u8(resp, P2P_SERV_P2PS); + wpabuf_put_u8(resp, srv_trans_id); + /* Status Code */ + wpabuf_put_u8(resp, P2P_SD_SUCCESS); + count_pos = wpabuf_put(resp, 1); + *count_pos = 0; + } else if (wpabuf_tailroom(resp) < + len + svc_info_len + 10) + return; + + if (svc_info_len) { + wpa_printf(MSG_DEBUG, + "P2P: Add Svc: %s info: %s", + adv_data->svc_name, + adv_data->svc_info); + } else { + wpa_printf(MSG_DEBUG, "P2P: Add Svc: %s", + adv_data->svc_name); + } + + /* Advertisement ID */ + wpabuf_put_le32(resp, adv_data->id); + + /* Config Methods */ + wpabuf_put_be16(resp, adv_data->config_methods); + + /* Service Name */ + wpabuf_put_u8(resp, (u8) len); + wpabuf_put_data(resp, adv_data->svc_name, len); + + /* Service State */ + wpabuf_put_u8(resp, adv_data->state); + + /* Service Information */ + wpabuf_put_le16(resp, (u16) svc_info_len); + wpabuf_put_data(resp, adv_data->svc_info, svc_info_len); + + /* Update length and count */ + (*count_pos)++; + WPA_PUT_LE16(len_pos, + (u8 *) wpabuf_put(resp, 0) - len_pos - 2); + } + } + + /* Return error if no matching svc found */ + if (count_pos == NULL) { + wpa_printf(MSG_DEBUG, "P2P: ASP service not found"); + wpas_sd_add_not_found(resp, P2P_SERV_P2PS, srv_trans_id); + } +} + + +static void wpas_sd_all_asp(struct wpa_supplicant *wpa_s, + struct wpabuf *resp, u8 srv_trans_id) +{ + /* Query data to add all P2PS advertisements: + * - Service name length: 1 + * - Service name: '*' + * - Service Information Request Length: 0 + */ + const u8 q[] = { 1, (const u8) '*', 0 }; + + if (p2p_get_p2ps_adv_list(wpa_s->global->p2p)) + wpas_sd_req_asp(wpa_s, resp, srv_trans_id, q, sizeof(q)); +} + + +void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token, + u16 update_indic, const u8 *tlvs, size_t tlvs_len) +{ + struct wpa_supplicant *wpa_s = ctx; + const u8 *pos = tlvs; + const u8 *end = tlvs + tlvs_len; + const u8 *tlv_end; + u16 slen; + struct wpabuf *resp; + u8 srv_proto, srv_trans_id; + size_t buf_len; + char *buf; + + wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Request TLVs", + tlvs, tlvs_len); + buf_len = 2 * tlvs_len + 1; + buf = os_malloc(buf_len); + if (buf) { + wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len); + wpa_msg_ctrl(wpa_s, MSG_INFO, P2P_EVENT_SERV_DISC_REQ "%d " + MACSTR " %u %u %s", + freq, MAC2STR(sa), dialog_token, update_indic, + buf); + os_free(buf); + } + + if (wpa_s->p2p_sd_over_ctrl_iface) { + wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token, + update_indic, tlvs, tlvs_len); + return; /* to be processed by an external program */ + } + + resp = wpabuf_alloc(10000); + if (resp == NULL) + return; + + while (pos + 1 < end) { + wpa_printf(MSG_DEBUG, "P2P: Service Request TLV"); + slen = WPA_GET_LE16(pos); + pos += 2; + if (pos + slen > end || slen < 2) { + wpa_printf(MSG_DEBUG, "P2P: Unexpected Query Data " + "length"); + wpabuf_free(resp); + return; + } + tlv_end = pos + slen; + + srv_proto = *pos++; + wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u", + srv_proto); + srv_trans_id = *pos++; + wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u", + srv_trans_id); + + wpa_hexdump(MSG_MSGDUMP, "P2P: Query Data", + pos, tlv_end - pos); + + + if (wpa_s->force_long_sd) { + wpa_printf(MSG_DEBUG, "P2P: SD test - force long " + "response"); + wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id); + wpas_sd_all_upnp(wpa_s, resp, srv_trans_id); + wpas_sd_all_asp(wpa_s, resp, srv_trans_id); + goto done; + } + + switch (srv_proto) { + case P2P_SERV_ALL_SERVICES: + wpa_printf(MSG_DEBUG, "P2P: Service Discovery Request " + "for all services"); + if (dl_list_empty(&wpa_s->global->p2p_srv_upnp) && + dl_list_empty(&wpa_s->global->p2p_srv_bonjour) && + !p2p_get_p2ps_adv_list(wpa_s->global->p2p)) { + wpa_printf(MSG_DEBUG, "P2P: No service " + "discovery protocols available"); + wpas_sd_add_proto_not_avail( + resp, P2P_SERV_ALL_SERVICES, + srv_trans_id); + break; + } + wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id); + wpas_sd_all_upnp(wpa_s, resp, srv_trans_id); + wpas_sd_all_asp(wpa_s, resp, srv_trans_id); + break; + case P2P_SERV_BONJOUR: + wpas_sd_req_bonjour(wpa_s, resp, srv_trans_id, + pos, tlv_end - pos); + break; + case P2P_SERV_UPNP: + wpas_sd_req_upnp(wpa_s, resp, srv_trans_id, + pos, tlv_end - pos); + break; +#ifdef CONFIG_WIFI_DISPLAY + case P2P_SERV_WIFI_DISPLAY: + wpas_sd_req_wfd(wpa_s, resp, srv_trans_id, + pos, tlv_end - pos); + break; +#endif /* CONFIG_WIFI_DISPLAY */ + case P2P_SERV_P2PS: + wpas_sd_req_asp(wpa_s, resp, srv_trans_id, + pos, tlv_end - pos); + break; + default: + wpa_printf(MSG_DEBUG, "P2P: Unavailable service " + "protocol %u", srv_proto); + wpas_sd_add_proto_not_avail(resp, srv_proto, + srv_trans_id); + break; + } + + pos = tlv_end; + } + +done: + wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token, + update_indic, tlvs, tlvs_len); + + wpas_p2p_sd_response(wpa_s, freq, sa, dialog_token, resp); + + wpabuf_free(resp); +} + + +static void wpas_sd_p2ps_serv_response(struct wpa_supplicant *wpa_s, + const u8 *sa, u8 srv_trans_id, + const u8 *pos, const u8 *tlv_end) +{ + u8 left = *pos++; + u32 adv_id; + u8 svc_status; + u16 config_methods; + char svc_str[256]; + + while (left-- && pos < tlv_end) { + char *buf = NULL; + size_t buf_len; + u8 svc_len; + + /* Sanity check fixed length+svc_str */ + if (pos + 6 >= tlv_end) + break; + svc_len = pos[6]; + if (pos + svc_len + 10 > tlv_end) + break; + + /* Advertisement ID */ + adv_id = WPA_GET_LE32(pos); + pos += sizeof(u32); + + /* Config Methods */ + config_methods = WPA_GET_BE16(pos); + pos += sizeof(u16); + + /* Service Name */ + pos++; /* svc_len */ + os_memcpy(svc_str, pos, svc_len); + svc_str[svc_len] = '\0'; + pos += svc_len; + + /* Service Status */ + svc_status = *pos++; + + /* Service Information Length */ + buf_len = WPA_GET_LE16(pos); + pos += sizeof(u16); + + /* Sanity check buffer length */ + if (buf_len > (unsigned int) (tlv_end - pos)) + break; + + if (buf_len) { + buf = os_zalloc(2 * buf_len + 1); + if (buf) { + utf8_escape((const char *) pos, buf_len, buf, + 2 * buf_len + 1); + } + } + + pos += buf_len; + + if (buf) { + wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_SERV_ASP_RESP + MACSTR " %x %x %x %x %s '%s'", + MAC2STR(sa), srv_trans_id, adv_id, + svc_status, config_methods, svc_str, + buf); + os_free(buf); + } else { + wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_SERV_ASP_RESP + MACSTR " %x %x %x %x %s", + MAC2STR(sa), srv_trans_id, adv_id, + svc_status, config_methods, svc_str); + } + } +} + + +void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic, + const u8 *tlvs, size_t tlvs_len) +{ + struct wpa_supplicant *wpa_s = ctx; + const u8 *pos = tlvs; + const u8 *end = tlvs + tlvs_len; + const u8 *tlv_end; + u16 slen; + size_t buf_len; + char *buf; + + wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Response TLVs", + tlvs, tlvs_len); + if (tlvs_len > 1500) { + /* TODO: better way for handling this */ + wpa_msg_ctrl(wpa_s, MSG_INFO, + P2P_EVENT_SERV_DISC_RESP MACSTR + " %u <long response: %u bytes>", + MAC2STR(sa), update_indic, + (unsigned int) tlvs_len); + } else { + buf_len = 2 * tlvs_len + 1; + buf = os_malloc(buf_len); + if (buf) { + wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len); + wpa_msg_ctrl(wpa_s, MSG_INFO, + P2P_EVENT_SERV_DISC_RESP MACSTR " %u %s", + MAC2STR(sa), update_indic, buf); + os_free(buf); + } + } + + while (pos < end) { + 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) { + wpa_printf(MSG_DEBUG, "P2P: Unexpected Response Data " + "length"); + return; + } + tlv_end = pos + slen; + + srv_proto = *pos++; + wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u", + srv_proto); + srv_trans_id = *pos++; + wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u", + srv_trans_id); + status = *pos++; + wpa_printf(MSG_DEBUG, "P2P: Status Code ID %u", + status); + + wpa_hexdump(MSG_MSGDUMP, "P2P: Response Data", + pos, tlv_end - pos); + + if (srv_proto == P2P_SERV_P2PS && pos < tlv_end) { + wpas_sd_p2ps_serv_response(wpa_s, sa, srv_trans_id, + pos, tlv_end); + } + + pos = tlv_end; + } + + wpas_notify_p2p_sd_response(wpa_s, sa, update_indic, tlvs, tlvs_len); +} + + +u64 wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst, + const struct wpabuf *tlvs) +{ + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return 0; + return (uintptr_t) p2p_sd_request(wpa_s->global->p2p, dst, tlvs); +} + + +u64 wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst, + u8 version, const char *query) +{ + struct wpabuf *tlvs; + u64 ret; + + tlvs = wpabuf_alloc(2 + 1 + 1 + 1 + os_strlen(query)); + if (tlvs == NULL) + return 0; + wpabuf_put_le16(tlvs, 1 + 1 + 1 + os_strlen(query)); + wpabuf_put_u8(tlvs, P2P_SERV_UPNP); /* Service Protocol Type */ + wpabuf_put_u8(tlvs, 1); /* Service Transaction ID */ + wpabuf_put_u8(tlvs, version); + wpabuf_put_str(tlvs, query); + ret = wpas_p2p_sd_request(wpa_s, dst, tlvs); + wpabuf_free(tlvs); + return ret; +} + + +u64 wpas_p2p_sd_request_asp(struct wpa_supplicant *wpa_s, const u8 *dst, u8 id, + const char *svc_str, const char *info_substr) +{ + struct wpabuf *tlvs; + size_t plen, svc_len, substr_len = 0; + u64 ret; + + svc_len = os_strlen(svc_str); + if (info_substr) + substr_len = os_strlen(info_substr); + + if (svc_len > 0xff || substr_len > 0xff) + return 0; + + plen = 1 + 1 + 1 + svc_len + 1 + substr_len; + tlvs = wpabuf_alloc(2 + plen); + if (tlvs == NULL) + return 0; + + wpabuf_put_le16(tlvs, plen); + wpabuf_put_u8(tlvs, P2P_SERV_P2PS); + wpabuf_put_u8(tlvs, id); /* Service Transaction ID */ + wpabuf_put_u8(tlvs, (u8) svc_len); /* Service String Length */ + wpabuf_put_data(tlvs, svc_str, svc_len); + wpabuf_put_u8(tlvs, (u8) substr_len); /* Info Substring Length */ + wpabuf_put_data(tlvs, info_substr, substr_len); + ret = wpas_p2p_sd_request(wpa_s, dst, tlvs); + wpabuf_free(tlvs); + + return ret; +} + + +#ifdef CONFIG_WIFI_DISPLAY + +static u64 wpas_p2p_sd_request_wfd(struct wpa_supplicant *wpa_s, const u8 *dst, + const struct wpabuf *tlvs) +{ + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return 0; + return (uintptr_t) p2p_sd_request_wfd(wpa_s->global->p2p, dst, tlvs); +} + + +#define MAX_WFD_SD_SUBELEMS 20 + +static void wfd_add_sd_req_role(struct wpabuf *tlvs, u8 id, u8 role, + const char *subelems) +{ + u8 *len; + const char *pos; + int val; + int count = 0; + + len = wpabuf_put(tlvs, 2); + wpabuf_put_u8(tlvs, P2P_SERV_WIFI_DISPLAY); /* Service Protocol Type */ + wpabuf_put_u8(tlvs, id); /* Service Transaction ID */ + + wpabuf_put_u8(tlvs, role); + + pos = subelems; + while (*pos) { + val = atoi(pos); + if (val >= 0 && val < 256) { + wpabuf_put_u8(tlvs, val); + count++; + if (count == MAX_WFD_SD_SUBELEMS) + break; + } + pos = os_strchr(pos + 1, ','); + if (pos == NULL) + break; + pos++; + } + + WPA_PUT_LE16(len, (u8 *) wpabuf_put(tlvs, 0) - len - 2); +} + + +u64 wpas_p2p_sd_request_wifi_display(struct wpa_supplicant *wpa_s, + const u8 *dst, const char *role) +{ + struct wpabuf *tlvs; + u64 ret; + const char *subelems; + u8 id = 1; + + subelems = os_strchr(role, ' '); + if (subelems == NULL) + return 0; + subelems++; + + tlvs = wpabuf_alloc(4 * (2 + 1 + 1 + 1 + MAX_WFD_SD_SUBELEMS)); + if (tlvs == NULL) + return 0; + + if (os_strstr(role, "[source]")) + wfd_add_sd_req_role(tlvs, id++, 0x00, subelems); + if (os_strstr(role, "[pri-sink]")) + wfd_add_sd_req_role(tlvs, id++, 0x01, subelems); + if (os_strstr(role, "[sec-sink]")) + wfd_add_sd_req_role(tlvs, id++, 0x02, subelems); + if (os_strstr(role, "[source+sink]")) + wfd_add_sd_req_role(tlvs, id++, 0x03, subelems); + + ret = wpas_p2p_sd_request_wfd(wpa_s, dst, tlvs); + wpabuf_free(tlvs); + return ret; +} + +#endif /* CONFIG_WIFI_DISPLAY */ + + +int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, u64 req) +{ + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return -1; + return p2p_sd_cancel_request(wpa_s->global->p2p, + (void *) (uintptr_t) req); +} + + +void wpas_p2p_sd_response(struct wpa_supplicant *wpa_s, int freq, + const u8 *dst, u8 dialog_token, + const struct wpabuf *resp_tlvs) +{ + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return; + p2p_sd_response(wpa_s->global->p2p, freq, dst, dialog_token, + resp_tlvs); +} + + +void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->global->p2p) + p2p_sd_service_update(wpa_s->global->p2p); +} + + +static void wpas_p2p_srv_bonjour_free(struct p2p_srv_bonjour *bsrv) +{ + dl_list_del(&bsrv->list); + wpabuf_free(bsrv->query); + wpabuf_free(bsrv->resp); + os_free(bsrv); +} + + +static void wpas_p2p_srv_upnp_free(struct p2p_srv_upnp *usrv) +{ + dl_list_del(&usrv->list); + os_free(usrv->service); + os_free(usrv); +} + + +void wpas_p2p_service_flush(struct wpa_supplicant *wpa_s) +{ + struct p2p_srv_bonjour *bsrv, *bn; + struct p2p_srv_upnp *usrv, *un; + + dl_list_for_each_safe(bsrv, bn, &wpa_s->global->p2p_srv_bonjour, + struct p2p_srv_bonjour, list) + wpas_p2p_srv_bonjour_free(bsrv); + + dl_list_for_each_safe(usrv, un, &wpa_s->global->p2p_srv_upnp, + struct p2p_srv_upnp, list) + wpas_p2p_srv_upnp_free(usrv); + + wpas_p2p_service_flush_asp(wpa_s); + wpas_p2p_sd_service_update(wpa_s); +} + + +int wpas_p2p_service_p2ps_id_exists(struct wpa_supplicant *wpa_s, u32 adv_id) +{ + if (adv_id == 0) + return 1; + + if (p2p_service_p2ps_id(wpa_s->global->p2p, adv_id)) + return 1; + + return 0; +} + + +int wpas_p2p_service_del_asp(struct wpa_supplicant *wpa_s, u32 adv_id) +{ + int ret; + + ret = p2p_service_del_asp(wpa_s->global->p2p, adv_id); + if (ret == 0) + wpas_p2p_sd_service_update(wpa_s); + return ret; +} + + +int wpas_p2p_service_add_asp(struct wpa_supplicant *wpa_s, + int auto_accept, u32 adv_id, + const char *adv_str, u8 svc_state, + u16 config_methods, const char *svc_info, + const u8 *cpt_priority) +{ + int ret; + + ret = p2p_service_add_asp(wpa_s->global->p2p, auto_accept, adv_id, + adv_str, svc_state, config_methods, + svc_info, cpt_priority); + if (ret == 0) + wpas_p2p_sd_service_update(wpa_s); + return ret; +} + + +void wpas_p2p_service_flush_asp(struct wpa_supplicant *wpa_s) +{ + p2p_service_flush_asp(wpa_s->global->p2p); +} + + +int wpas_p2p_service_add_bonjour(struct wpa_supplicant *wpa_s, + struct wpabuf *query, struct wpabuf *resp) +{ + struct p2p_srv_bonjour *bsrv; + + bsrv = os_zalloc(sizeof(*bsrv)); + if (bsrv == NULL) + return -1; + bsrv->query = query; + bsrv->resp = resp; + dl_list_add(&wpa_s->global->p2p_srv_bonjour, &bsrv->list); + + wpas_p2p_sd_service_update(wpa_s); + return 0; +} + + +int wpas_p2p_service_del_bonjour(struct wpa_supplicant *wpa_s, + const struct wpabuf *query) +{ + struct p2p_srv_bonjour *bsrv; + + bsrv = wpas_p2p_service_get_bonjour(wpa_s, query); + if (bsrv == NULL) + return -1; + wpas_p2p_srv_bonjour_free(bsrv); + wpas_p2p_sd_service_update(wpa_s); + return 0; +} + + +int wpas_p2p_service_add_upnp(struct wpa_supplicant *wpa_s, u8 version, + const char *service) +{ + struct p2p_srv_upnp *usrv; + + if (wpas_p2p_service_get_upnp(wpa_s, version, service)) + return 0; /* Already listed */ + usrv = os_zalloc(sizeof(*usrv)); + if (usrv == NULL) + return -1; + usrv->version = version; + usrv->service = os_strdup(service); + if (usrv->service == NULL) { + os_free(usrv); + return -1; + } + dl_list_add(&wpa_s->global->p2p_srv_upnp, &usrv->list); + + wpas_p2p_sd_service_update(wpa_s); + return 0; +} + + +int wpas_p2p_service_del_upnp(struct wpa_supplicant *wpa_s, u8 version, + const char *service) +{ + struct p2p_srv_upnp *usrv; + + usrv = wpas_p2p_service_get_upnp(wpa_s, version, service); + if (usrv == NULL) + return -1; + wpas_p2p_srv_upnp_free(usrv); + wpas_p2p_sd_service_update(wpa_s); + return 0; +} diff --git a/contrib/wpa/wpa_supplicant/preauth_test.c b/contrib/wpa/wpa_supplicant/preauth_test.c index ed57085..f4bba98 100644 --- a/contrib/wpa/wpa_supplicant/preauth_test.c +++ b/contrib/wpa/wpa_supplicant/preauth_test.c @@ -27,7 +27,7 @@ #include "drivers/driver.h" -struct wpa_driver_ops *wpa_drivers[] = { NULL }; +const struct wpa_driver_ops *const wpa_drivers[] = { NULL }; struct preauth_test_data { diff --git a/contrib/wpa/wpa_supplicant/scan.c b/contrib/wpa/wpa_supplicant/scan.c index 805891a..d7049a1 100644 --- a/contrib/wpa/wpa_supplicant/scan.c +++ b/contrib/wpa/wpa_supplicant/scan.c @@ -418,22 +418,6 @@ static void wpa_supplicant_optimize_freqs( static void wpas_add_interworking_elements(struct wpa_supplicant *wpa_s, struct wpabuf *buf) { - if (wpa_s->conf->interworking == 0) - return; - - wpabuf_put_u8(buf, WLAN_EID_EXT_CAPAB); - wpabuf_put_u8(buf, 6); - wpabuf_put_u8(buf, 0x00); - wpabuf_put_u8(buf, 0x00); - wpabuf_put_u8(buf, 0x00); - wpabuf_put_u8(buf, 0x80); /* Bit 31 - Interworking */ - wpabuf_put_u8(buf, 0x00); -#ifdef CONFIG_HS20 - wpabuf_put_u8(buf, 0x40); /* Bit 46 - WNM-Notification */ -#else /* CONFIG_HS20 */ - wpabuf_put_u8(buf, 0x00); -#endif /* CONFIG_HS20 */ - wpabuf_put_u8(buf, WLAN_EID_INTERWORKING); wpabuf_put_u8(buf, is_zero_ether_addr(wpa_s->conf->hessid) ? 1 : 1 + ETH_ALEN); @@ -448,11 +432,19 @@ static void wpas_add_interworking_elements(struct wpa_supplicant *wpa_s, static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s) { struct wpabuf *extra_ie = NULL; + u8 ext_capab[18]; + int ext_capab_len; #ifdef CONFIG_WPS int wps = 0; enum wps_request_type req_type = WPS_REQ_ENROLLEE_INFO; #endif /* CONFIG_WPS */ + ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab, + sizeof(ext_capab)); + if (ext_capab_len > 0 && + wpabuf_resize(&extra_ie, ext_capab_len) == 0) + wpabuf_put_data(extra_ie, ext_capab, ext_capab_len); + #ifdef CONFIG_INTERWORKING if (wpa_s->conf->interworking && wpabuf_resize(&extra_ie, 100) == 0) @@ -493,6 +485,12 @@ static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s) wpas_hs20_add_indication(extra_ie, -1); #endif /* CONFIG_HS20 */ +#ifdef CONFIG_FST + if (wpa_s->fst_ies && + wpabuf_resize(&extra_ie, wpabuf_len(wpa_s->fst_ies)) == 0) + wpabuf_put_buf(extra_ie, wpa_s->fst_ies); +#endif /* CONFIG_FST */ + return extra_ie; } @@ -622,6 +620,37 @@ static void wpa_set_scan_ssids(struct wpa_supplicant *wpa_s, } +static int wpa_set_ssids_from_scan_req(struct wpa_supplicant *wpa_s, + struct wpa_driver_scan_params *params, + size_t max_ssids) +{ + unsigned int i; + + if (wpa_s->ssids_from_scan_req == NULL || + wpa_s->num_ssids_from_scan_req == 0) + return 0; + + if (wpa_s->num_ssids_from_scan_req > max_ssids) { + wpa_s->num_ssids_from_scan_req = max_ssids; + wpa_printf(MSG_DEBUG, "Over max scan SSIDs from scan req: %u", + (unsigned int) max_ssids); + } + + for (i = 0; i < wpa_s->num_ssids_from_scan_req; i++) { + params->ssids[i].ssid = wpa_s->ssids_from_scan_req[i].ssid; + params->ssids[i].ssid_len = + wpa_s->ssids_from_scan_req[i].ssid_len; + wpa_hexdump_ascii(MSG_DEBUG, "specific SSID", + params->ssids[i].ssid, + params->ssids[i].ssid_len); + } + + params->num_ssids = wpa_s->num_ssids_from_scan_req; + wpa_s->num_ssids_from_scan_req = 0; + return 1; +} + + static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; @@ -734,6 +763,12 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) goto scan; } + if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && + wpa_set_ssids_from_scan_req(wpa_s, ¶ms, max_ssids)) { + wpa_printf(MSG_DEBUG, "Use specific SSIDs from SCAN command"); + goto ssid_list_set; + } + #ifdef CONFIG_P2P if ((wpa_s->p2p_in_provisioning || wpa_s->show_group_started) && wpa_s->go_params && !wpa_s->conf->passive_scan) { @@ -773,6 +808,9 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) } if (wpa_s->last_scan_req != MANUAL_SCAN_REQ && +#ifdef CONFIG_AP + !wpa_s->ap_iface && +#endif /* CONFIG_AP */ wpa_s->conf->ap_scan == 2) { wpa_s->connect_without_scan = NULL; wpa_s->prev_scan_wildcard = 0; @@ -899,10 +937,8 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) wpa_dbg(wpa_s, MSG_DEBUG, "Starting AP scan for wildcard " "SSID"); } -#ifdef CONFIG_P2P -ssid_list_set: -#endif /* CONFIG_P2P */ +ssid_list_set: wpa_supplicant_optimize_freqs(wpa_s, ¶ms); extra_ie = wpa_supplicant_extra_ies(wpa_s); @@ -1652,7 +1688,7 @@ static int wpa_scan_result_compar(const void *a, const void *b) snr_a_full = wa->snr; snr_a = MIN(wa->snr, GREAT_SNR); snr_b_full = wb->snr; - snr_b = MIN(wa->snr, GREAT_SNR); + snr_b = MIN(wb->snr, GREAT_SNR); } else { /* Level is not in dBm, so we can't calculate * SNR. Just use raw level (units unknown). */ diff --git a/contrib/wpa/wpa_supplicant/sme.c b/contrib/wpa/wpa_supplicant/sme.c index 1788113..f2e5a43 100644 --- a/contrib/wpa/wpa_supplicant/sme.c +++ b/contrib/wpa/wpa_supplicant/sme.c @@ -67,7 +67,7 @@ static int sme_set_sae_group(struct wpa_supplicant *wpa_s) for (;;) { int group = groups[wpa_s->sme.sae_group_index]; - if (group < 0) + if (group <= 0) break; if (sae_set_group(&wpa_s->sme.sae, group) == 0) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: Selected SAE group %d", @@ -438,6 +438,21 @@ 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) { @@ -583,7 +598,8 @@ static void sme_auth_start_cb(struct wpa_radio_work *work, int deinit) wpa_s->connect_work = work; if (cwork->bss_removed || - !wpas_valid_bss_ssid(wpa_s, cwork->bss, cwork->ssid)) { + !wpas_valid_bss_ssid(wpa_s, cwork->bss, cwork->ssid) || + wpas_network_disabled(wpa_s, cwork->ssid)) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: BSS/SSID entry for authentication not valid anymore - drop connection attempt"); wpas_connect_work_done(wpa_s); return; @@ -698,6 +714,8 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction, return -1; if (auth_transaction == 1) { + u16 res; + groups = wpa_s->conf->sae_groups; wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE commit"); @@ -708,8 +726,14 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction, return -1; if (groups && groups[0] <= 0) groups = NULL; - if (sae_parse_commit(&wpa_s->sme.sae, data, len, NULL, NULL, - groups) != WLAN_STATUS_SUCCESS) + res = sae_parse_commit(&wpa_s->sme.sae, data, len, NULL, NULL, + groups); + if (res == SAE_SILENTLY_DISCARD) { + wpa_printf(MSG_DEBUG, + "SAE: Drop commit message due to reflection attack"); + return 0; + } + if (res != WLAN_STATUS_SUCCESS) return -1; if (sae_process_commit(&wpa_s->sme.sae) < 0) { @@ -793,8 +817,22 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) #endif /* CONFIG_SAE */ if (data->auth.status_code != WLAN_STATUS_SUCCESS) { - wpa_dbg(wpa_s, MSG_DEBUG, "SME: Authentication failed (status " - "code %d)", data->auth.status_code); + char *ie_txt = NULL; + + if (data->auth.ies && data->auth.ies_len) { + size_t buflen = 2 * data->auth.ies_len + 1; + ie_txt = os_malloc(buflen); + if (ie_txt) { + wpa_snprintf_hex(ie_txt, buflen, data->auth.ies, + data->auth.ies_len); + } + } + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_AUTH_REJECT MACSTR + " auth_type=%u auth_transaction=%u status_code=%u ie=%s", + MAC2STR(data->auth.peer), data->auth.auth_type, + data->auth.auth_transaction, data->auth.status_code, + ie_txt); + os_free(ie_txt); if (data->auth.status_code != WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG || @@ -831,12 +869,20 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) #ifdef CONFIG_IEEE80211R if (data->auth.auth_type == WLAN_AUTH_FT) { - union wpa_event_data edata; - os_memset(&edata, 0, sizeof(edata)); - edata.ft_ies.ies = data->auth.ies; - edata.ft_ies.ies_len = data->auth.ies_len; - os_memcpy(edata.ft_ies.target_ap, data->auth.peer, ETH_ALEN); - wpa_supplicant_event(wpa_s, EVENT_FT_RESPONSE, &edata); + if (wpa_ft_process_response(wpa_s->wpa, data->auth.ies, + data->auth.ies_len, 0, + data->auth.peer, NULL, 0) < 0) { + wpa_dbg(wpa_s, MSG_DEBUG, + "SME: FT Authentication response processing failed"); + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" + MACSTR + " reason=%d locally_generated=1", + MAC2STR(wpa_s->pending_bssid), + WLAN_REASON_DEAUTH_LEAVING); + wpas_connection_failed(wpa_s, wpa_s->pending_bssid); + wpa_supplicant_mark_disassoc(wpa_s); + return; + } } #endif /* CONFIG_IEEE80211R */ diff --git a/contrib/wpa/wpa_supplicant/wpa_cli.c b/contrib/wpa/wpa_supplicant/wpa_cli.c index 5a0af0d..7ddae3d 100644 --- a/contrib/wpa/wpa_supplicant/wpa_cli.c +++ b/contrib/wpa/wpa_supplicant/wpa_cli.c @@ -26,16 +26,16 @@ #endif /* ANDROID */ -static const char *wpa_cli_version = +static const char *const wpa_cli_version = "wpa_cli v" VERSION_STR "\n" "Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> and contributors"; -static const char *wpa_cli_license = +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 *wpa_cli_full_license = +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" @@ -76,6 +76,7 @@ static int wpa_cli_last_id = 0; #define CONFIG_CTRL_IFACE_DIR "/var/run/wpa_supplicant" #endif /* CONFIG_CTRL_IFACE_DIR */ static const char *ctrl_iface_dir = CONFIG_CTRL_IFACE_DIR; +static const char *client_socket_dir = NULL; static char *ctrl_ifname = NULL; static const char *pid_file = NULL; static const char *action_file = NULL; @@ -92,6 +93,7 @@ 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 */ static DEFINE_DL_LIST(ifnames); /* struct cli_txt_entry */ +static DEFINE_DL_LIST(networks); /* struct cli_txt_entry */ static void print_help(const char *cmd); @@ -99,13 +101,16 @@ static void wpa_cli_mon_receive(int sock, void *eloop_ctx, void *sock_ctx); static void wpa_cli_close_connection(void); static char * wpa_cli_get_default_ifname(void); static char ** wpa_list_cmd_list(void); +static void update_networks(struct wpa_ctrl *ctrl); static void usage(void) { printf("wpa_cli [-p<path to ctrl sockets>] [-i<ifname>] [-hvB] " "[-a<action file>] \\\n" - " [-P<pid file>] [-g<global ctrl>] [-G<ping interval>] " + " [-P<pid file>] [-g<global ctrl>] [-G<ping interval>] " + "\\\n" + " [-s<wpa_client_socket_file_path>] " "[command..]\n" " -h = help (show this usage text)\n" " -v = shown version information\n" @@ -168,11 +173,12 @@ static void cli_txt_list_del_addr(struct dl_list *txt_list, const char *txt) #ifdef CONFIG_P2P -static void cli_txt_list_del_word(struct dl_list *txt_list, const char *txt) +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, ' '); + end = os_strchr(txt, separator); if (end == NULL) end = txt + os_strlen(txt); buf = dup_binstr(txt, end - txt); @@ -213,14 +219,16 @@ static int cli_txt_list_add_addr(struct dl_list *txt_list, const char *txt) 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) +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, ' '); + end = os_strchr(txt, separator); if (end == NULL) end = txt + os_strlen(txt); buf = dup_binstr(txt, end - txt); @@ -230,7 +238,6 @@ static int cli_txt_list_add_word(struct dl_list *txt_list, const char *txt) os_free(buf); return ret; } -#endif /* CONFIG_P2P */ static char ** cli_txt_list_array(struct dl_list *txt_list) @@ -326,6 +333,13 @@ static int wpa_cli_open_connection(const char *ifname, int attach) } #endif /* ANDROID */ + if (client_socket_dir && client_socket_dir[0] && + access(client_socket_dir, F_OK) < 0) { + perror(client_socket_dir); + os_free(cfile); + return -1; + } + if (cfile == NULL) { flen = os_strlen(ctrl_iface_dir) + os_strlen(ifname) + 2; cfile = os_malloc(flen); @@ -339,14 +353,14 @@ static int wpa_cli_open_connection(const char *ifname, int attach) } } - ctrl_conn = wpa_ctrl_open(cfile); + ctrl_conn = wpa_ctrl_open2(cfile, client_socket_dir); if (ctrl_conn == NULL) { os_free(cfile); return -1; } if (attach && interactive) - mon_conn = wpa_ctrl_open(cfile); + mon_conn = wpa_ctrl_open2(cfile, client_socket_dir); else mon_conn = NULL; os_free(cfile); @@ -498,6 +512,10 @@ static int wpa_cli_cmd_status(struct wpa_ctrl *ctrl, int argc, char *argv[]) return wpa_ctrl_command(ctrl, "STATUS-WPS"); if (argc > 0 && os_strcmp(argv[0], "driver") == 0) return wpa_ctrl_command(ctrl, "STATUS-DRIVER"); +#ifdef ANDROID + if (argc > 0 && os_strcmp(argv[0], "no_events") == 0) + return wpa_ctrl_command(ctrl, "STATUS-NO_EVENTS"); +#endif /* ANDROID */ return wpa_ctrl_command(ctrl, "STATUS"); } @@ -608,35 +626,58 @@ static char ** wpa_cli_complete_set(const char *str, int pos) "uapsd", "ps", "wifi_display", "bssid_filter", "disallow_aps", "no_keep_alive", /* global configuration parameters */ - "eapol_version", "ap_scan", "disable_scan_offload", - "fast_reauth", "opensc_engine_path", "pkcs11_engine_path", - "pkcs11_module_path", "openssl_ciphers", - "pcsc_reader", "pcsc_pin", - "driver_param", "dot11RSNAConfigPMKLifetime", +#ifdef CONFIG_CTRL_IFACE + "ctrl_interface", "no_ctrl_interface", "ctrl_interface_group", +#endif /* CONFIG_CTRL_IFACE */ + "eapol_version", "ap_scan", "bgscan", +#ifdef CONFIG_MESH + "user_mpm", "max_peer_links", "mesh_max_inactivity", + "dot11RSNASAERetransPeriod", +#endif /* CONFIG_MESH */ + "disable_scan_offload", "fast_reauth", "opensc_engine_path", + "pkcs11_engine_path", "pkcs11_module_path", "openssl_ciphers", + "pcsc_reader", "pcsc_pin", "external_sim", "driver_param", + "dot11RSNAConfigPMKLifetime", "dot11RSNAConfigPMKReauthThreshold", "dot11RSNAConfigSATimeout", - "update_config", "load_dynamic_eap", "uuid", "device_name", - "manufacturer", "model_name", "model_number", "serial_number", - "device_type", "os_version", "config_methods", - "wps_cred_processing", "wps_vendor_ext_m1", "sec_device_type", +#ifndef CONFIG_NO_CONFIG_WRITE + "update_config", +#endif /* CONFIG_NO_CONFIG_WRITE */ + "load_dynamic_eap", +#ifdef CONFIG_WPS + "uuid", "device_name", "manufacturer", "model_name", + "model_number", "serial_number", "device_type", "os_version", + "config_methods", "wps_cred_processing", "wps_vendor_ext_m1", +#endif /* CONFIG_WPS */ +#ifdef CONFIG_P2P + "sec_device_type", "p2p_listen_reg_class", "p2p_listen_channel", - "p2p_oper_reg_class", "p2p_oper_channel", - "p2p_go_intent", "p2p_ssid_postfix", "persistent_reconnect", - "p2p_intra_bss", "p2p_group_idle", "p2p_pref_chan", - "p2p_no_go_freq", - "p2p_go_ht40", "p2p_disabled", "p2p_no_group_iface", - "p2p_go_vht", - "p2p_ignore_shared_freq", "country", "bss_max_count", - "bss_expiration_age", "bss_expiration_scan_count", - "filter_ssids", "filter_rssi", "max_num_sta", - "disassoc_low_ack", "hs20", "interworking", "hessid", - "access_network_type", "pbc_in_m1", "autoscan", - "wps_nfc_dev_pw_id", "wps_nfc_dh_pubkey", "wps_nfc_dh_privkey", - "wps_nfc_dev_pw", "ext_password_backend", + "p2p_oper_reg_class", "p2p_oper_channel", "p2p_go_intent", + "p2p_ssid_postfix", "persistent_reconnect", "p2p_intra_bss", + "p2p_group_idle", "p2p_passphrase_len", "p2p_pref_chan", + "p2p_no_go_freq", "p2p_add_cli_chan", + "p2p_optimize_listen_chan", "p2p_go_ht40", "p2p_go_vht", + "p2p_disabled", "p2p_go_ctwindow", "p2p_no_group_iface", + "p2p_ignore_shared_freq", "ip_addr_go", "ip_addr_mask", + "ip_addr_start", "ip_addr_end", +#endif /* CONFIG_P2P */ + "country", "bss_max_count", "bss_expiration_age", + "bss_expiration_scan_count", "filter_ssids", "filter_rssi", + "max_num_sta", "disassoc_low_ack", +#ifdef CONFIG_HS20 + "hs20", +#endif /* CONFIG_HS20 */ + "interworking", "hessid", "access_network_type", "pbc_in_m1", + "autoscan", "wps_nfc_dev_pw_id", "wps_nfc_dh_pubkey", + "wps_nfc_dh_privkey", "wps_nfc_dev_pw", "ext_password_backend", "p2p_go_max_inactivity", "auto_interworking", "okc", "pmf", - "sae_groups", "dtim_period", "beacon_int", "ap_vendor_elements", - "ignore_old_scan_res", "freq_list", "external_sim", - "tdls_external_control", "p2p_search_delay" + "sae_groups", "dtim_period", "beacon_int", + "ap_vendor_elements", "ignore_old_scan_res", "freq_list", + "scan_cur_freq", "sched_scan_interval", + "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" }; int i, num_fields = ARRAY_SIZE(fields); @@ -670,6 +711,74 @@ static int wpa_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +static char ** wpa_cli_complete_get(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + const char *fields[] = { +#ifdef CONFIG_CTRL_IFACE + "ctrl_interface", "ctrl_interface_group", +#endif /* CONFIG_CTRL_IFACE */ + "eapol_version", "ap_scan", +#ifdef CONFIG_MESH + "user_mpm", "max_peer_links", "mesh_max_inactivity", +#endif /* CONFIG_MESH */ + "disable_scan_offload", "fast_reauth", "opensc_engine_path", + "pkcs11_engine_path", "pkcs11_module_path", "openssl_ciphers", + "pcsc_reader", "pcsc_pin", "external_sim", "driver_param", + "dot11RSNAConfigPMKLifetime", + "dot11RSNAConfigPMKReauthThreshold", + "dot11RSNAConfigSATimeout", +#ifndef CONFIG_NO_CONFIG_WRITE + "update_config", +#endif /* CONFIG_NO_CONFIG_WRITE */ +#ifdef CONFIG_WPS + "device_name", "manufacturer", "model_name", "model_number", + "serial_number", "config_methods", "wps_cred_processing", +#endif /* CONFIG_WPS */ +#ifdef CONFIG_P2P + "p2p_listen_reg_class", "p2p_listen_channel", + "p2p_oper_reg_class", "p2p_oper_channel", "p2p_go_intent", + "p2p_ssid_postfix", "persistent_reconnect", "p2p_intra_bss", + "p2p_group_idle", "p2p_passphrase_len", "p2p_add_cli_chan", + "p2p_optimize_listen_chan", "p2p_go_ht40", "p2p_go_vht", + "p2p_disabled", "p2p_go_ctwindow", "p2p_no_group_iface", + "p2p_ignore_shared_freq", "ip_addr_go", "ip_addr_mask", + "ip_addr_start", "ip_addr_end", +#endif /* CONFIG_P2P */ + "bss_max_count", "bss_expiration_age", + "bss_expiration_scan_count", "filter_ssids", "filter_rssi", + "max_num_sta", "disassoc_low_ack", +#ifdef CONFIG_HS20 + "hs20", +#endif /* CONFIG_HS20 */ + "interworking", "access_network_type", "pbc_in_m1", "autoscan", + "wps_nfc_dev_pw_id", "ext_password_backend", + "p2p_go_max_inactivity", "auto_interworking", "okc", "pmf", + "dtim_period", "beacon_int", "ignore_old_scan_res", + "scan_cur_freq", "sched_scan_interval", + "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" + }; + int i, num_fields = ARRAY_SIZE(fields); + + if (arg == 1) { + char **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; + } + return res; + } + + return NULL; +} + + static int wpa_cli_cmd_logoff(struct wpa_ctrl *ctrl, int argc, char *argv[]) { return wpa_ctrl_command(ctrl, "LOGOFF"); @@ -873,12 +982,12 @@ static int wpa_cli_cmd_wps_reg(struct wpa_ctrl *ctrl, int argc, char *argv[]) res = os_snprintf(cmd, sizeof(cmd), "WPS_REG %s %s", argv[0], argv[1]); else if (argc == 5 || argc == 6) { - char ssid_hex[2 * 32 + 1]; + char ssid_hex[2 * SSID_MAX_LEN + 1]; char key_hex[2 * 64 + 1]; int i; ssid_hex[0] = '\0'; - for (i = 0; i < 32; i++) { + for (i = 0; i < SSID_MAX_LEN; i++) { if (argv[2][i] == '\0') break; os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[2][i]); @@ -1002,12 +1111,12 @@ static int wpa_cli_cmd_wps_er_config(struct wpa_ctrl *ctrl, int argc, int res; if (argc == 5 || argc == 6) { - char ssid_hex[2 * 32 + 1]; + char ssid_hex[2 * SSID_MAX_LEN + 1]; char key_hex[2 * 64 + 1]; int i; ssid_hex[0] = '\0'; - for (i = 0; i < 32; i++) { + for (i = 0; i < SSID_MAX_LEN; i++) { if (argv[2][i] == '\0') break; os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[2][i]); @@ -1361,14 +1470,20 @@ static int wpa_cli_cmd_disable_network(struct wpa_ctrl *ctrl, int argc, static int wpa_cli_cmd_add_network(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - return wpa_ctrl_command(ctrl, "ADD_NETWORK"); + int res = wpa_ctrl_command(ctrl, "ADD_NETWORK"); + if (interactive) + update_networks(ctrl); + return res; } static int wpa_cli_cmd_remove_network(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - return wpa_cli_cmd(ctrl, "REMOVE_NETWORK", 1, argc, argv); + int res = wpa_cli_cmd(ctrl, "REMOVE_NETWORK", 1, argc, argv); + if (interactive) + update_networks(ctrl); + return res; } @@ -1429,6 +1544,105 @@ static int wpa_cli_cmd_get_network(struct wpa_ctrl *ctrl, int argc, } +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", +#ifdef IEEE8021X_EAPOL + "eap", "identity", "anonymous_identity", "password", "ca_cert", + "ca_path", "client_cert", "private_key", "private_key_passwd", + "dh_file", "subject_match", "altsubject_match", + "domain_suffix_match", "domain_match", "ca_cert2", "ca_path2", + "client_cert2", "private_key2", "private_key2_passwd", + "dh_file2", "subject_match2", "altsubject_match2", + "domain_suffix_match2", "domain_match2", "phase1", "phase2", + "pcsc", "pin", "engine_id", "key_id", "cert_id", "ca_cert_id", + "pin2", "engine2_id", "key2_id", "cert2_id", "ca_cert2_id", + "engine", "engine2", "eapol_flags", "sim_num", + "openssl_ciphers", "erp", +#endif /* IEEE8021X_EAPOL */ + "wep_key0", "wep_key1", "wep_key2", "wep_key3", + "wep_tx_keyidx", "priority", +#ifdef IEEE8021X_EAPOL + "eap_workaround", "pac_file", "fragment_size", "ocsp", +#endif /* IEEE8021X_EAPOL */ +#ifdef CONFIG_MESH + "mode", "no_auto_peer", +#else /* CONFIG_MESH */ + "mode", +#endif /* CONFIG_MESH */ + "proactive_key_caching", "disabled", "id_str", +#ifdef CONFIG_IEEE80211W + "ieee80211w", +#endif /* CONFIG_IEEE80211W */ + "peerkey", "mixed_cell", "frequency", "fixed_freq", +#ifdef CONFIG_MESH + "mesh_basic_rates", "dot11MeshMaxRetries", + "dot11MeshRetryTimeout", "dot11MeshConfirmTimeout", + "dot11MeshHoldingTimeout", +#endif /* CONFIG_MESH */ + "wpa_ptk_rekey", "bgscan", "ignore_broadcast_ssid", +#ifdef CONFIG_P2P + "go_p2p_dev_addr", "p2p_client_list", "psk_list", +#endif /* CONFIG_P2P */ +#ifdef CONFIG_HT_OVERRIDES + "disable_ht", "disable_ht40", "disable_sgi", "disable_ldpc", + "ht40_intolerant", "disable_max_amsdu", "ampdu_factor", + "ampdu_density", "ht_mcs", +#endif /* CONFIG_HT_OVERRIDES */ +#ifdef CONFIG_VHT_OVERRIDES + "disable_vht", "vht_capa", "vht_capa_mask", "vht_rx_mcs_nss_1", + "vht_rx_mcs_nss_2", "vht_rx_mcs_nss_3", "vht_rx_mcs_nss_4", + "vht_rx_mcs_nss_5", "vht_rx_mcs_nss_6", "vht_rx_mcs_nss_7", + "vht_rx_mcs_nss_8", "vht_tx_mcs_nss_1", "vht_tx_mcs_nss_2", + "vht_tx_mcs_nss_3", "vht_tx_mcs_nss_4", "vht_tx_mcs_nss_5", + "vht_tx_mcs_nss_6", "vht_tx_mcs_nss_7", "vht_tx_mcs_nss_8", +#endif /* CONFIG_VHT_OVERRIDES */ + "ap_max_inactivity", "dtim_period", "beacon_int", +#ifdef CONFIG_MACSEC + "macsec_policy", +#endif /* CONFIG_MACSEC */ +#ifdef CONFIG_HS20 + "update_identifier", +#endif /* CONFIG_HS20 */ + "mac_addr" +}; + + +static char ** wpa_cli_complete_network(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + int i, num_fields = ARRAY_SIZE(network_fields); + char **res = NULL; + + switch (arg) { + case 1: + res = cli_txt_list_array(&networks); + break; + case 2: + res = os_calloc(num_fields + 1, sizeof(char *)); + if (res == NULL) + return NULL; + for (i = 0; i < num_fields; i++) { + res[i] = os_strdup(network_fields[i]); + if (res[i] == NULL) + break; + } + } + return res; +} + + +static char ** wpa_cli_complete_network_id(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + if (arg == 1) + return cli_txt_list_array(&networks); + return NULL; +} + + static int wpa_cli_cmd_dup_network(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -1447,6 +1661,31 @@ static int wpa_cli_cmd_dup_network(struct wpa_ctrl *ctrl, int argc, } +static char ** wpa_cli_complete_dup_network(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + int i, num_fields = ARRAY_SIZE(network_fields); + char **res = NULL; + + switch (arg) { + case 1: + case 2: + res = cli_txt_list_array(&networks); + break; + case 3: + res = os_calloc(num_fields + 1, sizeof(char *)); + if (res == NULL) + return NULL; + for (i = 0; i < num_fields; i++) { + res[i] = os_strdup(network_fields[i]); + if (res[i] == NULL) + break; + } + } + return res; +} + + static int wpa_cli_cmd_list_creds(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -1621,20 +1860,20 @@ static int wpa_cli_cmd_interface_add(struct wpa_ctrl *ctrl, int argc, printf("Invalid INTERFACE_ADD command: needs at least one " "argument (interface name)\n" "All arguments: ifname confname driver ctrl_interface " - "driver_param bridge_name\n"); + "driver_param bridge_name [create]\n"); return -1; } /* * INTERFACE_ADD <ifname>TAB<confname>TAB<driver>TAB<ctrl_interface>TAB - * <driver_param>TAB<bridge_name> + * <driver_param>TAB<bridge_name>[TAB<create>] */ res = os_snprintf(cmd, sizeof(cmd), - "INTERFACE_ADD %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", argv[0], argc > 1 ? argv[1] : "", argc > 2 ? argv[2] : "", argc > 3 ? argv[3] : "", argc > 4 ? argv[4] : "", - argc > 5 ? argv[5] : ""); + argc > 5 ? argv[5] : "", argc > 6 ? argv[6] : ""); if (os_snprintf_error(sizeof(cmd), res)) return -1; cmd[sizeof(cmd) - 1] = '\0'; @@ -2431,6 +2670,13 @@ static int wpa_cli_cmd_tdls_teardown(struct wpa_ctrl *ctrl, int argc, } +static int wpa_cli_cmd_tdls_link_status(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "TDLS_LINK_STATUS", 1, argc, argv); +} + + static int wpa_cli_cmd_wmm_ac_addts(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -2570,6 +2816,13 @@ static int wpa_cli_cmd_mac_rand_scan(struct wpa_ctrl *ctrl, int argc, } +static int wpa_cli_cmd_get_pref_freq_list(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "GET_PREF_FREQ_LIST", 1, argc, argv); +} + + enum wpa_cli_cmd_flags { cli_cmd_flag_none = 0x00, cli_cmd_flag_sensitive = 0x01 @@ -2583,7 +2836,7 @@ struct wpa_cli_cmd { const char *usage; }; -static struct wpa_cli_cmd wpa_cli_commands[] = { +static const struct wpa_cli_cmd wpa_cli_commands[] = { { "status", wpa_cli_cmd_status, NULL, cli_cmd_flag_none, "[verbose] = get current WPA/EAPOL/EAP status" }, @@ -2624,7 +2877,7 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { { "dump", wpa_cli_cmd_dump, NULL, cli_cmd_flag_none, "= dump config variables" }, - { "get", wpa_cli_cmd_get, NULL, + { "get", wpa_cli_cmd_get, wpa_cli_complete_get, cli_cmd_flag_none, "<name> = get information" }, { "logon", wpa_cli_cmd_logon, NULL, @@ -2686,29 +2939,33 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { { "list_networks", wpa_cli_cmd_list_networks, NULL, cli_cmd_flag_none, "= list configured networks" }, - { "select_network", wpa_cli_cmd_select_network, NULL, + { "select_network", wpa_cli_cmd_select_network, + wpa_cli_complete_network_id, cli_cmd_flag_none, "<network id> = select a network (disable others)" }, - { "enable_network", wpa_cli_cmd_enable_network, NULL, + { "enable_network", wpa_cli_cmd_enable_network, + wpa_cli_complete_network_id, cli_cmd_flag_none, "<network id> = enable a network" }, - { "disable_network", wpa_cli_cmd_disable_network, NULL, + { "disable_network", wpa_cli_cmd_disable_network, + wpa_cli_complete_network_id, cli_cmd_flag_none, "<network id> = disable a network" }, { "add_network", wpa_cli_cmd_add_network, NULL, cli_cmd_flag_none, "= add a network" }, - { "remove_network", wpa_cli_cmd_remove_network, NULL, + { "remove_network", wpa_cli_cmd_remove_network, + wpa_cli_complete_network_id, cli_cmd_flag_none, "<network id> = remove a network" }, - { "set_network", wpa_cli_cmd_set_network, NULL, + { "set_network", wpa_cli_cmd_set_network, wpa_cli_complete_network, cli_cmd_flag_sensitive, "<network id> <variable> <value> = set network variables (shows\n" " list of variables when run without arguments)" }, - { "get_network", wpa_cli_cmd_get_network, NULL, + { "get_network", wpa_cli_cmd_get_network, wpa_cli_complete_network, cli_cmd_flag_none, "<network id> <variable> = get network variables" }, - { "dup_network", wpa_cli_cmd_dup_network, NULL, + { "dup_network", wpa_cli_cmd_dup_network, wpa_cli_complete_dup_network, cli_cmd_flag_none, "<src network id> <dst network id> <variable> = duplicate network variables" }, @@ -2750,7 +3007,7 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { { "get_capability", wpa_cli_cmd_get_capability, NULL, cli_cmd_flag_none, "<eap/pairwise/group/key_mgmt/proto/auth_alg/channels/freq/modes> " - "= get capabilies" }, + "= get capabilities" }, { "reconfigure", wpa_cli_cmd_reconfigure, NULL, cli_cmd_flag_none, "= force wpa_supplicant to re-read its configuration file" }, @@ -3054,6 +3311,9 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { { "tdls_teardown", wpa_cli_cmd_tdls_teardown, NULL, cli_cmd_flag_none, "<addr> = tear down TDLS with <addr>" }, + { "tdls_link_status", wpa_cli_cmd_tdls_link_status, NULL, + cli_cmd_flag_none, + "<addr> = TDLS link status with <addr>" }, { "wmm_ac_addts", wpa_cli_cmd_wmm_ac_addts, NULL, cli_cmd_flag_none, "<uplink/downlink/bidi> <tsid=0..7> <up=0..7> [nominal_msdu_size=#] " @@ -3117,6 +3377,9 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { "<scan|sched|pno|all> enable=<0/1> [addr=mac-address " "mask=mac-address-mask] = scan MAC randomization" }, + { "get_pref_freq_list", wpa_cli_cmd_get_pref_freq_list, NULL, + cli_cmd_flag_none, + "<interface type> = retrieve preferred freq list for the specified interface type" }, { NULL, NULL, NULL, cli_cmd_flag_none, NULL } }; @@ -3124,7 +3387,7 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { /* * Prints command usage, lines are padded with the specified string. */ -static void print_cmd_help(struct wpa_cli_cmd *cmd, const char *pad) +static void print_cmd_help(const struct wpa_cli_cmd *cmd, const char *pad) { char c; size_t n; @@ -3262,7 +3525,7 @@ static char ** wpa_cli_edit_completion_cb(void *ctx, const char *str, int pos) static int wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - struct wpa_cli_cmd *cmd, *match = NULL; + const struct wpa_cli_cmd *cmd, *match = NULL; int count; int ret = 0; @@ -3347,6 +3610,9 @@ static void wpa_cli_action_process(const char *msg) const char *ifname = ctrl_ifname; char ifname_buf[100]; + if (eloop_terminated()) + return; + pos = msg; if (os_strncmp(pos, "IFNAME=", 7) == 0) { const char *end; @@ -3524,7 +3790,7 @@ static void cli_event(const char *str) s = os_strchr(start, ' '); if (s == NULL) return; - cli_txt_list_add_word(&p2p_groups, s + 1); + cli_txt_list_add_word(&p2p_groups, s + 1, ' '); return; } @@ -3532,7 +3798,7 @@ static void cli_event(const char *str) s = os_strchr(start, ' '); if (s == NULL) return; - cli_txt_list_del_word(&p2p_groups, s + 1); + cli_txt_list_del_word(&p2p_groups, s + 1, ' '); return; } #endif /* CONFIG_P2P */ @@ -3691,7 +3957,11 @@ static void start_edit(void) ps = wpa_ctrl_get_remote_ifname(ctrl_conn); #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ +#ifdef CONFIG_WPA_CLI_HISTORY_DIR + home = CONFIG_WPA_CLI_HISTORY_DIR; +#else /* CONFIG_WPA_CLI_HISTORY_DIR */ home = getenv("HOME"); +#endif /* CONFIG_WPA_CLI_HISTORY_DIR */ if (home) { const char *fname = ".wpa_cli_history"; int hfile_len = os_strlen(home) + 1 + os_strlen(fname) + 1; @@ -3774,6 +4044,38 @@ static void update_ifnames(struct wpa_ctrl *ctrl) } +static void update_networks(struct wpa_ctrl *ctrl) +{ + char buf[4096]; + size_t len = sizeof(buf); + int ret; + char *cmd = "LIST_NETWORKS"; + char *pos, *end; + int header = 1; + + cli_txt_list_flush(&networks); + + if (ctrl == NULL) + return; + ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len, NULL); + if (ret < 0) + return; + buf[len] = '\0'; + + pos = buf; + while (pos) { + end = os_strchr(pos, '\n'); + if (end == NULL) + break; + *end = '\0'; + if (!header) + cli_txt_list_add_word(&networks, pos, '\t'); + header = 0; + pos = end + 1; + } +} + + static void try_connection(void *eloop_ctx, void *timeout_ctx) { if (ctrl_conn) @@ -3794,6 +4096,7 @@ static void try_connection(void *eloop_ctx, void *timeout_ctx) } update_bssid_list(ctrl_conn); + update_networks(ctrl_conn); if (warning_displayed) printf("Connection established.\n"); @@ -3815,6 +4118,7 @@ static void wpa_cli_interactive(void) cli_txt_list_flush(&p2p_groups); cli_txt_list_flush(&bsses); cli_txt_list_flush(&ifnames); + cli_txt_list_flush(&networks); if (edit_started) edit_deinit(hfile, wpa_cli_edit_filter_history_cb); os_free(hfile); @@ -3823,45 +4127,49 @@ static void wpa_cli_interactive(void) } +static void wpa_cli_action_ping(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_ctrl *ctrl = eloop_ctx; + char buf[256]; + size_t len; + + /* verify that connection is still working */ + len = sizeof(buf) - 1; + if (wpa_ctrl_request(ctrl, "PING", 4, buf, &len, + wpa_cli_action_cb) < 0 || + len < 4 || os_memcmp(buf, "PONG", 4) != 0) { + printf("wpa_supplicant did not reply to PING command - exiting\n"); + eloop_terminate(); + return; + } + eloop_register_timeout(ping_interval, 0, wpa_cli_action_ping, + ctrl, NULL); +} + + +static void wpa_cli_action_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct wpa_ctrl *ctrl = eloop_ctx; + + wpa_cli_recv_pending(ctrl, 1); +} + + static void wpa_cli_action(struct wpa_ctrl *ctrl) { #ifdef CONFIG_ANSI_C_EXTRA /* TODO: ANSI C version(?) */ printf("Action processing not supported in ANSI C build.\n"); #else /* CONFIG_ANSI_C_EXTRA */ - fd_set rfds; - int fd, res; - struct timeval tv; - char buf[256]; /* note: large enough to fit in unsolicited messages */ - size_t len; + int fd; fd = wpa_ctrl_get_fd(ctrl); - - while (!wpa_cli_quit) { - FD_ZERO(&rfds); - FD_SET(fd, &rfds); - tv.tv_sec = ping_interval; - tv.tv_usec = 0; - res = select(fd + 1, &rfds, NULL, NULL, &tv); - if (res < 0 && errno != EINTR) { - perror("select"); - break; - } - - if (FD_ISSET(fd, &rfds)) - wpa_cli_recv_pending(ctrl, 1); - else { - /* verify that connection is still working */ - len = sizeof(buf) - 1; - if (wpa_ctrl_request(ctrl, "PING", 4, buf, &len, - wpa_cli_action_cb) < 0 || - len < 4 || os_memcmp(buf, "PONG", 4) != 0) { - printf("wpa_supplicant did not reply to PING " - "command - exiting\n"); - break; - } - } - } + eloop_register_timeout(ping_interval, 0, wpa_cli_action_ping, + ctrl, NULL); + eloop_register_read_sock(fd, wpa_cli_action_receive, ctrl, NULL); + eloop_run(); + eloop_cancel_timeout(wpa_cli_action_ping, ctrl, NULL); + eloop_unregister_read_sock(fd); #endif /* CONFIG_ANSI_C_EXTRA */ } @@ -3886,18 +4194,17 @@ static char * wpa_cli_get_default_ifname(void) { char *ifname = NULL; +#ifdef ANDROID + char ifprop[PROPERTY_VALUE_MAX]; + if (property_get("wifi.interface", ifprop, NULL) != 0) { + ifname = os_strdup(ifprop); + printf("Using interface '%s'\n", ifname ? ifname : "N/A"); + } +#else /* ANDROID */ #ifdef CONFIG_CTRL_IFACE_UNIX struct dirent *dent; DIR *dir = opendir(ctrl_iface_dir); if (!dir) { -#ifdef ANDROID - char ifprop[PROPERTY_VALUE_MAX]; - if (property_get("wifi.interface", ifprop, NULL) != 0) { - ifname = os_strdup(ifprop); - printf("Using interface '%s'\n", ifname); - return ifname; - } -#endif /* ANDROID */ return NULL; } while ((dent = readdir(dir))) { @@ -3941,6 +4248,7 @@ static char * wpa_cli_get_default_ifname(void) } wpa_ctrl_close(ctrl); #endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ +#endif /* ANDROID */ return ifname; } @@ -3957,7 +4265,7 @@ int main(int argc, char *argv[]) return -1; for (;;) { - c = getopt(argc, argv, "a:Bg:G:hi:p:P:v"); + c = getopt(argc, argv, "a:Bg:G:hi:p:P:s:v"); if (c < 0) break; switch (c) { @@ -3989,6 +4297,9 @@ int main(int argc, char *argv[]) case 'P': pid_file = optarg; break; + case 's': + client_socket_dir = optarg; + break; default: usage(); return -1; diff --git a/contrib/wpa/wpa_supplicant/wpa_priv.c b/contrib/wpa/wpa_supplicant/wpa_priv.c index ac38d69..850ec40 100644 --- a/contrib/wpa/wpa_supplicant/wpa_priv.c +++ b/contrib/wpa/wpa_supplicant/wpa_priv.c @@ -29,8 +29,9 @@ struct wpa_priv_interface { char *sock_name; int fd; - struct wpa_driver_ops *driver; + const struct wpa_driver_ops *driver; void *drv_priv; + void *drv_global_priv; struct sockaddr_un drv_addr; int wpas_registered; @@ -48,6 +49,10 @@ static void wpa_priv_cmd_register(struct wpa_priv_interface *iface, if (iface->driver->deinit) iface->driver->deinit(iface->drv_priv); iface->drv_priv = NULL; + if (iface->drv_global_priv) { + iface->driver->global_deinit(iface->drv_global_priv); + iface->drv_global_priv = NULL; + } iface->wpas_registered = 0; } @@ -58,10 +63,24 @@ static void wpa_priv_cmd_register(struct wpa_priv_interface *iface, iface->l2 = NULL; } - if (iface->driver->init == NULL) + if (iface->driver->init2) { + if (iface->driver->global_init) { + iface->drv_global_priv = iface->driver->global_init(); + if (!iface->drv_global_priv) { + wpa_printf(MSG_INFO, + "Failed to initialize driver global context"); + return; + } + } else { + iface->drv_global_priv = NULL; + } + iface->drv_priv = iface->driver->init2(iface, iface->ifname, + iface->drv_global_priv); + } else if (iface->driver->init) { + iface->drv_priv = iface->driver->init(iface, iface->ifname); + } else { return; - - iface->drv_priv = iface->driver->init(iface, iface->ifname); + } if (iface->drv_priv == NULL) { wpa_printf(MSG_DEBUG, "Failed to initialize driver wrapper"); return; @@ -87,6 +106,10 @@ static void wpa_priv_cmd_unregister(struct wpa_priv_interface *iface, if (iface->driver->deinit) iface->driver->deinit(iface->drv_priv); iface->drv_priv = NULL; + if (iface->drv_global_priv) { + iface->driver->global_deinit(iface->drv_global_priv); + iface->drv_global_priv = NULL; + } iface->wpas_registered = 0; } } @@ -172,6 +195,58 @@ static void wpa_priv_cmd_get_scan_results(struct wpa_priv_interface *iface, } +static void wpa_priv_cmd_authenticate(struct wpa_priv_interface *iface, + void *buf, size_t len) +{ + struct wpa_driver_auth_params params; + struct privsep_cmd_authenticate *auth; + int res, i; + + if (iface->drv_priv == NULL || iface->driver->authenticate == NULL) + return; + + if (len < sizeof(*auth)) { + wpa_printf(MSG_DEBUG, "Invalid authentication request"); + return; + } + + auth = buf; + if (sizeof(*auth) + auth->ie_len + auth->sae_data_len > len) { + wpa_printf(MSG_DEBUG, "Authentication request overflow"); + return; + } + + os_memset(¶ms, 0, sizeof(params)); + params.freq = auth->freq; + params.bssid = auth->bssid; + params.ssid = auth->ssid; + if (auth->ssid_len > SSID_MAX_LEN) + return; + params.ssid_len = auth->ssid_len; + params.auth_alg = auth->auth_alg; + for (i = 0; i < 4; i++) { + if (auth->wep_key_len[i]) { + params.wep_key[i] = auth->wep_key[i]; + params.wep_key_len[i] = auth->wep_key_len[i]; + } + } + params.wep_tx_keyidx = auth->wep_tx_keyidx; + params.local_state_change = auth->local_state_change; + params.p2p = auth->p2p; + if (auth->ie_len) { + params.ie = (u8 *) (auth + 1); + params.ie_len = auth->ie_len; + } + if (auth->sae_data_len) { + params.sae_data = ((u8 *) (auth + 1)) + auth->ie_len; + params.sae_data_len = auth->sae_data_len; + } + + res = iface->driver->authenticate(iface->drv_priv, ¶ms); + wpa_printf(MSG_DEBUG, "drv->authenticate: res=%d", res); +} + + static void wpa_priv_cmd_associate(struct wpa_priv_interface *iface, void *buf, size_t len) { @@ -199,7 +274,7 @@ static void wpa_priv_cmd_associate(struct wpa_priv_interface *iface, if (bssid[0] | bssid[1] | bssid[2] | bssid[3] | bssid[4] | bssid[5]) params.bssid = bssid; params.ssid = assoc->ssid; - if (assoc->ssid_len > 32) + if (assoc->ssid_len > SSID_MAX_LEN) return; params.ssid_len = assoc->ssid_len; params.freq.mode = assoc->hwmode; @@ -244,7 +319,7 @@ fail: static void wpa_priv_cmd_get_ssid(struct wpa_priv_interface *iface, struct sockaddr_un *from) { - u8 ssid[sizeof(int) + 32]; + u8 ssid[sizeof(int) + SSID_MAX_LEN]; int res; if (iface->drv_priv == NULL) @@ -254,7 +329,7 @@ static void wpa_priv_cmd_get_ssid(struct wpa_priv_interface *iface, goto fail; res = iface->driver->get_ssid(iface->drv_priv, &ssid[sizeof(int)]); - if (res < 0 || res > 32) + if (res < 0 || res > SSID_MAX_LEN) goto fail; os_memcpy(ssid, &res, sizeof(int)); @@ -307,6 +382,10 @@ static void wpa_priv_cmd_get_capa(struct wpa_priv_interface *iface, iface->driver->get_capa(iface->drv_priv, &capa) < 0) goto fail; + /* For now, no support for passing extended_capa pointers */ + capa.extended_capa = NULL; + capa.extended_capa_mask = NULL; + capa.extended_capa_len = 0; sendto(iface->fd, &capa, sizeof(capa), 0, (struct sockaddr *) from, sizeof(*from)); return; @@ -356,7 +435,8 @@ static void wpa_priv_cmd_l2_register(struct wpa_priv_interface *iface, } proto = reg_cmd[0]; - if (proto != ETH_P_EAPOL && proto != ETH_P_RSN_PREAUTH) { + if (proto != ETH_P_EAPOL && proto != ETH_P_RSN_PREAUTH && + proto != ETH_P_80211_ENCAP) { wpa_printf(MSG_DEBUG, "Refused l2_packet connection for " "ethertype 0x%x", proto); return; @@ -529,6 +609,9 @@ static void wpa_priv_receive(int sock, void *eloop_ctx, void *sock_ctx) pos[cmd_len] = '\0'; wpa_priv_cmd_set_country(iface, pos); break; + case PRIVSEP_CMD_AUTHENTICATE: + wpa_priv_cmd_authenticate(iface, cmd_buf, cmd_len); + break; } } @@ -698,6 +781,36 @@ static int wpa_priv_send_event(struct wpa_priv_interface *iface, int event, } +static void wpa_priv_send_auth(struct wpa_priv_interface *iface, + union wpa_event_data *data) +{ + size_t buflen = sizeof(struct privsep_event_auth) + data->auth.ies_len; + struct privsep_event_auth *auth; + u8 *buf, *pos; + + buf = os_malloc(buflen); + if (buf == NULL) + return; + + auth = (struct privsep_event_auth *) buf; + pos = (u8 *) (auth + 1); + + os_memcpy(auth->peer, data->auth.peer, ETH_ALEN); + os_memcpy(auth->bssid, data->auth.bssid, ETH_ALEN); + auth->auth_type = data->auth.auth_type; + auth->auth_transaction = data->auth.auth_transaction; + auth->status_code = data->auth.status_code; + if (data->auth.ies) { + os_memcpy(pos, data->auth.ies, data->auth.ies_len); + auth->ies_len = data->auth.ies_len; + } + + wpa_priv_send_event(iface, PRIVSEP_EVENT_AUTH, buf, buflen); + + os_free(buf); +} + + static void wpa_priv_send_assoc(struct wpa_priv_interface *iface, int event, union wpa_event_data *data) { @@ -851,6 +964,10 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, &data->michael_mic_failure.unicast, sizeof(int)); break; + case EVENT_SCAN_STARTED: + wpa_priv_send_event(iface, PRIVSEP_EVENT_SCAN_STARTED, NULL, + 0); + break; case EVENT_SCAN_RESULTS: wpa_priv_send_event(iface, PRIVSEP_EVENT_SCAN_RESULTS, NULL, 0); @@ -874,9 +991,12 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, case EVENT_FT_RESPONSE: wpa_priv_send_ft_response(iface, data); break; + case EVENT_AUTH: + wpa_priv_send_auth(iface, data); + break; default: - wpa_printf(MSG_DEBUG, "Unsupported driver event %d - TODO", - event); + wpa_printf(MSG_DEBUG, "Unsupported driver event %d (%s) - TODO", + event, event_to_string(event)); break; } } @@ -944,8 +1064,9 @@ static void usage(void) "contributors\n" "\n" "usage:\n" - " wpa_priv [-Bdd] [-P<pid file>] <driver:ifname> " - "[driver:ifname ...]\n"); + " wpa_priv [-Bdd] [-c<ctrl dir>] [-P<pid file>] " + "<driver:ifname> \\\n" + " [driver:ifname ...]\n"); } @@ -982,20 +1103,20 @@ int main(int argc, char *argv[]) break; default: usage(); - goto out; + goto out2; } } if (optind >= argc) { usage(); - goto out; + goto out2; } wpa_printf(MSG_DEBUG, "wpa_priv control directory: '%s'", ctrl_dir); if (eloop_init()) { wpa_printf(MSG_ERROR, "Failed to initialize event loop"); - goto out; + goto out2; } for (i = optind; i < argc; i++) { @@ -1025,7 +1146,9 @@ out: eloop_destroy(); - os_daemonize_terminate(pid_file); +out2: + if (daemonize) + os_daemonize_terminate(pid_file); os_free(pid_file); os_program_deinit(); diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant.c b/contrib/wpa/wpa_supplicant/wpa_supplicant.c index 19fb890..ef55fdc 100644 --- a/contrib/wpa/wpa_supplicant/wpa_supplicant.c +++ b/contrib/wpa/wpa_supplicant/wpa_supplicant.c @@ -35,6 +35,7 @@ #include "common/ieee802_11_defs.h" #include "common/hw_features_common.h" #include "p2p/p2p.h" +#include "fst/fst.h" #include "blacklist.h" #include "wpas_glue.h" #include "wps_supplicant.h" @@ -55,11 +56,11 @@ #include "wpas_kay.h" #include "mesh.h" -const char *wpa_supplicant_version = +const char *const wpa_supplicant_version = "wpa_supplicant v" VERSION_STR "\n" "Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> and contributors"; -const char *wpa_supplicant_license = +const char *const wpa_supplicant_license = "This software may be distributed under the terms of the BSD license.\n" "See README for more details.\n" #ifdef EAP_TLS_OPENSSL @@ -70,16 +71,16 @@ const char *wpa_supplicant_license = #ifndef CONFIG_NO_STDOUT_DEBUG /* Long text divided into parts in order to fit in C89 strings size limits. */ -const char *wpa_supplicant_full_license1 = +const char *const wpa_supplicant_full_license1 = ""; -const char *wpa_supplicant_full_license2 = +const char *const wpa_supplicant_full_license2 = "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"; -const char *wpa_supplicant_full_license3 = +const char *const wpa_supplicant_full_license3 = "1. Redistributions of source code must retain the above copyright\n" " notice, this list of conditions and the following disclaimer.\n" "\n" @@ -87,7 +88,7 @@ const char *wpa_supplicant_full_license3 = " notice, this list of conditions and the following disclaimer in the\n" " documentation and/or other materials provided with the distribution.\n" "\n"; -const char *wpa_supplicant_full_license4 = +const char *const wpa_supplicant_full_license4 = "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" @@ -96,7 +97,7 @@ const char *wpa_supplicant_full_license4 = "\"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"; -const char *wpa_supplicant_full_license5 = +const char *const wpa_supplicant_full_license5 = "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" @@ -456,6 +457,8 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) wpa_s, NULL); #endif /* CONFIG_DELAYED_MIC_ERROR_REPORT */ + eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL); + wpas_wps_deinit(wpa_s); wpabuf_free(wpa_s->pending_eapol_rx); @@ -491,6 +494,16 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) wpas_mac_addr_rand_scan_clear(wpa_s, MAC_ADDR_RAND_ALL); + /* + * Need to remove any pending gas-query radio work before the + * gas_query_deinit() call because gas_query::work has not yet been set + * for works that have not been started. gas_query_free() will be unable + * to cancel such pending radio works and once the pending gas-query + * radio work eventually gets removed, the deinit notification call to + * gas_query_start_cb() would result in dereferencing freed memory. + */ + if (wpa_s->radio) + radio_remove_works(wpa_s, "gas-query", 0); gas_query_deinit(wpa_s->gas); wpa_s->gas = NULL; @@ -716,6 +729,30 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, wpa_s->normal_scans = 0; } +#ifdef CONFIG_P2P + /* + * P2PS client has to reply to Probe Request frames received on the + * group operating channel. Enable Probe Request frame reporting for + * P2P connected client in case p2p_cli_probe configuration property is + * set to 1. + */ + if (wpa_s->conf->p2p_cli_probe && wpa_s->current_ssid && + wpa_s->current_ssid->mode == WPAS_MODE_INFRA && + wpa_s->current_ssid->p2p_group) { + if (state == WPA_COMPLETED && !wpa_s->p2p_cli_probe) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Enable CLI Probe Request RX reporting"); + wpa_s->p2p_cli_probe = + wpa_drv_probe_req_report(wpa_s, 1) >= 0; + } else if (state != WPA_COMPLETED && wpa_s->p2p_cli_probe) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Disable CLI Probe Request RX reporting"); + wpa_s->p2p_cli_probe = 0; + wpa_drv_probe_req_report(wpa_s, 0); + } + } +#endif /* CONFIG_P2P */ + if (state != WPA_SCANNING) wpa_supplicant_notify_scanning(wpa_s, 0); @@ -729,6 +766,7 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, ssid && ssid->id_str ? ssid->id_str : ""); #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ wpas_clear_temp_disabled(wpa_s, ssid, 1); + wpa_blacklist_clear(wpa_s); wpa_s->extra_blacklist_count = 0; wpa_s->new_connection = 0; wpa_drv_set_operstate(wpa_s, 1); @@ -870,7 +908,8 @@ int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s) eapol_sm_invalidate_cached_session(wpa_s->eapol); if (wpa_s->current_ssid) { - wpa_s->own_disconnect_req = 1; + if (wpa_s->wpa_state >= WPA_AUTHENTICATING) + wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); } @@ -1237,7 +1276,12 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, } if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) { - wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN, NULL); + int psk_set = 0; + + if (ssid->psk_set) { + wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN, NULL); + psk_set = 1; + } #ifndef CONFIG_NO_PBKDF2 if (bss && ssid->bssid_set && ssid->ssid_len == 0 && ssid->passphrase) { @@ -1247,6 +1291,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)", psk, PMK_LEN); wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL); + psk_set = 1; os_memset(psk, 0, sizeof(psk)); } #endif /* CONFIG_NO_PBKDF2 */ @@ -1284,6 +1329,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, "external passphrase)", psk, PMK_LEN); wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL); + psk_set = 1; os_memset(psk, 0, sizeof(psk)); } else #endif /* CONFIG_NO_PBKDF2 */ @@ -1296,6 +1342,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, return -1; } wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL); + psk_set = 1; os_memset(psk, 0, sizeof(psk)); } else { wpa_msg(wpa_s, MSG_INFO, "EXT PW: No suitable " @@ -1309,6 +1356,12 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, ext_password_free(pw); } #endif /* CONFIG_EXT_PASSWORD */ + + if (!psk_set) { + wpa_msg(wpa_s, MSG_INFO, + "No PSK available for association"); + return -1; + } } else wpa_sm_set_pmk_from_pmksa(wpa_s->wpa); @@ -1914,7 +1967,8 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) wpa_s->connect_work = work; - if (cwork->bss_removed || !wpas_valid_bss_ssid(wpa_s, bss, ssid)) { + if (cwork->bss_removed || !wpas_valid_bss_ssid(wpa_s, bss, ssid) || + wpas_network_disabled(wpa_s, ssid)) { wpa_dbg(wpa_s, MSG_DEBUG, "BSS/SSID entry for association not valid anymore - drop connection attempt"); wpas_connect_work_done(wpa_s); return; @@ -1963,7 +2017,9 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); } - wpa_supplicant_cancel_sched_scan(wpa_s); + if (!wpa_s->pno) + wpa_supplicant_cancel_sched_scan(wpa_s); + wpa_supplicant_cancel_scan(wpa_s); /* Starting new association, so clear the possibly used WPA IE from the @@ -2136,6 +2192,18 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) } } +#ifdef CONFIG_FST + if (wpa_s->fst_ies) { + int fst_ies_len = wpabuf_len(wpa_s->fst_ies); + + if (wpa_ie_len + fst_ies_len <= sizeof(wpa_ie)) { + os_memcpy(wpa_ie + wpa_ie_len, + wpabuf_head(wpa_s->fst_ies), fst_ies_len); + wpa_ie_len += fst_ies_len; + } + } +#endif /* CONFIG_FST */ + wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL); use_crypt = 1; cipher_pairwise = wpa_s->pairwise_cipher; @@ -2383,7 +2451,8 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) } old_ssid = wpa_s->current_ssid; wpa_s->current_ssid = ssid; - wpa_s->current_bss = bss; + if (!wpas_driver_bss_selection(wpa_s) || ssid->bssid_set) + wpa_s->current_bss = bss; 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) @@ -2505,15 +2574,20 @@ void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s, } else wpa_supplicant_enable_one_network(wpa_s, ssid); - if (wpa_s->reassociate && !wpa_s->disconnected) { + if (wpa_s->reassociate && !wpa_s->disconnected && + (!wpa_s->current_ssid || + wpa_s->wpa_state == WPA_DISCONNECTED || + wpa_s->wpa_state == WPA_SCANNING)) { if (wpa_s->sched_scanning) { wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to add " "new network to scan filters"); wpa_supplicant_cancel_sched_scan(wpa_s); } - if (wpa_supplicant_fast_associate(wpa_s) != 1) + if (wpa_supplicant_fast_associate(wpa_s) != 1) { + wpa_s->scan_req = NORMAL_SCAN_REQ; wpa_supplicant_req_scan(wpa_s, 0, 0); + } } } @@ -2586,7 +2660,8 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s, int disconnected = 0; if (ssid && ssid != wpa_s->current_ssid && wpa_s->current_ssid) { - wpa_s->own_disconnect_req = 1; + if (wpa_s->wpa_state >= WPA_AUTHENTICATING) + wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); disconnected = 1; @@ -2625,6 +2700,13 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s, eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); wpa_s->connect_without_scan = (ssid->mode == WPAS_MODE_MESH) ? ssid : NULL; + + /* + * Don't optimize next scan freqs since a new ESS has been + * selected. + */ + os_free(wpa_s->next_scan_freqs); + wpa_s->next_scan_freqs = NULL; } else { wpa_s->connect_without_scan = NULL; } @@ -2633,8 +2715,10 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s, wpa_s->reassociate = 1; if (wpa_s->connect_without_scan || - wpa_supplicant_fast_associate(wpa_s) != 1) + wpa_supplicant_fast_associate(wpa_s) != 1) { + wpa_s->scan_req = NORMAL_SCAN_REQ; wpa_supplicant_req_scan(wpa_s, 0, disconnected ? 100000 : 0); + } if (ssid) wpas_notify_network_selected(wpa_s, ssid); @@ -2709,6 +2793,11 @@ int wpa_supplicant_set_ap_scan(struct wpa_supplicant *wpa_s, int ap_scan) if (ap_scan < 0 || ap_scan > 2) return -1; + if (ap_scan == 2 && os_strcmp(wpa_s->driver->name, "nl80211") == 0) { + wpa_printf(MSG_INFO, + "Note: nl80211 driver interface is not designed to be used with ap_scan=2; this can result in connection failures"); + } + #ifdef ANDROID if (ap_scan == 2 && ap_scan != wpa_s->conf->ap_scan && wpa_s->wpa_state >= WPA_ASSOCIATING && @@ -2848,7 +2937,7 @@ int wpa_supplicant_set_debug_params(struct wpa_global *global, int debug_level, struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s) { struct wpa_ssid *entry; - u8 ssid[MAX_SSID_LEN]; + u8 ssid[SSID_MAX_LEN]; int res; size_t ssid_len; u8 bssid[ETH_ALEN]; @@ -3053,12 +3142,36 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, (wpa_s->current_ssid == NULL || wpa_s->current_ssid->mode != IEEE80211_MODE_IBSS)) { /* Timeout for completing IEEE 802.1X and WPA authentication */ - wpa_supplicant_req_auth_timeout( - wpa_s, - (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) || - wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA || - wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) ? - 70 : 10, 0); + int timeout = 10; + + if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) || + wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA || + wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) { + /* Use longer timeout for IEEE 802.1X/EAP */ + timeout = 70; + } + +#ifdef CONFIG_WPS + if (wpa_s->current_ssid && wpa_s->current_bss && + (wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_WPS) && + eap_is_wps_pin_enrollee(&wpa_s->current_ssid->eap)) { + /* + * Use shorter timeout if going through WPS AP iteration + * for PIN config method with an AP that does not + * advertise Selected Registrar. + */ + struct wpabuf *wps_ie; + + wps_ie = wpa_bss_get_vendor_ie_multi( + wpa_s->current_bss, WPS_IE_VENDOR_TYPE); + if (wps_ie && + !wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1)) + timeout = 10; + wpabuf_free(wps_ie); + } +#endif /* CONFIG_WPS */ + + wpa_supplicant_req_auth_timeout(wpa_s, timeout, 0); } wpa_s->eapol_received++; @@ -3190,6 +3303,12 @@ int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s) } } + if (wpa_s->conf->ap_scan == 2 && + os_strcmp(wpa_s->driver->name, "nl80211") == 0) { + wpa_printf(MSG_INFO, + "Note: nl80211 driver interface is not designed to be used with ap_scan=2; this can result in connection failures"); + } + wpa_clear_keys(wpa_s, NULL); /* Make sure that TKIP countermeasures are not left enabled (could @@ -3615,6 +3734,124 @@ int wpas_init_ext_pw(struct wpa_supplicant *wpa_s) } +#ifdef CONFIG_FST + +static const u8 * wpas_fst_get_bssid_cb(void *ctx) +{ + struct wpa_supplicant *wpa_s = ctx; + + return (is_zero_ether_addr(wpa_s->bssid) || + wpa_s->wpa_state != WPA_COMPLETED) ? NULL : wpa_s->bssid; +} + + +static void wpas_fst_get_channel_info_cb(void *ctx, + enum hostapd_hw_mode *hw_mode, + u8 *channel) +{ + struct wpa_supplicant *wpa_s = ctx; + + if (wpa_s->current_bss) { + *hw_mode = ieee80211_freq_to_chan(wpa_s->current_bss->freq, + channel); + } else if (wpa_s->hw.num_modes) { + *hw_mode = wpa_s->hw.modes[0].mode; + } else { + WPA_ASSERT(0); + *hw_mode = 0; + } +} + + +static int wpas_fst_get_hw_modes(void *ctx, struct hostapd_hw_modes **modes) +{ + struct wpa_supplicant *wpa_s = ctx; + + *modes = wpa_s->hw.modes; + return wpa_s->hw.num_modes; +} + + +static void wpas_fst_set_ies_cb(void *ctx, const struct wpabuf *fst_ies) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpa_hexdump_buf(MSG_DEBUG, "FST: Set IEs", fst_ies); + wpa_s->fst_ies = fst_ies; +} + + +static int wpas_fst_send_action_cb(void *ctx, const u8 *da, struct wpabuf *data) +{ + struct wpa_supplicant *wpa_s = ctx; + + WPA_ASSERT(os_memcmp(wpa_s->bssid, da, ETH_ALEN) == 0); + return wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, + wpa_s->own_addr, wpa_s->bssid, + wpabuf_head(data), wpabuf_len(data), + 0); +} + + +static const struct wpabuf * wpas_fst_get_mb_ie_cb(void *ctx, const u8 *addr) +{ + struct wpa_supplicant *wpa_s = ctx; + + WPA_ASSERT(os_memcmp(wpa_s->bssid, addr, ETH_ALEN) == 0); + return wpa_s->received_mb_ies; +} + + +static void wpas_fst_update_mb_ie_cb(void *ctx, const u8 *addr, + const u8 *buf, size_t size) +{ + struct wpa_supplicant *wpa_s = ctx; + struct mb_ies_info info; + + WPA_ASSERT(os_memcmp(wpa_s->bssid, addr, ETH_ALEN) == 0); + + if (!mb_ies_info_by_ies(&info, buf, size)) { + wpabuf_free(wpa_s->received_mb_ies); + wpa_s->received_mb_ies = mb_ies_by_info(&info); + } +} + + +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; + + *get_ctx = NULL; + if (!is_zero_ether_addr(wpa_s->bssid)) + return (wpa_s->received_mb_ies || !mb_only) ? + wpa_s->bssid : NULL; + return NULL; +} + + +const u8 * wpas_fst_get_peer_next(void *ctx, struct fst_get_peer_ctx **get_ctx, + Boolean mb_only) +{ + return NULL; +} + +void fst_wpa_supplicant_fill_iface_obj(struct wpa_supplicant *wpa_s, + struct fst_wpa_obj *iface_obj) +{ + iface_obj->ctx = wpa_s; + iface_obj->get_bssid = wpas_fst_get_bssid_cb; + iface_obj->get_channel_info = wpas_fst_get_channel_info_cb; + iface_obj->get_hw_modes = wpas_fst_get_hw_modes; + iface_obj->set_ies = wpas_fst_set_ies_cb; + iface_obj->send_action = wpas_fst_send_action_cb; + iface_obj->get_mb_ie = wpas_fst_get_mb_ie_cb; + iface_obj->update_mb_ie = wpas_fst_update_mb_ie_cb; + iface_obj->get_peer_first = wpas_fst_get_peer_first; + iface_obj->get_peer_next = wpas_fst_get_peer_next; +} +#endif /* CONFIG_FST */ + static int wpas_set_wowlan_triggers(struct wpa_supplicant *wpa_s, const struct wpa_driver_capa *capa) { @@ -4153,6 +4390,28 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, return -1; } +#ifdef CONFIG_FST + if (wpa_s->conf->fst_group_id) { + struct fst_iface_cfg cfg; + struct fst_wpa_obj iface_obj; + + fst_wpa_supplicant_fill_iface_obj(wpa_s, &iface_obj); + os_strlcpy(cfg.group_id, wpa_s->conf->fst_group_id, + sizeof(cfg.group_id)); + cfg.priority = wpa_s->conf->fst_priority; + cfg.llt = wpa_s->conf->fst_llt; + + wpa_s->fst = fst_attach(wpa_s->ifname, wpa_s->own_addr, + &iface_obj, &cfg); + if (!wpa_s->fst) { + wpa_msg(wpa_s, MSG_ERROR, + "FST: Cannot attach iface %s to group %s", + wpa_s->ifname, cfg.group_id); + return -1; + } + } +#endif /* CONFIG_FST */ + if (wpas_wps_init(wpa_s)) return -1; @@ -4261,6 +4520,17 @@ static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s, wpas_ctrl_radio_work_flush(wpa_s); radio_remove_interface(wpa_s); +#ifdef CONFIG_FST + if (wpa_s->fst) { + fst_detach(wpa_s->fst); + wpa_s->fst = NULL; + } + if (wpa_s->received_mb_ies) { + wpabuf_free(wpa_s->received_mb_ies); + wpa_s->received_mb_ies = NULL; + } +#endif /* CONFIG_FST */ + if (wpa_s->drv_priv) wpa_drv_deinit(wpa_s); @@ -4287,6 +4557,8 @@ static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s, wpa_s->conf = NULL; } + os_free(wpa_s->ssids_from_scan_req); + os_free(wpa_s); } @@ -4362,8 +4634,10 @@ struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global, #ifdef CONFIG_P2P if (wpa_s->global->p2p == NULL && + !wpa_s->global->p2p_disabled && !wpa_s->conf->p2p_disabled && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) && - wpas_p2p_add_p2pdev_interface(wpa_s, iface->conf_p2p_dev) < 0) { + wpas_p2p_add_p2pdev_interface( + wpa_s, wpa_s->global->params.conf_p2p_dev) < 0) { wpa_printf(MSG_INFO, "P2P: Failed to enable P2P Device interface"); /* Try to continue without. P2P will be disabled. */ @@ -4489,6 +4763,33 @@ static const char * wpa_supplicant_msg_ifname_cb(void *ctx) #endif /* CONFIG_NO_WPA_MSG */ +#ifndef WPA_SUPPLICANT_CLEANUP_INTERVAL +#define WPA_SUPPLICANT_CLEANUP_INTERVAL 10 +#endif /* WPA_SUPPLICANT_CLEANUP_INTERVAL */ + +/* Periodic cleanup tasks */ +static void wpas_periodic(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_global *global = eloop_ctx; + struct wpa_supplicant *wpa_s; + + eloop_register_timeout(WPA_SUPPLICANT_CLEANUP_INTERVAL, 0, + wpas_periodic, global, NULL); + +#ifdef CONFIG_P2P + if (global->p2p) + p2p_expire_peers(global->p2p); +#endif /* CONFIG_P2P */ + + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { + wpa_bss_flush_by_age(wpa_s, wpa_s->conf->bss_expiration_age); +#ifdef CONFIG_AP + ap_periodic(wpa_s); +#endif /* CONFIG_AP */ + } +} + + /** * wpa_supplicant_init - Initialize %wpa_supplicant * @params: Parameters for %wpa_supplicant @@ -4563,6 +4864,11 @@ 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_P2P + if (params->conf_p2p_dev) + global->params.conf_p2p_dev = + os_strdup(params->conf_p2p_dev); +#endif /* CONFIG_P2P */ wpa_debug_level = global->params.wpa_debug_level = params->wpa_debug_level; wpa_debug_show_keys = global->params.wpa_debug_show_keys = @@ -4612,6 +4918,9 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params) } #endif /* CONFIG_WIFI_DISPLAY */ + eloop_register_timeout(WPA_SUPPLICANT_CLEANUP_INTERVAL, 0, + wpas_periodic, global, NULL); + return global; } @@ -4663,6 +4972,8 @@ void wpa_supplicant_deinit(struct wpa_global *global) if (global == NULL) return; + eloop_cancel_timeout(wpas_periodic, global, NULL); + #ifdef CONFIG_WIFI_DISPLAY wifi_display_deinit(global); #endif /* CONFIG_WIFI_DISPLAY */ @@ -4699,6 +5010,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_P2P + os_free(global->params.conf_p2p_dev); +#endif /* CONFIG_P2P */ os_free(global->p2p_disallow_freq.range); os_free(global->p2p_go_avoid_freq.range); @@ -4958,6 +5272,15 @@ int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s, str_clear_free(eap->external_sim_resp); eap->external_sim_resp = os_strdup(value); break; + case WPA_CTRL_REQ_PSK_PASSPHRASE: + if (wpa_config_set(ssid, "psk", value, 0) < 0) + return -1; + ssid->mem_only_psk = 1; + if (ssid->passphrase) + wpa_config_update_psk(ssid); + if (wpa_s->wpa_state == WPA_SCANNING && !wpa_s->scanning) + wpa_supplicant_req_scan(wpa_s, 0, 0); + break; default: wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", field); return -1; @@ -5005,7 +5328,8 @@ int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) } if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && !ssid->psk_set && - (!ssid->passphrase || ssid->ssid_len != 0) && !ssid->ext_psk) + (!ssid->passphrase || ssid->ssid_len != 0) && !ssid->ext_psk && + !ssid->mem_only_psk) return 1; return 0; @@ -5227,7 +5551,8 @@ int get_shared_radio_freqs_data(struct wpa_supplicant *wpa_s, continue; if (ifs->current_ssid->mode == WPAS_MODE_AP || - ifs->current_ssid->mode == WPAS_MODE_P2P_GO) + ifs->current_ssid->mode == WPAS_MODE_P2P_GO || + ifs->current_ssid->mode == WPAS_MODE_MESH) freq = ifs->current_ssid->frequency; else if (wpa_drv_get_bssid(ifs, bssid) == 0) freq = ifs->assoc_freq; @@ -5243,7 +5568,7 @@ int get_shared_radio_freqs_data(struct wpa_supplicant *wpa_s, freqs_data[idx++].freq = freq; if (ifs->current_ssid->mode == WPAS_MODE_INFRA) { - freqs_data[i].flags = ifs->current_ssid->p2p_group ? + freqs_data[i].flags |= ifs->current_ssid->p2p_group ? WPA_FREQ_USED_BY_P2P_CLIENT : WPA_FREQ_USED_BY_INFRA_STATION; } diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant.conf b/contrib/wpa/wpa_supplicant/wpa_supplicant.conf index 9980dbc..68c0476 100644 --- a/contrib/wpa/wpa_supplicant/wpa_supplicant.conf +++ b/contrib/wpa/wpa_supplicant/wpa_supplicant.conf @@ -99,6 +99,7 @@ eapol_version=1 # key_mgmt, pairwise, group, proto variables # # For use in FreeBSD with the wlan module ap_scan must be set to 1. +# # When using IBSS or AP mode, ap_scan=2 mode can force the new network to be # created immediately regardless of scan results. ap_scan=1 mode will first try # to scan for existing networks and only if no matches with the enabled @@ -259,6 +260,11 @@ fast_reauth=1 #wps_nfc_dh_privkey: Hexdump of DH Private Key #wps_nfc_dev_pw: Hexdump of Device Password +# Priority for the networks added through WPS +# This priority value will be set to each network profile that is added +# by executing the WPS protocol. +#wps_priority=0 + # Maximum number of BSS entries to keep in memory # Default: 200 # This can be used to limit memory use on the BSS entries (cached scan @@ -288,6 +294,10 @@ fast_reauth=1 # format: <backend name>[:<optional backend parameters>] #ext_password_backend=test:pw1=password|pw2=testing + +# Disable P2P functionality +# p2p_disabled=1 + # Timeout in seconds to detect STA inactivity (default: 300 seconds) # # This timeout value is used in P2P GO mode to clean up @@ -731,6 +741,11 @@ fast_reauth=1 # startup and reconfiguration time can be optimized by generating the PSK only # only when the passphrase or SSID has actually changed. # +# mem_only_psk: Whether to keep PSK/passphrase only in memory +# 0 = allow psk/passphrase to be stored to the configuration file +# 1 = do not store psk/passphrase to the configuration file +#mem_only_psk=0 +# # eapol_flags: IEEE 802.1X/EAPOL options (bit field) # Dynamic WEP key required for non-WPA mode # bit0 (1): require dynamically generated unicast WEP key @@ -960,9 +975,10 @@ fast_reauth=1 # tls_disable_session_ticket=0 - allow TLS Session Ticket extension to be used # Note: If not set, this is automatically set to 1 for EAP-TLS/PEAP/TTLS # as a workaround for broken authentication server implementations unless -# EAP workarounds are disabled with eap_workarounds=0. +# EAP workarounds are disabled with eap_workaround=0. # For EAP-FAST, this must be set to 0 (or left unconfigured for the # default value to be used automatically). +# tls_disable_tlsv1_0=1 - disable use of TLSv1.0 # tls_disable_tlsv1_1=1 - disable use of TLSv1.1 (a workaround for AAA servers # that have issues interoperating with updated TLS version) # tls_disable_tlsv1_2=1 - disable use of TLSv1.2 (a workaround for AAA servers @@ -1113,6 +1129,32 @@ fast_reauth=1 # 2: MCS 0-9 # 3: not supported +##### 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. +# +# 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. + +# Identifier of an FST Group the interface belongs to. +#fst_group_id=bond0 + +# Interface priority within the FST Group. +# Announcing a higher priority for an interface means declaring it more +# preferable for FST switch. +# fst_priority is in 1..255 range with 1 being the lowest priority. +#fst_priority=100 + +# Default LLT value for this interface in milliseconds. The value used in case +# no value provided during session setup. Default is 50 msec. +# fst_llt is in 1..4294967 range (due to spec limitation, see 10.32.2.2 +# Transitioning between states). +#fst_llt=100 + # Example blocks: # Simple case: WPA-PSK, PSK as an ASCII passphrase, allow all valid ciphers diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h b/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h index 26ff216..58df48c 100644 --- a/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h +++ b/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h @@ -17,14 +17,14 @@ #include "config_ssid.h" #include "wmm_ac.h" -extern const char *wpa_supplicant_version; -extern const char *wpa_supplicant_license; +extern const char *const wpa_supplicant_version; +extern const char *const wpa_supplicant_license; #ifndef CONFIG_NO_STDOUT_DEBUG -extern const char *wpa_supplicant_full_license1; -extern const char *wpa_supplicant_full_license2; -extern const char *wpa_supplicant_full_license3; -extern const char *wpa_supplicant_full_license4; -extern const char *wpa_supplicant_full_license5; +extern const char *const wpa_supplicant_full_license1; +extern const char *const wpa_supplicant_full_license2; +extern const char *const wpa_supplicant_full_license3; +extern const char *const wpa_supplicant_full_license4; +extern const char *const wpa_supplicant_full_license5; #endif /* CONFIG_NO_STDOUT_DEBUG */ struct wpa_sm; @@ -66,17 +66,6 @@ struct wpa_interface { */ const char *confanother; -#ifdef CONFIG_P2P - /** - * conf_p2p_dev - Configuration file used to hold the - * P2P Device configuration parameters. - * - * This can also be %NULL. In such a case, if a P2P Device dedicated - * interfaces is created, the main configuration file will be used. - */ - const char *conf_p2p_dev; -#endif /* CONFIG_P2P */ - /** * ctrl_interface - Control interface parameter * @@ -227,6 +216,18 @@ struct wpa_params { * its internal entropy store over restarts. */ char *entropy_file; + +#ifdef CONFIG_P2P + /** + * conf_p2p_dev - Configuration file used to hold the + * P2P Device configuration parameters. + * + * This can also be %NULL. In such a case, if a P2P Device dedicated + * interfaces is created, the main configuration file will be used. + */ + char *conf_p2p_dev; +#endif /* CONFIG_P2P */ + }; struct p2p_srv_bonjour { @@ -366,10 +367,12 @@ struct wps_ap_info { } type; unsigned int tries; struct os_reltime last_attempt; + unsigned int pbc_active; + u8 uuid[WPS_UUID_LEN]; }; struct wpa_ssid_value { - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; size_t ssid_len; }; @@ -479,7 +482,7 @@ struct wpa_supplicant { struct wpa_ssid_value *disallow_aps_ssid; size_t disallow_aps_ssid_count; - enum { WPA_SETBAND_AUTO, WPA_SETBAND_5G, WPA_SETBAND_2G } setband; + enum set_band setband; /* Preferred network for the next connection attempt */ struct wpa_ssid *next_ssid; @@ -518,7 +521,7 @@ struct wpa_supplicant { unsigned int last_scan_res_size; struct os_reltime last_scan; - struct wpa_driver_ops *driver; + const struct wpa_driver_ops *driver; int interface_removed; /* whether the network interface has been * removed */ struct wpa_sm *wpa; @@ -590,6 +593,8 @@ struct wpa_supplicant { } scan_req, last_scan_req; enum wpa_states scan_prev_wpa_state; struct os_reltime scan_trigger_time, scan_start_time; + /* Minimum freshness requirement for connection purposes */ + struct os_reltime scan_min_time; int scan_runs; /* number of scan runs since WPS was started */ int *next_scan_freqs; int *manual_scan_freqs; @@ -609,6 +614,9 @@ struct wpa_supplicant { int scan_id[MAX_SCAN_ID]; unsigned int scan_id_count; + struct wpa_ssid_value *ssids_from_scan_req; + unsigned int num_ssids_from_scan_req; + u64 drv_flags; unsigned int drv_enc; unsigned int drv_smps_modes; @@ -639,6 +647,7 @@ struct wpa_supplicant { int wps_success; /* WPS success event received */ struct wps_er *wps_er; unsigned int wps_run; + struct os_reltime wps_pin_start_time; int blacklist_cleared; struct wpabuf *pending_eapol_rx; @@ -648,6 +657,7 @@ struct wpa_supplicant { unsigned int eap_expected_failure:1; unsigned int reattach:1; /* reassociation to the same BSS requested */ unsigned int mac_addr_changed:1; + unsigned int added_vif:1; struct os_reltime last_mac_addr_change; int last_mac_addr_style; @@ -661,7 +671,7 @@ struct wpa_supplicant { #ifdef CONFIG_SME struct { - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; size_t ssid_len; int freq; u8 assoc_req_ie[200]; @@ -736,7 +746,6 @@ struct wpa_supplicant { int p2p_mgmt; #ifdef CONFIG_P2P - struct wpa_supplicant *p2p_dev; struct p2p_go_neg_results *go_params; int create_p2p_iface; u8 pending_interface_addr[ETH_ALEN]; @@ -767,7 +776,7 @@ struct wpa_supplicant { u8 pending_join_iface_addr[ETH_ALEN]; u8 pending_join_dev_addr[ETH_ALEN]; int pending_join_wps_method; - u8 p2p_join_ssid[32]; + u8 p2p_join_ssid[SSID_MAX_LEN]; size_t p2p_join_ssid_len; int p2p_join_scan_count; int auto_pd_scan_retry; @@ -813,7 +822,8 @@ struct wpa_supplicant { unsigned int p2p_nfc_tag_enabled:1; unsigned int p2p_peer_oob_pk_hash_known:1; unsigned int p2p_disable_ip_addr_req:1; - unsigned int p2ps_join_addr_valid:1; + unsigned int p2ps_method_config_any:1; + unsigned int p2p_cli_probe:1; int p2p_persistent_go_freq; int p2p_persistent_id; int p2p_go_intent; @@ -969,6 +979,12 @@ struct wpa_supplicant { u8 last_tspecs_count; struct rrm_data rrm; + +#ifdef CONFIG_FST + struct fst_iface *fst; + const struct wpabuf *fst_ies; + struct wpabuf *received_mb_ies; +#endif /* CONFIG_FST */ }; @@ -1116,13 +1132,13 @@ struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s, int eap_register_methods(void); /** - * Utility method to tell if a given network is a persistent group + * Utility method to tell if a given network is for persistent group storage * @ssid: Network object * Returns: 1 if network is a persistent group, 0 otherwise */ static inline int network_is_persistent_group(struct wpa_ssid *ssid) { - return ((ssid->disabled == 2) || ssid->p2p_persistent_group); + return ssid->disabled == 2 && ssid->p2p_persistent_group; } int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); @@ -1140,4 +1156,15 @@ int get_shared_radio_freqs_data(struct wpa_supplicant *wpa_s, int get_shared_radio_freqs(struct wpa_supplicant *wpa_s, int *freq_array, unsigned int len); +void wpas_network_reenabled(void *eloop_ctx, void *timeout_ctx); + +#ifdef CONFIG_FST + +struct fst_wpa_obj; + +void fst_wpa_supplicant_fill_iface_obj(struct wpa_supplicant *wpa_s, + struct fst_wpa_obj *iface_obj); + +#endif /* CONFIG_FST */ + #endif /* WPA_SUPPLICANT_I_H */ diff --git a/contrib/wpa/wpa_supplicant/wpas_glue.c b/contrib/wpa/wpa_supplicant/wpas_glue.c index 1bb82ba..29c22ba 100644 --- a/contrib/wpa/wpa_supplicant/wpas_glue.c +++ b/contrib/wpa/wpa_supplicant/wpas_glue.c @@ -737,6 +737,8 @@ enum wpa_ctrl_req_type wpa_supplicant_ctrl_req_from_string(const char *field) return WPA_CTRL_REQ_EAP_PASSPHRASE; else if (os_strcmp(field, "SIM") == 0) return WPA_CTRL_REQ_SIM; + else if (os_strcmp(field, "PSK_PASSPHRASE") == 0) + return WPA_CTRL_REQ_PSK_PASSPHRASE; return WPA_CTRL_REQ_UNKNOWN; } @@ -776,6 +778,10 @@ const char * wpa_supplicant_ctrl_req_to_string(enum wpa_ctrl_req_type field, case WPA_CTRL_REQ_SIM: ret = "SIM"; break; + case WPA_CTRL_REQ_PSK_PASSPHRASE: + *txt = "PSK or passphrase"; + ret = "PSK_PASSPHRASE"; + break; default: break; } @@ -789,6 +795,35 @@ const char * wpa_supplicant_ctrl_req_to_string(enum wpa_ctrl_req_type field, return ret; } + +void wpas_send_ctrl_req(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, + const char *field_name, const char *txt) +{ + char *buf; + size_t buflen; + int len; + + buflen = 100 + os_strlen(txt) + ssid->ssid_len; + buf = os_malloc(buflen); + if (buf == NULL) + return; + len = os_snprintf(buf, buflen, "%s-%d:%s needed for SSID ", + field_name, ssid->id, txt); + if (os_snprintf_error(buflen, len)) { + os_free(buf); + return; + } + if (ssid->ssid && buflen > len + ssid->ssid_len) { + os_memcpy(buf + len, ssid->ssid, ssid->ssid_len); + len += ssid->ssid_len; + buf[len] = '\0'; + } + buf[buflen - 1] = '\0'; + wpa_msg(wpa_s, MSG_INFO, WPA_CTRL_REQ "%s", buf); + os_free(buf); +} + + #ifdef IEEE8021X_EAPOL #if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) static void wpa_supplicant_eap_param_needed(void *ctx, @@ -798,9 +833,6 @@ static void wpa_supplicant_eap_param_needed(void *ctx, struct wpa_supplicant *wpa_s = ctx; struct wpa_ssid *ssid = wpa_s->current_ssid; const char *field_name, *txt = NULL; - char *buf; - size_t buflen; - int len; if (ssid == NULL) return; @@ -817,25 +849,7 @@ static void wpa_supplicant_eap_param_needed(void *ctx, wpas_notify_eap_status(wpa_s, "eap parameter needed", field_name); - buflen = 100 + os_strlen(txt) + ssid->ssid_len; - buf = os_malloc(buflen); - if (buf == NULL) - return; - len = os_snprintf(buf, buflen, - WPA_CTRL_REQ "%s-%d:%s needed for SSID ", - field_name, ssid->id, txt); - if (os_snprintf_error(buflen, len)) { - os_free(buf); - return; - } - if (ssid->ssid && buflen > len + ssid->ssid_len) { - os_memcpy(buf + len, ssid->ssid, ssid->ssid_len); - len += ssid->ssid_len; - buf[len] = '\0'; - } - buf[buflen - 1] = '\0'; - wpa_msg(wpa_s, MSG_INFO, "%s", buf); - os_free(buf); + wpas_send_ctrl_req(wpa_s, ssid, field_name, txt); } #else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ #define wpa_supplicant_eap_param_needed NULL @@ -1007,7 +1021,8 @@ static int wpa_supplicant_key_mgmt_set_pmk(void *ctx, const u8 *pmk, { struct wpa_supplicant *wpa_s = ctx; - if (wpa_s->conf->key_mgmt_offload) + if (wpa_s->conf->key_mgmt_offload && + (wpa_s->drv_flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD)) return wpa_drv_set_key(wpa_s, WPA_ALG_PMK, NULL, 0, 0, NULL, 0, pmk, pmk_len); else diff --git a/contrib/wpa/wpa_supplicant/wpas_glue.h b/contrib/wpa/wpa_supplicant/wpas_glue.h index 9808c22..5585e56 100644 --- a/contrib/wpa/wpa_supplicant/wpas_glue.h +++ b/contrib/wpa/wpa_supplicant/wpas_glue.h @@ -22,4 +22,7 @@ const char * wpa_supplicant_ctrl_req_to_string(enum wpa_ctrl_req_type field, enum wpa_ctrl_req_type wpa_supplicant_ctrl_req_from_string(const char *field); +void wpas_send_ctrl_req(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, + const char *field_name, const char *txt); + #endif /* WPAS_GLUE_H */ diff --git a/contrib/wpa/wpa_supplicant/wps_supplicant.c b/contrib/wpa/wpa_supplicant/wps_supplicant.c index eabe986..60f761c 100644 --- a/contrib/wpa/wpa_supplicant/wps_supplicant.c +++ b/contrib/wpa/wpa_supplicant/wps_supplicant.c @@ -39,6 +39,14 @@ #define WPS_PIN_SCAN_IGNORE_SEL_REG 3 #endif /* WPS_PIN_SCAN_IGNORE_SEL_REG */ +/* + * The minimum time in seconds before trying to associate to a WPS PIN AP that + * does not have Selected Registrar TRUE. + */ +#ifndef WPS_PIN_TIME_IGNORE_SEL_REG +#define WPS_PIN_TIME_IGNORE_SEL_REG 5 +#endif /* WPS_PIN_TIME_IGNORE_SEL_REG */ + static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx); static void wpas_clear_wps(struct wpa_supplicant *wpa_s); @@ -539,6 +547,7 @@ static int wpa_supplicant_wps_cred(void *ctx, return -1; } } + ssid->priority = wpa_s->conf->wps_priority; wpas_wps_security_workaround(wpa_s, ssid, cred); @@ -552,6 +561,9 @@ static int wpa_supplicant_wps_cred(void *ctx, } #endif /* CONFIG_NO_CONFIG_WRITE */ + if (ssid->priority) + wpa_config_update_prio_list(wpa_s->conf); + /* * Optimize the post-WPS scan based on the channel used during * the provisioning in case EAP-Failure is not received. @@ -880,7 +892,8 @@ static int wpa_supplicant_wps_rf_band(void *ctx) if (!wpa_s->current_ssid || !wpa_s->assoc_freq) return 0; - return (wpa_s->assoc_freq > 2484) ? WPS_RF_50GHZ : WPS_RF_24GHZ; + return (wpa_s->assoc_freq > 50000) ? WPS_RF_60GHZ : + (wpa_s->assoc_freq > 2484) ? WPS_RF_50GHZ : WPS_RF_24GHZ; } @@ -942,8 +955,20 @@ static void wpas_clear_wps(struct wpa_supplicant *wpa_s) static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; + union wps_event_data data; + wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_TIMEOUT "Requested operation timed " "out"); + os_memset(&data, 0, sizeof(data)); + data.fail.config_error = WPS_CFG_MSG_TIMEOUT; + data.fail.error_indication = WPS_EI_NO_ERROR; + /* + * Call wpas_notify_wps_event_fail() directly instead of through + * wpa_supplicant_wps_event() which would end up registering unnecessary + * timeouts (those are only for the case where the failure happens + * during an EAP-WSC exchange). + */ + wpas_notify_wps_event_fail(wpa_s, &data.fail); wpas_clear_wps(wpa_s); } @@ -1178,6 +1203,7 @@ static int wpas_wps_start_dev_pw(struct wpa_supplicant *wpa_s, } #ifdef CONFIG_P2P if (p2p_group && wpa_s->go_params && wpa_s->go_params->ssid_len) { + os_free(ssid->ssid); ssid->ssid = os_zalloc(wpa_s->go_params->ssid_len + 1); if (ssid->ssid) { ssid->ssid_len = wpa_s->go_params->ssid_len; @@ -1216,11 +1242,28 @@ static int wpas_wps_start_dev_pw(struct wpa_supplicant *wpa_s, int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, const char *pin, int p2p_group, u16 dev_pw_id) { + os_get_reltime(&wpa_s->wps_pin_start_time); return wpas_wps_start_dev_pw(wpa_s, NULL, bssid, pin, p2p_group, dev_pw_id, NULL, NULL, 0, 0); } +void wpas_wps_pbc_overlap(struct wpa_supplicant *wpa_s) +{ + union wps_event_data data; + + os_memset(&data, 0, sizeof(data)); + data.fail.config_error = WPS_CFG_MULTIPLE_PBC_DETECTED; + data.fail.error_indication = WPS_EI_NO_ERROR; + /* + * Call wpas_notify_wps_event_fail() directly instead of through + * wpa_supplicant_wps_event() which would end up registering unnecessary + * timeouts (those are only for the case where the failure happens + * during an EAP-WSC exchange). + */ + wpas_notify_wps_event_fail(wpa_s, &data.fail); +} + /* Cancel the wps pbc/pin requests */ int wpas_wps_cancel(struct wpa_supplicant *wpa_s) { @@ -1487,6 +1530,8 @@ int wpas_wps_init(struct wpa_supplicant *wpa_s) wps->dev.rf_bands |= WPS_RF_24GHZ; else if (modes[m].mode == HOSTAPD_MODE_IEEE80211A) wps->dev.rf_bands |= WPS_RF_50GHZ; + else if (modes[m].mode == HOSTAPD_MODE_IEEE80211AD) + wps->dev.rf_bands |= WPS_RF_60GHZ; } } if (wps->dev.rf_bands == 0) { @@ -1609,9 +1654,15 @@ int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s, * external Registrar. */ if (!wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1)) { - if (wpa_s->scan_runs < WPS_PIN_SCAN_IGNORE_SEL_REG) { - wpa_printf(MSG_DEBUG, " skip - WPS AP " - "without active PIN Registrar"); + struct os_reltime age; + + os_reltime_age(&wpa_s->wps_pin_start_time, &age); + + if (wpa_s->scan_runs < WPS_PIN_SCAN_IGNORE_SEL_REG || + age.sec < WPS_PIN_TIME_IGNORE_SEL_REG) { + wpa_printf(MSG_DEBUG, + " skip - WPS AP without active PIN Registrar (scan_runs=%d age=%d)", + wpa_s->scan_runs, (int) age.sec); wpabuf_free(wps_ie); return 0; } @@ -1694,10 +1745,10 @@ int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s, int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s, struct wpa_bss *selected, struct wpa_ssid *ssid) { - const u8 *sel_uuid, *uuid; + const u8 *sel_uuid; struct wpabuf *wps_ie; int ret = 0; - struct wpa_bss *bss; + size_t i; if (!eap_is_wps_pbc_enrollee(&ssid->eap)) return 0; @@ -1718,40 +1769,28 @@ int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s, sel_uuid = NULL; } - dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { - struct wpabuf *ie; - if (bss == selected) - continue; - ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); - if (!ie) - continue; - if (!wps_is_selected_pbc_registrar(ie)) { - wpabuf_free(ie); + for (i = 0; i < wpa_s->num_wps_ap; i++) { + struct wps_ap_info *ap = &wpa_s->wps_ap[i]; + + if (!ap->pbc_active || + os_memcmp(selected->bssid, ap->bssid, ETH_ALEN) == 0) continue; - } + wpa_printf(MSG_DEBUG, "WPS: Another BSS in active PBC mode: " - MACSTR, MAC2STR(bss->bssid)); - uuid = wps_get_uuid_e(ie); + MACSTR, MAC2STR(ap->bssid)); wpa_hexdump(MSG_DEBUG, "WPS: UUID of the other BSS", - uuid, UUID_LEN); - if (os_memcmp(selected->bssid, bss->bssid, ETH_ALEN) == 0) { - wpabuf_free(ie); - continue; - } - if (sel_uuid == NULL || uuid == NULL || - os_memcmp(sel_uuid, uuid, UUID_LEN) != 0) { + ap->uuid, UUID_LEN); + if (sel_uuid == NULL || + os_memcmp(sel_uuid, ap->uuid, UUID_LEN) != 0) { ret = 1; /* PBC overlap */ wpa_msg(wpa_s, MSG_INFO, "WPS: PBC overlap detected: " MACSTR " and " MACSTR, MAC2STR(selected->bssid), - MAC2STR(bss->bssid)); - wpabuf_free(ie); + MAC2STR(ap->bssid)); break; } /* TODO: verify that this is reasonable dual-band situation */ - - wpabuf_free(ie); } wpabuf_free(wps_ie); @@ -1910,7 +1949,7 @@ static int wpas_wps_network_to_cred(struct wpa_ssid *ssid, struct wps_credential *cred) { os_memset(cred, 0, sizeof(*cred)); - if (ssid->ssid_len > 32) + if (ssid->ssid_len > SSID_MAX_LEN) return -1; os_memcpy(cred->ssid, ssid->ssid, ssid->ssid_len); cred->ssid_len = ssid->ssid_len; @@ -2582,6 +2621,10 @@ static int wpas_wps_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s, (attr.rf_bands == NULL || *attr.rf_bands & WPS_RF_50GHZ)) freq = 5000 + 5 * chan; + else if (chan >= 1 && chan <= 4 && + (attr.rf_bands == NULL || + *attr.rf_bands & WPS_RF_60GHZ)) + freq = 56160 + 2160 * chan; if (freq) { wpa_printf(MSG_DEBUG, @@ -2771,7 +2814,8 @@ static void wpas_wps_update_ap_info_bss(struct wpa_supplicant *wpa_s, struct wpabuf *wps; enum wps_ap_info_type type; struct wps_ap_info *ap; - int r; + int r, pbc_active; + const u8 *uuid; if (wpa_scan_get_vendor_ie(res, WPS_IE_VENDOR_TYPE) == NULL) return; @@ -2788,7 +2832,8 @@ static void wpas_wps_update_ap_info_bss(struct wpa_supplicant *wpa_s, else type = WPS_AP_NOT_SEL_REG; - wpabuf_free(wps); + uuid = wps_get_uuid_e(wps); + pbc_active = wps_is_selected_pbc_registrar(wps); ap = wpas_wps_get_ap_info(wpa_s, res->bssid); if (ap) { @@ -2800,13 +2845,16 @@ static void wpas_wps_update_ap_info_bss(struct wpa_supplicant *wpa_s, if (type != WPS_AP_NOT_SEL_REG) wpa_blacklist_del(wpa_s, ap->bssid); } - return; + ap->pbc_active = pbc_active; + if (uuid) + os_memcpy(ap->uuid, uuid, WPS_UUID_LEN); + goto out; } ap = os_realloc_array(wpa_s->wps_ap, wpa_s->num_wps_ap + 1, sizeof(struct wps_ap_info)); if (ap == NULL) - return; + goto out; wpa_s->wps_ap = ap; ap = &wpa_s->wps_ap[wpa_s->num_wps_ap]; @@ -2815,8 +2863,14 @@ static void wpas_wps_update_ap_info_bss(struct wpa_supplicant *wpa_s, os_memset(ap, 0, sizeof(*ap)); os_memcpy(ap->bssid, res->bssid, ETH_ALEN); ap->type = type; + ap->pbc_active = pbc_active; + if (uuid) + os_memcpy(ap->uuid, uuid, WPS_UUID_LEN); wpa_printf(MSG_DEBUG, "WPS: AP " MACSTR " type %d added", MAC2STR(ap->bssid), ap->type); + +out: + wpabuf_free(wps); } diff --git a/contrib/wpa/wpa_supplicant/wps_supplicant.h b/contrib/wpa/wpa_supplicant/wps_supplicant.h index 683bd50..3c25ca8 100644 --- a/contrib/wpa/wpa_supplicant/wps_supplicant.h +++ b/contrib/wpa/wpa_supplicant/wps_supplicant.h @@ -33,6 +33,7 @@ int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid, int p2p_group); int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, const char *pin, int p2p_group, u16 dev_pw_id); +void wpas_wps_pbc_overlap(struct wpa_supplicant *wpa_s); int wpas_wps_cancel(struct wpa_supplicant *wpa_s); int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid, const char *pin, struct wps_new_ap_settings *settings); diff --git a/usr.sbin/wpa/Makefile.crypto b/usr.sbin/wpa/Makefile.crypto index 46cd9ef..5c03f7d 100644 --- a/usr.sbin/wpa/Makefile.crypto +++ b/usr.sbin/wpa/Makefile.crypto @@ -1,7 +1,7 @@ # $FreeBSD$ .if ${MK_OPENSSL} != "no" && !defined(RELEASE_CRUNCH) -SRCS+= crypto_openssl.c random.c sha1-prf.c sha256-prf.c +SRCS+= crypto_openssl.c random.c sha1-prf.c sha256-prf.c sha256-tlsprf.c LIBADD+= ssl crypto CFLAGS+= -DCONFIG_SHA256 .else @@ -19,6 +19,7 @@ CONFIG_INTERNAL_TLS=y CONFIG_INTERNAL_DH5=y CONFIG_INTERNAL_DH=y NEED_AES_ENC=true +NEED_AES_CBC=true .endif .if defined(TLS_FUNCS) diff --git a/usr.sbin/wpa/hostapd/Makefile b/usr.sbin/wpa/hostapd/Makefile index 743f917..4437839 100644 --- a/usr.sbin/wpa/hostapd/Makefile +++ b/usr.sbin/wpa/hostapd/Makefile @@ -97,7 +97,6 @@ NEED_SIM_COMMON=y .if defined(NEED_SIM_COMMON) SRCS+= eap_sim_common.c \ eap_sim_db.c -NEED_AES_CBC=y NEED_FIPS186_2_PRF=y .endif diff --git a/usr.sbin/wpa/wpa_supplicant/Makefile b/usr.sbin/wpa/wpa_supplicant/Makefile index badae0d..65b0c77 100644 --- a/usr.sbin/wpa/wpa_supplicant/Makefile +++ b/usr.sbin/wpa/wpa_supplicant/Makefile @@ -96,10 +96,6 @@ NEED_AES_ENCBLOCK=y NEED_AES_OMAC1=y .endif -.if !empty(CFLAGS:M-DCONFIG_WPS) -NEED_AES_CBC=y -.endif - .if !empty(CFLAGS:M*-DEAP_AKA) SRCS+= eap_aka.c NEED_SIM_COMMON=y |