Index: usr.sbin/wpa/Makefile.crypto =================================================================== --- usr.sbin/wpa/Makefile.crypto (revision 289259) +++ usr.sbin/wpa/Makefile.crypto (working copy) @@ -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) Index: usr.sbin/wpa/hostapd/Makefile =================================================================== --- usr.sbin/wpa/hostapd/Makefile (revision 289259) +++ usr.sbin/wpa/hostapd/Makefile (working copy) @@ -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 Index: usr.sbin/wpa/wpa_supplicant/Makefile =================================================================== --- usr.sbin/wpa/wpa_supplicant/Makefile (revision 289259) +++ usr.sbin/wpa/wpa_supplicant/Makefile (working copy) @@ -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 Index: contrib/wpa/hostapd/ChangeLog =================================================================== --- contrib/wpa/hostapd/ChangeLog (revision 289259) +++ contrib/wpa/hostapd/ChangeLog (working copy) @@ -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) Index: contrib/wpa/hostapd/config_file.c =================================================================== --- contrib/wpa/hostapd/config_file.c (revision 289259) +++ contrib/wpa/hostapd/config_file.c (working copy) @@ -222,9 +222,15 @@ static int hostapd_config_read_eap_user(const char 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_ } +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 *v 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 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_ 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_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_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_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,6 +2553,8 @@ static int hostapd_config_fill(struct hostapd_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); @@ -2521,7 +2561,9 @@ static int hostapd_config_fill(struct hostapd_conf 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_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_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_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_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_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 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; Index: contrib/wpa/hostapd/config_file.h =================================================================== --- contrib/wpa/hostapd/config_file.h (revision 289259) +++ contrib/wpa/hostapd/config_file.h (working copy) @@ -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 */ Index: contrib/wpa/hostapd/ctrl_iface.c =================================================================== --- contrib/wpa/hostapd/ctrl_iface.c (revision 289259) +++ contrib/wpa/hostapd/ctrl_iface.c (working copy) @@ -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 ho #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,6 +1200,13 @@ static int hostapd_ctrl_iface_get_config(struct ho } #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)) @@ -1110,76 +1213,7 @@ static int hostapd_ctrl_iface_get_config(struct ho 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 { 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 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 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 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 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 host #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 hostap } -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 []" */ + 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 int hostapd_ctrl_iface_vendor(struct hostap 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 int hostapd_ctrl_iface_vendor(struct hostap } 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 int hostapd_ctrl_iface_vendor(struct hostap #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 int hostapd_ctrl_iface_vendor(struct hostap 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 hosta } -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_i } +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_i } +#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: " */ + + 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 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 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,6 +2920,14 @@ static void hostapd_global_ctrl_iface_receive(int } 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); @@ -2413,6 +2934,26 @@ static void hostapd_global_ctrl_iface_receive(int 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"); @@ -2419,6 +2960,7 @@ static void hostapd_global_ctrl_iface_receive(int 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 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_ 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 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,7 +3189,7 @@ static void hostapd_ctrl_iface_send(struct hostapd 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", @@ -2634,9 +3196,15 @@ static void hostapd_ctrl_iface_send(struct hostapd 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; Index: contrib/wpa/hostapd/defconfig =================================================================== --- contrib/wpa/hostapd/defconfig (revision 289259) +++ contrib/wpa/hostapd/defconfig (working copy) @@ -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 Index: contrib/wpa/hostapd/hlr_auc_gw.c =================================================================== --- contrib/wpa/hostapd/hlr_auc_gw.c (revision 289259) +++ contrib/wpa/hostapd/hlr_auc_gw.c (working copy) @@ -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 = N #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, ch 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_milenag 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,8 +521,20 @@ 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; m = NULL; @@ -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, si } 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 Index: contrib/wpa/hostapd/hlr_auc_gw.milenage_db =================================================================== --- contrib/wpa/hostapd/hlr_auc_gw.milenage_db (revision 289259) +++ contrib/wpa/hostapd/hlr_auc_gw.milenage_db (working copy) @@ -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. Index: contrib/wpa/hostapd/hostapd.conf =================================================================== --- contrib/wpa/hostapd/hostapd.conf (revision 289259) +++ contrib/wpa/hostapd/hostapd.conf (working copy) @@ -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,7 +582,8 @@ 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 @@ -582,7 +590,8 @@ wmm_ac_vo_acm=0 # 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,7 +1279,44 @@ 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) ############################################# # WPS state @@ -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 Index: contrib/wpa/hostapd/hostapd_cli.c =================================================================== --- contrib/wpa/hostapd/hostapd_cli.c (revision 289259) +++ contrib/wpa/hostapd/hostapd_cli.c (working copy) @@ -10,6 +10,7 @@ #include #include "common/wpa_ctrl.h" +#include "common/ieee802_11_defs.h" #include "utils/common.h" #include "utils/eloop.h" #include "utils/edit.h" @@ -16,16 +17,16 @@ #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 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 @@ "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 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,7 +113,7 @@ static void usage(void) "\n" "usage: hostapd_cli [-p] [-i] [-hvB] " "[-a] \\\n" - " [-G] [command..]\n" + " [-P] [-G] [command..]\n" "\n" "Options:\n" " -h help (show this usage text)\n" @@ -118,6 +120,8 @@ static void usage(void) " -v shown version information\n" " -p path to find control sockets (default: " "/var/run/hostapd)\n" + " -s dir path to open client sockets (default: " + CONFIG_CTRL_IFACE_DIR ")\n" " -a 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_connecti 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_c 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_c } 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 *ct } +#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_ct } +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 @@ struct hostapd_cli_cmd { { "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,6 +1125,7 @@ struct hostapd_cli_cmd { { "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 } }; @@ -1069,7 +1132,7 @@ struct hostapd_cli_cmd { 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; Index: contrib/wpa/hostapd/main.c =================================================================== --- contrib/wpa/hostapd/main.c (revision 289259) +++ contrib/wpa/hostapd/main.c (working copy) @@ -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; Index: contrib/wpa/hs20/client/Makefile =================================================================== --- contrib/wpa/hs20/client/Makefile (revision 289259) +++ contrib/wpa/hs20/client/Makefile (working copy) @@ -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 Index: contrib/wpa/hs20/client/osu_client.c =================================================================== --- contrib/wpa/hs20/client/osu_client.c (revision 289259) +++ contrib/wpa/hs20/client/osu_client.c (working copy) @@ -25,7 +25,9 @@ #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, ...) { va_list ap; @@ -540,6 +542,7 @@ int hs20_add_pps_mo(struct hs20_osu_client *ctx, c 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, c 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 } 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 * 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_cer 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_cer 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_cer 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] " "[-r] [-f] \\\n" " [-s] \\\n" + " [-x] \\\n" " [arguments..]\n" "commands:\n" "- to_tnds [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) { Index: contrib/wpa/hs20/client/spp_client.c =================================================================== --- contrib/wpa/hs20/client/spp_client.c (revision 289259) +++ contrib/wpa/hs20/client/spp_client.c (working copy) @@ -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_clien 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 * 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 ch 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); Index: contrib/wpa/patches/openssl-0.9.8zf-tls-extensions.patch =================================================================== --- contrib/wpa/patches/openssl-0.9.8zf-tls-extensions.patch (revision 0) +++ contrib/wpa/patches/openssl-0.9.8zf-tls-extensions.patch (working copy) @@ -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 +(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 Index: contrib/wpa/src/ap/accounting.c =================================================================== --- contrib/wpa/src/ap/accounting.c (revision 289259) +++ contrib/wpa/src/ap/accounting.c (working copy) @@ -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) Index: contrib/wpa/src/ap/acs.c =================================================================== --- contrib/wpa/src/ap/acs.c (revision 289259) +++ contrib/wpa/src/ap/acs.c (working copy) @@ -479,16 +479,10 @@ static int acs_usable_chan(struct hostapd_channel_ 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 * if (chan->flag & HOSTAPD_CHAN_DISABLED) continue; + if (!is_in_chanlist(iface, chan)) + continue; + *freq++ = chan->freq; } *freq = 0; Index: contrib/wpa/src/ap/ap_config.c =================================================================== --- contrib/wpa/src/ap/ap_config.c (revision 289259) +++ contrib/wpa/src/ap/ap_config.c (working copy) @@ -172,6 +172,7 @@ struct hostapd_config * hostapd_config_defaults(vo 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(vo 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_co 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 *co 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 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 } +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 *co 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_bs 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; + } } } Index: contrib/wpa/src/ap/ap_config.h =================================================================== --- contrib/wpa/src/ap/ap_config.h (revision 289259) +++ contrib/wpa/src/ap/ap_config.h (working copy) @@ -1,6 +1,6 @@ /* * hostapd / Configuration definitions and helpers functions - * Copyright (c) 2003-2012, Jouni Malinen + * Copyright (c) 2003-2015, Jouni Malinen * * 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 */ Index: contrib/wpa/src/ap/ap_drv_ops.c =================================================================== --- contrib/wpa/src/ap/ap_drv_ops.c (revision 289259) +++ contrib/wpa/src/ap/ap_drv_ops.c (working copy) @@ -81,6 +81,22 @@ int hostapd_build_ap_extra_ies(struct hostapd_data 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 } +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 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 *hap { 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 *h } +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; } Index: contrib/wpa/src/ap/ap_drv_ops.h =================================================================== --- contrib/wpa/src/ap/ap_drv_ops.h (revision 289259) +++ contrib/wpa/src/ap/ap_drv_ops.h (working copy) @@ -24,6 +24,7 @@ int hostapd_build_ap_extra_ies(struct hostapd_data 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); Index: contrib/wpa/src/ap/ap_list.c =================================================================== --- contrib/wpa/src/ap/ap_list.c (revision 289259) +++ contrib/wpa/src/ap/ap_list.c (working copy) @@ -193,14 +193,14 @@ void ap_list_process_beacon(struct hostapd_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 * } -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,7 +302,6 @@ void ap_list_process_beacon(struct hostapd_iface * int ap_list_init(struct hostapd_iface *iface) { - eloop_register_timeout(10, 0, ap_list_timer, iface, NULL); return 0; } @@ -312,6 +308,5 @@ int ap_list_init(struct hostapd_iface *iface) void ap_list_deinit(struct hostapd_iface *iface) { - eloop_cancel_timeout(ap_list_timer, iface, NULL); hostapd_free_aps(iface); } Index: contrib/wpa/src/ap/ap_list.h =================================================================== --- contrib/wpa/src/ap/ap_list.h (revision 289259) +++ contrib/wpa/src/ap/ap_list.h (working copy) @@ -39,6 +39,7 @@ void ap_list_process_beacon(struct hostapd_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_ifac 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 */ Index: contrib/wpa/src/ap/authsrv.c =================================================================== --- contrib/wpa/src/ap/authsrv.c (revision 289259) +++ contrib/wpa/src/ap/authsrv.c (working copy) @@ -55,10 +55,11 @@ static int hostapd_radius_get_eap_user(void *ctx, { 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, 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, 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 #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); Index: contrib/wpa/src/ap/beacon.c =================================================================== --- contrib/wpa/src/ap/beacon.c (revision 289259) +++ contrib/wpa/src/ap/beacon.c (working copy) @@ -360,7 +360,6 @@ static u8 * hostapd_add_csa_elems(struct hostapd_d 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_ 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_ /* 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_ 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 ho } +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 hos "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 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 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 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 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; Index: contrib/wpa/src/ap/beacon.h =================================================================== --- contrib/wpa/src/ap/beacon.h (revision 289259) +++ contrib/wpa/src/ap/beacon.h (working copy) @@ -21,5 +21,10 @@ int ieee802_11_update_beacons(struct hostapd_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 */ Index: contrib/wpa/src/ap/ctrl_iface_ap.c =================================================================== --- contrib/wpa/src/ap/ctrl_iface_ap.c (revision 289259) +++ contrib/wpa/src/ap/ctrl_iface_ap.c (working copy) @@ -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 hosta } #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 *ha 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; } Index: contrib/wpa/src/ap/dfs.c =================================================================== --- contrib/wpa/src/ap/dfs.c (revision 289259) +++ contrib/wpa/src/ap/dfs.c (working copy) @@ -122,6 +122,20 @@ static int dfs_is_chan_allowed(struct hostapd_chan } +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 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 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); } Index: contrib/wpa/src/ap/drv_callbacks.c =================================================================== --- contrib/wpa/src/ap/drv_callbacks.c (revision 289259) +++ contrib/wpa/src/ap/drv_callbacks.c (working copy) @@ -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, 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, * 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, } 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, #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, 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, 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,8 +203,8 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, 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, @@ -204,8 +211,9 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, 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,7 +256,6 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, 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); @@ -255,7 +262,6 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, 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, } 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, 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, 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 *h * 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 *h 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 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 * 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 ho #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,10 +550,27 @@ static void hostapd_acs_channel_selected(struct ho 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"); @@ -553,13 +577,14 @@ static void hostapd_acs_channel_selected(struct ho 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!"); @@ -566,6 +591,32 @@ static void hostapd_acs_channel_selected(struct ho 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 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 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 #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 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 *ha 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 *ha 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 *ha 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 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 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_ 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(stru 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_even 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_even 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: Index: contrib/wpa/src/ap/eap_user_db.c =================================================================== --- contrib/wpa/src/ap/eap_user_db.c (revision 289259) +++ contrib/wpa/src/ap/eap_user_db.c (working copy) @@ -138,8 +138,12 @@ eap_user_sqlite_get(struct hostapd_data *hapd, con 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, con 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, con 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); Index: contrib/wpa/src/ap/hostapd.c =================================================================== --- contrib/wpa/src/ap/hostapd.c (revision 289259) +++ contrib/wpa/src/ap/hostapd.c (working copy) @@ -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 *if 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_ { 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 *h } +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 h 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_da 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 hostap 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 hostap #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 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 *interf 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 *inter 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 *inter 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 *inte 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_ifa 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 */ + } +} Index: contrib/wpa/src/ap/hostapd.h =================================================================== --- contrib/wpa/src/ap/hostapd.h (revision 289259) +++ contrib/wpa/src/ap/hostapd.h (working copy) @@ -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 */ Index: contrib/wpa/src/ap/hw_features.c =================================================================== --- contrib/wpa/src/ap/hw_features.c (revision 289259) +++ contrib/wpa/src/ap/hw_features.c (working copy) @@ -260,8 +260,14 @@ static int ieee80211n_check_40mhz_5g(struct hostap 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 ho 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 ho 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 *i 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_i 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 *i } 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)) { Index: contrib/wpa/src/ap/hw_features.h =================================================================== --- contrib/wpa/src/ap/hw_features.h (revision 289259) +++ contrib/wpa/src/ap/hw_features.h (working copy) @@ -36,6 +36,11 @@ static inline int hostapd_get_hw_features(struct h 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; Index: contrib/wpa/src/ap/ieee802_11.c =================================================================== --- contrib/wpa/src/ap/ieee802_11.c (revision 289259) +++ contrib/wpa/src/ap/ieee802_11.c (working copy) @@ -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_dat } -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 @@ u8 * hostapd_eid_ext_supp_rates(struct hostapd_dat 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 @@ u8 * hostapd_eid_ext_supp_rates(struct hostapd_dat } +#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 *ha 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 #ifdef CONFIG_SAE -#define dot11RSNASAERetransPeriod 40 /* msec */ #define dot11RSNASAESync 5 /* attempts */ @@ -511,12 +499,14 @@ static void auth_sae_retransmit_timer(void *eloop_ 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 hostap 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, 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 *h ((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 *ha 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 *ha #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 *ha 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 *h 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 *h 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, con 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 host 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 *h * 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; } Index: contrib/wpa/src/ap/ieee802_11.h =================================================================== --- contrib/wpa/src/ap/ieee802_11.h (revision 289259) +++ contrib/wpa/src/ap/ieee802_11.h (working copy) @@ -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 ho 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 *ha 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, st 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, Index: contrib/wpa/src/ap/ieee802_11_auth.c =================================================================== --- contrib/wpa/src/ap/ieee802_11_auth.c (revision 289259) +++ contrib/wpa/src/ap/ieee802_11_auth.c (working copy) @@ -399,19 +399,15 @@ static void hostapd_acl_expire_queries(struct host /** * 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, st 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 */ Index: contrib/wpa/src/ap/ieee802_11_auth.h =================================================================== --- contrib/wpa/src/ap/ieee802_11_auth.h (revision 289259) +++ contrib/wpa/src/ap/ieee802_11_auth.h (working copy) @@ -24,5 +24,6 @@ int hostapd_allowed_address(struct hostapd_data *h 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 */ Index: contrib/wpa/src/ap/ieee802_11_ht.c =================================================================== --- contrib/wpa/src/ap/ieee802_11_ht.c (revision 289259) +++ contrib/wpa/src/ap/ieee802_11_ht.c (working copy) @@ -209,7 +209,7 @@ void hostapd_2040_coex_action(struct hostapd_data 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 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 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 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 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; Index: contrib/wpa/src/ap/ieee802_11_vht.c =================================================================== --- contrib/wpa/src/ap/ieee802_11_vht.c (revision 289259) +++ contrib/wpa/src/ap/ieee802_11_vht.c (working copy) @@ -132,11 +132,10 @@ static int check_valid_vht_mcs(struct hostapd_hw_m 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; Index: contrib/wpa/src/ap/ieee802_1x.c =================================================================== --- contrib/wpa/src/ap/ieee802_1x.c (revision 289259) +++ contrib/wpa/src/ap/ieee802_1x.c (working copy) @@ -125,6 +125,9 @@ void ieee802_1x_set_sta_authorized(struct hostapd_ } +#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_d } -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,7 +262,10 @@ static void ieee802_1x_tx_key_one(struct hostapd_d } } +#endif /* CONFIG_NO_RC4 */ +#endif /* CONFIG_FIPS */ + const char *radius_mode_txt(struct hostapd_data *hapd) { switch (hapd->iface->conf->hw_mode) { @@ -346,7 +352,8 @@ static int add_common_radius_sta_attr_rsn(struct h 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 hosta } #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 h 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 *h 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 *h 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 h 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 h 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 h 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, st 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,22 +1659,29 @@ ieee802_1x_receive_auth(struct radius_msg *msg, st 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, HOSTAPD_LEVEL_INFO, "authentication " "server did not include required VLAN " @@ -1681,7 +1690,9 @@ ieee802_1x_receive_auth(struct radius_msg *msg, st } #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, cons 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, cons 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, cons 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 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 } -static const char * bool_txt(Boolean bool) +static const char * bool_txt(Boolean val) { - return bool ? "TRUE" : "FALSE"; + return val ? "TRUE" : "FALSE"; } Index: contrib/wpa/src/ap/ieee802_1x.h =================================================================== --- contrib/wpa/src/ap/ieee802_1x.h (revision 289259) +++ contrib/wpa/src/ap/ieee802_1x.h (working copy) @@ -23,7 +23,6 @@ void ieee802_1x_receive(struct hostapd_data *hapd, 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); Index: contrib/wpa/src/ap/ndisc_snoop.c =================================================================== --- contrib/wpa/src/ap/ndisc_snoop.c (revision 289259) +++ contrib/wpa/src/ap/ndisc_snoop.c (working copy) @@ -81,12 +81,24 @@ static int sta_has_ip6addr(struct sta_info *sta, s } +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,9 +113,13 @@ static void handle_ndisc(void *ctx, const u8 *src_ 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); @@ -110,16 +126,17 @@ static void handle_ndisc(void *ctx, const u8 *src_ 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_ 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; Index: contrib/wpa/src/ap/sta_info.c =================================================================== --- contrib/wpa/src/ap/sta_info.c (revision 289259) +++ contrib/wpa/src/ap/sta_info.c (working copy) @@ -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->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 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->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; + int old_vlanid = sta->vlan_id_bound; - /* - * 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; - 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_wps_cancel(struct hostapd_data *hapd, 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_wps_cancel(struct hostapd_data *hapd, } 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_wps_cancel(struct hostapd_data *hapd, 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_wps_cancel(struct hostapd_data *hapd, * 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_wps_cancel(struct hostapd_data *hapd, } } + /* 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_wps_cancel(struct hostapd_data *hapd, "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 *ha 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 */ } Index: contrib/wpa/src/ap/sta_info.h =================================================================== --- contrib/wpa/src/ap/sta_info.h (revision 289259) +++ contrib/wpa/src/ap/sta_info.h (working copy) @@ -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 *ha 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); Index: contrib/wpa/src/ap/utils.c =================================================================== --- contrib/wpa/src/ap/utils.c (revision 289259) +++ contrib/wpa/src/ap/utils.c (working copy) @@ -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 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); } Index: contrib/wpa/src/ap/vlan_init.c =================================================================== --- contrib/wpa/src/ap/vlan_init.c (revision 289259) +++ contrib/wpa/src/ap/vlan_init.c (working copy) @@ -9,6 +9,13 @@ */ #include "utils/includes.h" +#ifdef CONFIG_FULL_DYNAMIC_VLAN +#include +#include +#include +#include +#include +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ #include "utils/common.h" #include "hostapd.h" @@ -20,12 +27,6 @@ #ifdef CONFIG_FULL_DYNAMIC_VLAN -#include -#include -#include -#include -#include - #include "drivers/priv_netlink.h" #include "utils/eloop.h" @@ -34,7 +35,91 @@ 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) { int fd; @@ -481,11 +566,13 @@ static void vlan_newlink(char *ifname, struct host 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 host "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,14 +604,17 @@ static void vlan_newlink(char *ifname, struct host 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,6 +637,7 @@ static void vlan_dellink(char *ifname, struct host 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); @@ -553,7 +644,8 @@ static void vlan_dellink(char *ifname, struct host 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 host 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, 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_d #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 @@ static void full_dynamic_vlan_deinit(struct full_d /* 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, 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, 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; } Index: contrib/wpa/src/ap/vlan_init.h =================================================================== --- contrib/wpa/src/ap/vlan_init.h (revision 289259) +++ contrib/wpa/src/ap/vlan_init.h (working copy) @@ -18,7 +18,6 @@ struct hostapd_vlan * vlan_add_dynamic(struct host 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 hosta } static inline int vlan_setup_encryption_dyn(struct hostapd_data *hapd, - struct hostapd_ssid *mssid, const char *dyn_vlan) { return -1; Index: contrib/wpa/src/ap/vlan_util.c =================================================================== --- contrib/wpa/src/ap/vlan_util.c (revision 289259) +++ contrib/wpa/src/ap/vlan_util.c (working copy) @@ -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 c 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,8 +96,10 @@ int vlan_add(const char *if_name, int vid, const c 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; } @@ -100,15 +106,19 @@ int vlan_add(const char *if_name, int vid, const c 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; } Index: contrib/wpa/src/ap/wmm.c =================================================================== --- contrib/wpa/src/ap/wmm.c (revision 289259) +++ contrib/wpa/src/ap/wmm.c (working copy) @@ -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, Index: contrib/wpa/src/ap/wpa_auth.c =================================================================== --- contrib/wpa/src/ap/wpa_auth.c (revision 289259) +++ contrib/wpa/src/ap/wpa_auth.c (working copy) @@ -45,6 +45,12 @@ static int wpa_group_config_group_keys(struct wpa_ 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 *t 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_au 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_machi #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 *wp 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 *wp 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 @@ void wpa_remove_ptk(struct wpa_state_machine *sm) 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_authe 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_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_a } -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_authenticato } +/* + * 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 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; } Index: contrib/wpa/src/ap/wpa_auth.h =================================================================== --- contrib/wpa/src/ap/wpa_auth.h (revision 289259) +++ contrib/wpa/src/ap/wpa_auth.h (working copy) @@ -1,6 +1,6 @@ /* * hostapd - IEEE 802.11i-2004 / WPA Authenticator - * Copyright (c) 2004-2007, Jouni Malinen + * Copyright (c) 2004-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -12,7 +12,10 @@ #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) #endif /* _MSC_VER */ @@ -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 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); Index: contrib/wpa/src/ap/wpa_auth_ft.c =================================================================== --- contrib/wpa/src/ap/wpa_auth_ft.c (revision 289259) +++ contrib/wpa/src/ap/wpa_auth_ft.c (working copy) @@ -534,10 +534,8 @@ static u8 * wpa_ft_process_rdie(struct wpa_state_m 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_m } 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,7 +570,7 @@ static u8 * wpa_ft_process_rdie(struct wpa_state_m else { /* TSPEC accepted; include updated TSPEC in response */ rdie->descr_count = 1; - pos += sizeof(*tspec); + pos += sizeof(struct wmm_tspec_element); } return pos; } @@ -573,13 +577,10 @@ static u8 * wpa_ft_process_rdie(struct wpa_state_m #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_m /* TSPEC accepted; include updated TSPEC in * response */ rdie->descr_count = 1; - pos += sizeof(*tspec); + pos += sizeof(struct wmm_tspec_element); } return pos; } Index: contrib/wpa/src/ap/wpa_auth_glue.c =================================================================== --- contrib/wpa/src/ap/wpa_auth_glue.c (revision 289259) +++ contrib/wpa/src/ap/wpa_auth_glue.c (working copy) @@ -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_b #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_b #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(voi } +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, Index: contrib/wpa/src/ap/wpa_auth_i.h =================================================================== --- contrib/wpa/src/ap/wpa_auth_i.h (revision 289259) +++ contrib/wpa/src/ap/wpa_auth_i.h (working copy) @@ -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; }; Index: contrib/wpa/src/ap/wpa_auth_ie.c =================================================================== --- contrib/wpa/src/ap/wpa_auth_ie.c (revision 289259) +++ contrib/wpa/src/ap/wpa_auth_ie.c (working copy) @@ -261,7 +261,8 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, } #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 * 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) { Index: contrib/wpa/src/ap/wps_hostapd.c =================================================================== --- contrib/wpa/src/ap/wps_hostapd.c (revision 289259) +++ contrib/wpa/src/ap/wps_hostapd.c (working copy) @@ -324,7 +324,7 @@ static int hapd_wps_reconfig_in_memory(struct host 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 host 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 *h 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 *h 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_d 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,12 +1304,22 @@ int hostapd_wps_add_pin(struct hostapd_data *hapd, } +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; } @@ -1297,18 +1326,31 @@ static int wps_button_pushed(struct hostapd_data * 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, v 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 Index: contrib/wpa/src/ap/x_snoop.c =================================================================== --- contrib/wpa/src/ap/x_snoop.c (revision 289259) +++ contrib/wpa/src/ap/x_snoop.c (working copy) @@ -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; } Index: contrib/wpa/src/common/common_module_tests.c =================================================================== --- contrib/wpa/src/common/common_module_tests.c (revision 289259) +++ contrib/wpa/src/common/common_module_tests.c (working copy) @@ -1,6 +1,6 @@ /* * common module tests - * Copyright (c) 2014, Jouni Malinen + * Copyright (c) 2014-2015, Jouni Malinen * * 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 par { (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; Index: contrib/wpa/src/common/defs.h =================================================================== --- contrib/wpa/src/common/defs.h (revision 289259) +++ contrib/wpa/src/common/defs.h (working copy) @@ -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 */ Index: contrib/wpa/src/common/hw_features_common.c =================================================================== --- contrib/wpa/src/common/hw_features_common.c (revision 289259) +++ contrib/wpa/src/common/hw_features_common.c (working copy) @@ -88,8 +88,8 @@ int allowed_ht40_channel_pair(struct hostapd_hw_mo 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, in *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,12 +176,10 @@ int check_40mhz_5g(struct hostapd_hw_modes *mode, size_t i; int match; - if (!mode || !scan_res || !pri_chan || !sec_chan) + if (!mode || !scan_res || !pri_chan || !sec_chan || + pri_chan == sec_chan) return 0; - if (pri_chan == sec_chan) - return 0; - pri_freq = hw_get_freq(mode, pri_chan); sec_freq = hw_get_freq(mode, sec_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_40mhz_5g(struct hostapd_hw_modes *mode, 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,12 +272,10 @@ 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) + if (!mode || !scan_res || !pri_chan || !sec_chan || + pri_chan == sec_chan) return 0; - if (pri_chan == sec_chan) - return 0; - pri_freq = hw_get_freq(mode, pri_chan); sec_freq = hw_get_freq(mode, sec_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_pa 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,12 +369,11 @@ int hostapd_set_freq_params(struct hostapd_freq_pa if (data->vht_enabled) switch (vht_oper_chwidth) { case VHT_CHANWIDTH_USE_HT: - if (center_segment1) + if (center_segment1 || + (center_segment0 != 0 && + 5000 + center_segment0 * 5 != data->center_freq1 && + 2407 + center_segment0 * 5 != data->center_freq1)) return -1; - if (center_segment0 != 0 && - 5000 + center_segment0 * 5 != data->center_freq1 && - 2407 + center_segment0 * 5 != data->center_freq1) - return -1; break; case VHT_CHANWIDTH_80P80MHZ: if (!(vht_caps & VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)) { @@ -398,19 +388,38 @@ int hostapd_set_freq_params(struct hostapd_freq_pa /* 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_pa 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; } Index: contrib/wpa/src/common/hw_features_common.h =================================================================== --- contrib/wpa/src/common/hw_features_common.h (revision 289259) +++ contrib/wpa/src/common/hw_features_common.h (working copy) @@ -26,7 +26,6 @@ void get_pri_sec_chan(struct wpa_scan_res *bss, in 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); Index: contrib/wpa/src/common/ieee802_11_common.c =================================================================== --- contrib/wpa/src/common/ieee802_11_common.c (revision 289259) +++ contrib/wpa/src/common/ieee802_11_common.c (working copy) @@ -1,6 +1,6 @@ /* * IEEE 802.11 Common routines - * Copyright (c) 2002-2013, Jouni Malinen + * Copyright (c) 2002-2015, Jouni Malinen * * 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 } 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, s 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, s 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, s 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, s 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, s 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, s /* 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,7 +531,7 @@ int hostapd_config_wmm_ac(struct hostapd_wmm_ac_pa 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; } @@ -493,7 +538,7 @@ int hostapd_config_wmm_ac(struct hostapd_wmm_ac_pa 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,34 +568,147 @@ int hostapd_config_wmm_ac(struct hostapd_wmm_ac_pa 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; - } else if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 4) { - mode = HOSTAPD_MODE_IEEE80211AD; + + return HOSTAPD_MODE_IEEE80211A; + } + + /* 5 GHz, channels 149..161 */ + if (freq >= 5745 && freq <= 5805) { + if ((freq - 5000) % 5) + return NUM_HOSTAPD_MODES; + + if (sec_channel == 1) + *op_class = 126; + else if (sec_channel == -1) + *op_class = 127; + else if (vht) + *op_class = 128; + else + *op_class = 124; + + *channel = (freq - 5000) / 5; + + return HOSTAPD_MODE_IEEE80211A; + } + + /* 5 GHz, channels 149..169 */ + if (freq >= 5745 && freq <= 5845) { + if ((freq - 5000) % 5) + return NUM_HOSTAPD_MODES; + + *op_class = 125; + + *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", @@ -557,16 +715,16 @@ enum hostapd_hw_mode ieee80211_freq_to_chan(int fr "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, 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_cla 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; +} Index: contrib/wpa/src/common/ieee802_11_common.h =================================================================== --- contrib/wpa/src/common/ieee802_11_common.h (revision 289259) +++ contrib/wpa/src/common/ieee802_11_common.h (working copy) @@ -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_pa 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 */ Index: contrib/wpa/src/common/ieee802_11_defs.h =================================================================== --- contrib/wpa/src/common/ieee802_11_defs.h (revision 289259) +++ contrib/wpa/src/common/ieee802_11_defs.h (working copy) @@ -10,6 +10,8 @@ #ifndef IEEE802_11_DEFS_H #define IEEE802_11_DEFS_H +#include + /* 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,17 +478,17 @@ 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; @@ -487,7 +495,7 @@ struct ieee80211_mgmt { le16 status_code; le16 aid; /* followed by Supported rates */ - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED assoc_resp, reassoc_resp; struct { le16 capab_info; @@ -494,11 +502,11 @@ struct ieee80211_mgmt { 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,7 +549,7 @@ 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; @@ -548,7 +556,7 @@ struct ieee80211_mgmt { 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 */ Index: contrib/wpa/src/common/privsep_commands.h =================================================================== --- contrib/wpa/src/common/privsep_commands.h (revision 289259) +++ contrib/wpa/src/common/privsep_commands.h (working copy) @@ -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 */ Index: contrib/wpa/src/common/qca-vendor.h =================================================================== --- contrib/wpa/src/common/qca-vendor.h (revision 289259) +++ contrib/wpa/src/common/qca-vendor.h (working copy) @@ -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 */ Index: contrib/wpa/src/common/sae.c =================================================================== --- contrib/wpa/src/common/sae.c (revision 289259) +++ contrib/wpa/src/common/sae.c (working copy) @@ -1,6 +1,6 @@ /* * Simultaneous authentication of equals - * Copyright (c) 2012-2013, Jouni Malinen + * Copyright (c) 2012-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -124,10 +124,8 @@ static struct crypto_bignum * sae_get_rand(struct return NULL; for (;;) { - if (iter++ > 100) + if (iter++ > 100 || random_get_bytes(val, order_len) < 0) return NULL; - if (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); bn = crypto_bignum_init_set(val, order_len); @@ -171,17 +169,107 @@ static void sae_pwd_seed_key(const u8 *addr1, cons } +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 * 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; - 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; + y_sqr = crypto_ec_point_compute_y_sqr(sae->tmp->ec, x_cand); + if (!y_sqr) { + crypto_bignum_deinit(x_cand, 1); + return -1; } - 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,33 +379,87 @@ static int sae_test_pwd_seed_ffc(struct sae_data * } +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", password, password_len); /* * 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 * 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 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,28 +650,42 @@ static int sae_derive_commit(struct sae_data *sae) { struct crypto_bignum *mask; int ret = -1; + unsigned int counter = 0; - mask = sae_get_rand_and_mask(sae); - if (mask == NULL) { - wpa_printf(MSG_DEBUG, "SAE: Could not get rand/mask"); - return -1; - } + 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; + } - /* 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) + /* 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; - if (sae->tmp->dh && sae_derive_commit_element_ffc(sae, mask) < 0) - goto fail; ret = 0; fail: @@ -506,16 +698,13 @@ int sae_prepare_commit(const u8 *addr1, const u8 * const u8 *password, size_t password_len, struct sae_data *sae) { - if (sae->tmp == NULL) + 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; - 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) - return -1; return 0; } @@ -780,8 +969,9 @@ static u16 sae_parse_commit_scalar(struct sae_data 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 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 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 u 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; } Index: contrib/wpa/src/common/sae.h =================================================================== --- contrib/wpa/src/common/sae.h (revision 289259) +++ contrib/wpa/src/common/sae.h (working copy) @@ -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; Index: contrib/wpa/src/common/version.h =================================================================== --- contrib/wpa/src/common/version.h (revision 289259) +++ contrib/wpa/src/common/version.h (working copy) @@ -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 */ Index: contrib/wpa/src/common/wpa_common.c =================================================================== --- contrib/wpa/src/common/wpa_common.c (revision 289259) +++ contrib/wpa/src/common/wpa_common.c (working copy) @@ -170,6 +170,12 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, 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, cons 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,49 +224,59 @@ int wpa_ft_mic(const u8 *kck, size_t kck_len, cons 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++; - 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; + addr[num_elem] = ap_addr; + len[num_elem] = ETH_ALEN; + num_elem++; + + addr[num_elem] = &transaction_seqnum; + len[num_elem] = 1; + num_elem++; + 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,19 +555,30 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t 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; + + 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); } - pos = (const u8 *) (hdr + 1); - left = rsn_ie_len - sizeof(*hdr); - if (left >= RSN_SELECTOR_LEN) { data->group_cipher = rsn_selector_to_bitfield(pos); if (!wpa_cipher_valid_group(data->group_cipher)) { @@ -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 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 xxk 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 xxk * 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, cons 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, cons 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, cons } 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 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); Index: contrib/wpa/src/common/wpa_common.h =================================================================== --- contrib/wpa/src/common/wpa_common.h (revision 289259) +++ contrib/wpa/src/common/wpa_common.h (working copy) @@ -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 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); Index: contrib/wpa/src/common/wpa_ctrl.c =================================================================== --- contrib/wpa/src/common/wpa_ctrl.c (revision 289259) +++ contrib/wpa/src/common/wpa_ctrl.c (working copy) @@ -21,6 +21,7 @@ #ifdef ANDROID #include +#include #include #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_p 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) { Index: contrib/wpa/src/common/wpa_ctrl.h =================================================================== --- contrib/wpa/src/common/wpa_ctrl.h (revision 289259) +++ contrib/wpa/src/common/wpa_ctrl.h (working copy) @@ -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,7 +318,21 @@ 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 * @ctrl: Control interface data from wpa_ctrl_open() Index: contrib/wpa/src/crypto/crypto.h =================================================================== --- contrib/wpa/src/crypto/crypto.h (revision 289259) +++ contrib/wpa/src/crypto/crypto.h (working copy) @@ -614,6 +614,15 @@ int crypto_bignum_is_zero(const struct crypto_bign 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 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_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 */ Index: contrib/wpa/src/crypto/crypto_cryptoapi.c =================================================================== --- contrib/wpa/src/crypto/crypto_cryptoapi.c (revision 289259) +++ contrib/wpa/src/crypto/crypto_cryptoapi.c (working copy) @@ -1,783 +0,0 @@ -/* - * Crypto wrapper for Microsoft CryptoAPI - * Copyright (c) 2005-2009, Jouni Malinen - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -#include "includes.h" -#include -#include - -#include "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; -} Index: contrib/wpa/src/crypto/crypto_module_tests.c =================================================================== --- contrib/wpa/src/crypto/crypto_module_tests.c (revision 289259) +++ contrib/wpa/src/crypto/crypto_module_tests.c (working copy) @@ -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 @@ struct omac1_test_vector { }; -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 int test_eap_fast(void) 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 int test_eap_fast(void) }; -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 int test_eap_fast(void) 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 @@ static int test_sha1(void) } }; -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 */ } Index: contrib/wpa/src/crypto/crypto_openssl.c =================================================================== --- contrib/wpa/src/crypto/crypto_openssl.c (revision 289259) +++ contrib/wpa/src/crypto/crypto_openssl.c (working copy) @@ -93,10 +93,12 @@ static int openssl_digest_vector(const EVP_MD *typ } +#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, u } +#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,7 +332,60 @@ int aes_unwrap(const u8 *kek, size_t kek_len, int 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, const u8 *modulus, size_t modulus_len, @@ -380,11 +442,13 @@ struct crypto_cipher * crypto_cipher_init(enum cry 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_bignu } +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 } +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_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 */ Index: contrib/wpa/src/crypto/dh_groups.c =================================================================== --- contrib/wpa/src/crypto/dh_groups.c (revision 289259) +++ contrib/wpa/src/crypto/dh_groups.c (working copy) @@ -1153,7 +1153,7 @@ dh_group ## id ## _prime, sizeof(dh_group ## id ## 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), Index: contrib/wpa/src/crypto/fips_prf_openssl.c =================================================================== --- contrib/wpa/src/crypto/fips_prf_openssl.c (revision 289259) +++ contrib/wpa/src/crypto/fips_prf_openssl.c (working copy) @@ -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, /* 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]); Index: contrib/wpa/src/crypto/ms_funcs.c =================================================================== --- contrib/wpa/src/crypto/ms_funcs.c (revision 289259) +++ contrib/wpa/src/crypto/ms_funcs.c (working copy) @@ -78,9 +78,8 @@ static int utf8_to_ucs2(const u8 *utf8_string, siz * @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,10 +174,9 @@ int generate_nt_response(const u8 *auth_challenge, u8 password_hash[16]; if (challenge_hash(peer_challenge, auth_challenge, username, - username_len, challenge)) + username_len, challenge) || + nt_password_hash(password, password_len, password_hash)) return -1; - if (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, } +#ifndef CONFIG_NO_RC4 + #define PWBLOCK_LEN 516 /** @@ -436,12 +433,10 @@ 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) + if (utf8_to_ucs2(password, password_len, pw_block, 512, &ucs2_len) < 0 + || ucs2_len > 256) return -1; - if (ucs2_len > 256) - return -1; - offset = (256 - ucs2_len) * 2; if (offset != 0) { os_memmove(pw_block + offset, pw_block, ucs2_len * 2); @@ -484,7 +479,9 @@ int new_password_encrypted_with_old_nt_password_ha return 0; } +#endif /* CONFIG_NO_RC4 */ + /** * nt_password_hash_encrypted_with_block - NtPasswordHashEncryptedWithBlock() - RFC 2759, Sect 8.13 * @password_hash: 16-octer PasswordHash (IN) Index: contrib/wpa/src/crypto/ms_funcs.h =================================================================== --- contrib/wpa/src/crypto/ms_funcs.h (revision 289259) +++ contrib/wpa/src/crypto/ms_funcs.h (working copy) @@ -33,6 +33,8 @@ int nt_challenge_response(const u8 *challenge, con 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); Index: contrib/wpa/src/crypto/random.c =================================================================== --- contrib/wpa/src/crypto/random.c (revision 289259) +++ contrib/wpa/src/crypto/random.c (working copy) @@ -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; Index: contrib/wpa/src/crypto/sha1-tlsprf.c =================================================================== --- contrib/wpa/src/crypto/sha1-tlsprf.c (revision 289259) +++ contrib/wpa/src/crypto/sha1-tlsprf.c (working copy) @@ -95,5 +95,10 @@ int tls_prf_sha1_md5(const u8 *secret, size_t secr 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; } Index: contrib/wpa/src/crypto/sha1-tprf.c =================================================================== --- contrib/wpa/src/crypto/sha1-tprf.c (revision 289259) +++ contrib/wpa/src/crypto/sha1-tprf.c (working copy) @@ -66,5 +66,7 @@ int sha1_t_prf(const u8 *key, size_t key_len, cons len[0] = SHA1_MAC_LEN; } + os_memset(hash, 0, SHA1_MAC_LEN); + return 0; } Index: contrib/wpa/src/crypto/sha256-kdf.c =================================================================== --- contrib/wpa/src/crypto/sha256-kdf.c (revision 289259) +++ contrib/wpa/src/crypto/sha256-kdf.c (working copy) @@ -61,6 +61,7 @@ int hmac_sha256_kdf(const u8 *secret, size_t secre 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 secre 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; } Index: contrib/wpa/src/crypto/sha384-prf.c =================================================================== --- contrib/wpa/src/crypto/sha384-prf.c (revision 0) +++ contrib/wpa/src/crypto/sha384-prf.c (working copy) @@ -0,0 +1,100 @@ +/* + * SHA384-based KDF (IEEE 802.11ac) + * Copyright (c) 2003-2015, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "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)); +} Property changes on: contrib/wpa/src/crypto/sha384-prf.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: contrib/wpa/src/crypto/sha384.h =================================================================== --- contrib/wpa/src/crypto/sha384.h (revision 289259) +++ contrib/wpa/src/crypto/sha384.h (working copy) @@ -15,5 +15,10 @@ int hmac_sha384_vector(const u8 *key, size_t key_l 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 */ Index: contrib/wpa/src/crypto/tls.h =================================================================== --- contrib/wpa/src/crypto/tls.h (revision 289259) +++ contrib/wpa/src/crypto/tls.h (working copy) @@ -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, stru 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 tl * @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_c * @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_set_verify(void *t * @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(vo 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, 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_conn 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 */ Index: contrib/wpa/src/crypto/tls_gnutls.c =================================================================== --- contrib/wpa/src/crypto/tls_gnutls.c (revision 289259) +++ contrib/wpa/src/crypto/tls_gnutls.c (working copy) @@ -708,7 +708,8 @@ int tls_global_set_verify(void *ssl_ctx, int check 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, struc } -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_set_verify(void *ssl_ctx, struc 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, } +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, } -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) { - return os_snprintf(buf, buf_len, "GnuTLS build=%s run=%s", - GNUTLS_VERSION, gnutls_check_version(NULL)); } + + +const struct wpabuf * +tls_connection_get_success_data(struct tls_connection *conn) +{ + return NULL; +} + + +void tls_connection_remove_session(struct tls_connection *conn) +{ +} Index: contrib/wpa/src/crypto/tls_internal.c =================================================================== --- contrib/wpa/src/crypto/tls_internal.c (revision 289259) +++ contrib/wpa/src/crypto/tls_internal.c (working copy) @@ -192,26 +192,31 @@ int tls_connection_set_params(void *tls_ctx, struc 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 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,40 +339,72 @@ int tls_connection_set_verify(void *tls_ctx, struc } -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_keys(conn->client, keys); + 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_keys(conn->server, keys); + 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_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; +} + + 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, } +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_ { 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) +{ +} Index: contrib/wpa/src/crypto/tls_none.c =================================================================== --- contrib/wpa/src/crypto/tls_none.c (revision 289259) +++ contrib/wpa/src/crypto/tls_none.c (working copy) @@ -72,14 +72,15 @@ int tls_global_set_verify(void *tls_ctx, int check 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_set_verify(void *tls_ctx, struc 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, } +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) { - return os_snprintf(buf, buf_len, "none"); } + + +const struct wpabuf * +tls_connection_get_success_data(struct tls_connection *conn) +{ + return NULL; +} + + +void tls_connection_remove_session(struct tls_connection *conn) +{ +} Index: contrib/wpa/src/crypto/tls_openssl.c =================================================================== --- contrib/wpa/src/crypto/tls_openssl.c (revision 289259) +++ contrib/wpa/src/crypto/tls_openssl.c (working copy) @@ -1,6 +1,6 @@ /* * SSL/TLS interface functions for OpenSSL - * Copyright (c) 2004-2013, Jouni Malinen + * Copyright (c) 2004-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -23,15 +23,19 @@ #ifndef OPENSSL_NO_ENGINE #include #endif /* OPENSSL_NO_ENGINE */ +#ifndef OPENSSL_NO_DSA +#include +#endif +#ifndef OPENSSL_NO_DH +#include +#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 ch #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,24 +830,21 @@ 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; @@ -815,15 +851,38 @@ void * tls_init(const struct tls_config *conf) } 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"); ERR_load_ENGINE_strings(); @@ -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,9 +941,32 @@ 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, const char *cert_id, const char *ca_cert_id) @@ -935,11 +1018,16 @@ static int tls_engine_init(struct tls_connection * 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, i 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 t { 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 * 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_ST #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_verify_cb(int preverify_ok, X509_ST #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_verify_cb(int preverify_ok, X509_ST 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_verify_cb(int preverify_ok, X509_ST } 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_verify_cb(int preverify_ok, X509_ST } -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 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 } +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, struc 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_c } -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 #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_passwd_cb(char *buf, int size, int 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_passwd_cb(char *buf, int size, int 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,7 +2222,7 @@ static int tls_passwd_cb(char *buf, int size, int 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); @@ -2088,27 +2229,68 @@ static int tls_passwd_cb(char *buf, int size, int } 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)); + wpa_printf(MSG_DEBUG, "TLS: additional certificate" + " from PKCS12: subject='%s'", buf); /* * 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; } @@ -2115,8 +2297,8 @@ static int tls_passwd_cb(char *buf, int size, int #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_passwd_cb(char *buf, int size, int 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_passwd_cb(char *buf, int size, int } -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_passwd_cb(char *buf, int size, int 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_connecti 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(struc } -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,7 +2483,7 @@ static int tls_connection_engine_private_key(struc } -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, @@ -2304,7 +2490,7 @@ static int tls_connection_engine_private_key(struc 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_engine_private_key(struc 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_engine_private_key(struc __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_engine_private_key(struc } -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_connection_engine_private_key(struc 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 } -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_connection_dh(struct tls_connection "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_connection_dh(struct tls_connection } -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_c 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, con * 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 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, 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, } +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, struc 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, struc 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, struc 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, struc 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, struc 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 */ + tls_set_conn_flags(conn->ssl, params->flags); -#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 */ - #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, struc 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,20 +3918,13 @@ int tls_global_set_params(void *tls_ctx, __func__, ERR_error_string(err, NULL)); } - if (tls_global_ca_cert(ssl_ctx, params->ca_cert)) + 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; - - 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); - return -1; } if (params->openssl_ciphers && @@ -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, i 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, i 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_ 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"); +} Index: contrib/wpa/src/crypto/tls_schannel.c =================================================================== --- contrib/wpa/src/crypto/tls_schannel.c (revision 289259) +++ contrib/wpa/src/crypto/tls_schannel.c (working copy) @@ -1,763 +0,0 @@ -/* - * SSL/TLS interface functions for Microsoft Schannel - * Copyright (c) 2005-2009, Jouni Malinen - * - * 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 -#include -#include -#define SECURITY_WIN32 -#include -#include - -#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"); -} Index: contrib/wpa/src/drivers/driver.h =================================================================== --- contrib/wpa/src/drivers/driver.h (revision 289259) +++ contrib/wpa/src/drivers/driver.h (working copy) @@ -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_trigger 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 */ Index: contrib/wpa/src/drivers/driver_bsd.c =================================================================== --- contrib/wpa/src/drivers/driver_bsd.c (revision 289259) +++ contrib/wpa/src/drivers/driver_bsd.c (working copy) @@ -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; Index: contrib/wpa/src/drivers/driver_ndis.c =================================================================== --- contrib/wpa/src/drivers/driver_ndis.c (revision 289259) +++ contrib/wpa/src/drivers/driver_ndis.c (working copy) @@ -710,11 +710,11 @@ static int wpa_driver_ndis_radio_off(struct wpa_dr /* 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_s 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); Index: contrib/wpa/src/drivers/driver_nl80211.h =================================================================== --- contrib/wpa/src/drivers/driver_nl80211.h (revision 289259) +++ contrib/wpa/src/drivers/driver_nl80211.h (working copy) @@ -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 *ar #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 */ Index: contrib/wpa/src/drivers/driver_nl80211_android.c =================================================================== --- contrib/wpa/src/drivers/driver_nl80211_android.c (revision 289259) +++ contrib/wpa/src/drivers/driver_nl80211_android.c (working copy) @@ -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 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_ha } -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; -} Index: contrib/wpa/src/drivers/driver_nl80211_capa.c =================================================================== --- contrib/wpa/src/drivers/driver_nl80211_capa.c (revision 289259) +++ contrib/wpa/src/drivers/driver_nl80211_capa.c (working copy) @@ -335,6 +335,33 @@ static void wiphy_info_tdls(struct wpa_driver_capa } +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, 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, 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_ 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 *ms 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,6 +830,7 @@ static void qca_nl80211_get_features(struct wpa_dr } 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; @@ -782,6 +837,9 @@ static void qca_nl80211_get_features(struct wpa_dr 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; } Index: contrib/wpa/src/drivers/driver_nl80211_event.c =================================================================== --- contrib/wpa/src/drivers/driver_nl80211_event.c (revision 289259) +++ contrib/wpa/src/drivers/driver_nl80211_event.c (working copy) @@ -271,6 +271,7 @@ static void mlme_event_connect(struct wpa_driver_n 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_n 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_driv } +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,7 +1530,40 @@ static void qca_nl80211_acs_select_ch(struct wpa_d 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); } Index: contrib/wpa/src/drivers/driver_nl80211_scan.c =================================================================== --- contrib/wpa/src/drivers/driver_nl80211_scan.c (revision 289259) +++ contrib/wpa/src/drivers/driver_nl80211_scan.c (working copy) @@ -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; Index: contrib/wpa/src/drivers/driver_privsep.c =================================================================== --- contrib/wpa/src/drivers/driver_privsep.c (revision 289259) +++ contrib/wpa/src/drivers/driver_privsep.c (working copy) @@ -220,6 +220,56 @@ static int wpa_driver_privsep_set_key(const char * } +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,7 +331,7 @@ static int wpa_driver_privsep_get_ssid(void *priv, { 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); @@ -288,7 +338,8 @@ static int wpa_driver_privsep_get_ssid(void *priv, 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 } +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, v 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, v 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 Index: contrib/wpa/src/drivers/drivers.c =================================================================== --- contrib/wpa/src/drivers/drivers.c (revision 289259) +++ contrib/wpa/src/drivers/drivers.c (working copy) @@ -47,7 +47,7 @@ extern struct wpa_driver_ops wpa_driver_none_ops; #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, Index: contrib/wpa/src/eap_common/eap_common.c =================================================================== --- contrib/wpa/src/eap_common/eap_common.c (revision 289259) +++ contrib/wpa/src/eap_common/eap_common.c (working copy) @@ -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 */ Index: contrib/wpa/src/eap_common/eap_fast_common.c =================================================================== --- contrib/wpa/src/eap_common/eap_fast_common.c (revision 289259) +++ contrib/wpa/src/eap_common/eap_fast_common.c (working copy) @@ -96,49 +96,18 @@ void eap_fast_derive_master_secret(const u8 *pac_k 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) + out = os_malloc(len); + if (out == NULL) return NULL; - out = os_malloc(block_size + len); - if (out == NULL) + if (tls_connection_prf(ssl_ctx, conn, label, 1, 1, out, len)) { + os_free(out); 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_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; } Index: contrib/wpa/src/eap_common/eap_pwd_common.c =================================================================== --- contrib/wpa/src/eap_common/eap_pwd_common.c (revision 289259) +++ contrib/wpa/src/eap_common/eap_pwd_common.c (working copy) @@ -86,9 +86,10 @@ static int eap_pwd_kdf(const u8 *key, size_t keyle * 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, u } -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_password_element(EAP_PWD_group *grp, u 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); Index: contrib/wpa/src/eap_common/eap_pwd_common.h =================================================================== --- contrib/wpa/src/eap_common/eap_pwd_common.h (revision 289259) +++ contrib/wpa/src/eap_common/eap_pwd_common.h (working copy) @@ -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); Index: contrib/wpa/src/eap_common/eap_sake_common.c =================================================================== --- contrib/wpa/src/eap_common/eap_sake_common.c (revision 289259) +++ contrib/wpa/src/eap_common/eap_sake_common.c (working copy) @@ -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 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_ 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]; Index: contrib/wpa/src/eap_common/ikev2_common.c =================================================================== --- contrib/wpa/src/eap_common/ikev2_common.c (revision 289259) +++ contrib/wpa/src/eap_common/ikev2_common.c (working copy) @@ -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 @@ #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 @@ #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 } }; Index: contrib/wpa/src/eap_peer/eap.c =================================================================== --- contrib/wpa/src/eap_peer/eap.c (revision 289259) +++ contrib/wpa/src/eap_peer/eap.c (working copy) @@ -584,7 +584,7 @@ static int eap_peer_erp_reauth_start(struct eap_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 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, 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 * 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 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; Index: contrib/wpa/src/eap_peer/eap.h =================================================================== --- contrib/wpa/src/eap_peer/eap.h (revision 289259) +++ contrib/wpa/src/eap_peer/eap.h (working copy) @@ -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); Index: contrib/wpa/src/eap_peer/eap_aka.c =================================================================== --- contrib/wpa/src/eap_peer/eap_aka.c (revision 289259) +++ contrib/wpa/src/eap_peer/eap_aka.c (working copy) @@ -1296,7 +1296,7 @@ static struct wpabuf * eap_aka_process(struct eap_ 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; } Index: contrib/wpa/src/eap_peer/eap_eke.c =================================================================== --- contrib/wpa/src/eap_peer/eap_eke.c (revision 289259) +++ contrib/wpa/src/eap_peer/eap_eke.c (working copy) @@ -195,8 +195,7 @@ 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; @@ -203,7 +202,7 @@ static struct wpabuf * eap_eke_build_fail(struct e 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 e 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 e 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 e 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 e 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 e 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 e 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 e 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 e 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(stru 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(stru 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(stru 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(stru 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(stru 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(stru { 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(stru 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(stru 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(stru 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(stru 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(stru 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(str 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(str 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,12 +534,12 @@ static struct wpabuf * eap_eke_process_confirm(str 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", @@ -544,7 +546,7 @@ static struct wpabuf * eap_eke_process_confirm(str 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,13 +558,13 @@ static struct wpabuf * eap_eke_process_confirm(str 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); @@ -569,17 +571,17 @@ static struct wpabuf * eap_eke_process_confirm(str 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(str 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(str 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(str 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(str 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, vo } +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) Index: contrib/wpa/src/eap_peer/eap_fast.c =================================================================== --- contrib/wpa/src/eap_peer/eap_fast.c (revision 289259) +++ contrib/wpa/src/eap_peer/eap_fast.c (working copy) @@ -267,8 +267,8 @@ static int eap_fast_derive_msk(struct eap_fast_dat } -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 int eap_fast_derive_msk(struct eap_fast_dat if (sks == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive " "session_key_seed"); - return; + return -1; } /* @@ -294,11 +294,12 @@ static int eap_fast_derive_msk(struct eap_fast_dat 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 int eap_fast_derive_msk(struct eap_fast_dat 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 int eap_fast_derive_msk(struct eap_fast_dat 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_s 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_s 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, str /* 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, *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 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 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 /* 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,11 +1598,17 @@ static struct wpabuf * eap_fast_process(struct eap } 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. */ @@ -1597,9 +1615,7 @@ static struct wpabuf * eap_fast_process(struct eap 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); } } Index: contrib/wpa/src/eap_peer/eap_gpsk.c =================================================================== --- contrib/wpa/src/eap_peer/eap_gpsk.c (revision 289259) +++ contrib/wpa/src/eap_peer/eap_gpsk.c (working copy) @@ -274,7 +274,7 @@ static const u8 * eap_gpsk_process_csuite_list(str 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(str 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(str 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(str (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 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,7 +678,10 @@ static struct wpabuf * eap_gpsk_process(struct eap 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; @@ -684,18 +688,17 @@ static struct wpabuf * eap_gpsk_process(struct eap 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; } Index: contrib/wpa/src/eap_peer/eap_i.h =================================================================== --- contrib/wpa/src/eap_peer/eap_i.h (revision 289259) +++ contrib/wpa/src/eap_peer/eap_i.h (working copy) @@ -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; Index: contrib/wpa/src/eap_peer/eap_mschapv2.c =================================================================== --- contrib/wpa/src/eap_peer/eap_mschapv2.c (revision 289259) +++ contrib/wpa/src/eap_peer/eap_mschapv2.c (working copy) @@ -511,6 +511,11 @@ static struct wpabuf * eap_mschapv2_change_passwor 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_passwor fail: wpabuf_free(resp); return NULL; +#endif /* CONFIG_NO_RC4 */ } Index: contrib/wpa/src/eap_peer/eap_pax.c =================================================================== --- contrib/wpa/src/eap_peer/eap_pax.c (revision 289259) +++ contrib/wpa/src/eap_peer/eap_pax.c (working copy) @@ -333,7 +333,7 @@ static struct wpabuf * eap_pax_process(struct eap_ 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; } Index: contrib/wpa/src/eap_peer/eap_peap.c =================================================================== --- contrib/wpa/src/eap_peer/eap_peap.c (revision 289259) +++ contrib/wpa/src/eap_peer/eap_peap.c (working copy) @@ -968,6 +968,7 @@ static struct wpabuf * eap_peap_process(struct eap 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 * 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,7 +1085,6 @@ static struct wpabuf * eap_peap_process(struct eap } if (res == 2) { - struct wpabuf msg; /* * Application data included in the handshake message. */ @@ -1084,7 +1091,6 @@ static struct wpabuf * eap_peap_process(struct eap 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); } Index: contrib/wpa/src/eap_peer/eap_pwd.c =================================================================== --- contrib/wpa/src/eap_peer/eap_pwd.c (revision 289259) +++ contrib/wpa/src/eap_peer/eap_pwd.c (working copy) @@ -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, str 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, str 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, str 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, str 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,7 +362,24 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, 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) || ((cofactor = BN_new()) == NULL) || @@ -500,6 +578,18 @@ eap_pwd_perform_confirm_exchange(struct eap_sm *sm 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,11 +873,23 @@ eap_pwd_process(struct eap_sm *sm, void *priv, str * 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 " @@ -794,6 +896,7 @@ eap_pwd_process(struct eap_sm *sm, void *priv, str "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, str /* * 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, Index: contrib/wpa/src/eap_peer/eap_sake.c =================================================================== --- contrib/wpa/src/eap_peer/eap_sake.c (revision 289259) +++ contrib/wpa/src/eap_peer/eap_sake.c (working copy) @@ -141,7 +141,7 @@ static struct wpabuf * eap_sake_build_msg(struct e 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(s 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(s 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( 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( 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(st 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 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 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 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: Index: contrib/wpa/src/eap_peer/eap_sim.c =================================================================== --- contrib/wpa/src/eap_peer/eap_sim.c (revision 289259) +++ contrib/wpa/src/eap_peer/eap_sim.c (working copy) @@ -1042,7 +1042,7 @@ static struct wpabuf * eap_sim_process(struct eap_ } 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; } Index: contrib/wpa/src/eap_peer/eap_tls.c =================================================================== --- contrib/wpa/src/eap_peer/eap_tls.c (revision 289259) +++ contrib/wpa/src/eap_peer/eap_tls.c (working copy) @@ -156,20 +156,6 @@ static struct wpabuf * eap_tls_failure(struct eap_ 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_ 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_ } 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); Index: contrib/wpa/src/eap_peer/eap_tls_common.c =================================================================== --- contrib/wpa/src/eap_peer/eap_tls_common.c (revision 289259) +++ contrib/wpa/src/eap_peer/eap_tls_common.c (working copy) @@ -68,6 +68,10 @@ static void eap_tls_params_flags(struct tls_connec 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 * } 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, st 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; + if (tls_connection_prf(data->ssl_ctx, data->conn, label, 0, 0, + out, len)) { + os_free(out); + return NULL; + } -#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; - - 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 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_rea * @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_d * @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,7 +635,7 @@ static int eap_tls_process_output(struct eap_ssl_d */ 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; @@ -679,7 +642,8 @@ int eap_peer_tls_process_helper(struct eap_sm *sm, *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, * 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, 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,21 +759,25 @@ int eap_peer_tls_reauth_init(struct eap_sm *sm, st 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 { 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) Index: contrib/wpa/src/eap_peer/eap_tls_common.h =================================================================== --- contrib/wpa/src/eap_peer/eap_tls_common.h (revision 289259) +++ contrib/wpa/src/eap_peer/eap_tls_common.h (working copy) @@ -100,7 +100,7 @@ u8 * eap_peer_tls_derive_session_id(struct eap_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); Index: contrib/wpa/src/eap_peer/eap_ttls.c =================================================================== --- contrib/wpa/src/eap_peer/eap_ttls.c (revision 289259) +++ contrib/wpa/src/eap_peer/eap_ttls.c (working copy) @@ -175,7 +175,8 @@ static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_c } 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 *s } +#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 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 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 e 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 e ret->decision = DECISION_COND_SUCC; return 0; +#endif /* CONFIG_FIPS */ } @@ -654,6 +667,10 @@ static int eap_ttls_phase2_request_chap(struct eap 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 ret->decision = DECISION_COND_SUCC; return 0; +#endif /* CONFIG_FIPS */ } @@ -1385,7 +1403,7 @@ static int eap_ttls_process_handshake(struct eap_s 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; @@ -1392,7 +1410,13 @@ static int eap_ttls_process_handshake(struct eap_s 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,7 +1443,6 @@ static int eap_ttls_process_handshake(struct eap_s } if (res == 2) { - struct wpabuf msg; /* * Application data included in the handshake message. */ @@ -1426,8 +1449,7 @@ static int eap_ttls_process_handshake(struct eap_s 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 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 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); Index: contrib/wpa/src/eap_peer/eap_wsc.c =================================================================== --- contrib/wpa/src/eap_peer/eap_wsc.c (revision 289259) +++ contrib/wpa/src/eap_peer/eap_wsc.c (working copy) @@ -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; Index: contrib/wpa/src/eap_server/eap.h =================================================================== --- contrib/wpa/src/eap_server/eap.h (revision 289259) +++ contrib/wpa/src/eap_server/eap.h (working copy) @@ -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 */ Index: contrib/wpa/src/eap_server/eap_i.h =================================================================== --- contrib/wpa/src/eap_server/eap_i.h (revision 289259) +++ contrib/wpa/src/eap_server/eap_i.h (working copy) @@ -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; Index: contrib/wpa/src/eap_server/eap_server.c =================================================================== --- contrib/wpa/src/eap_server/eap_server.c (revision 289259) +++ contrib/wpa/src/eap_server/eap_server.c (working copy) @@ -96,7 +96,8 @@ static struct wpabuf * eap_sm_buildInitiateReauthS 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 * 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 * 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 */ Index: contrib/wpa/src/eap_server/eap_server_eke.c =================================================================== --- contrib/wpa/src/eap_server/eap_server_eke.c (revision 289259) +++ contrib/wpa/src/eap_server/eap_server_eke.c (working copy) @@ -766,6 +766,29 @@ static Boolean eap_eke_isSuccess(struct eap_sm *sm } +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) Index: contrib/wpa/src/eap_server/eap_server_fast.c =================================================================== --- contrib/wpa/src/eap_server/eap_server_fast.c (revision 289259) +++ contrib/wpa/src/eap_server/eap_server_fast.c (working copy) @@ -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; Index: contrib/wpa/src/eap_server/eap_server_mschapv2.c =================================================================== --- contrib/wpa/src/eap_server/eap_server_mschapv2.c (revision 289259) +++ contrib/wpa/src/eap_server/eap_server_mschapv2.c (working copy) @@ -360,6 +360,19 @@ static void eap_mschapv2_process_response(struct e } } +#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"); Index: contrib/wpa/src/eap_server/eap_server_peap.c =================================================================== --- contrib/wpa/src/eap_server/eap_server_peap.c (revision 289259) +++ contrib/wpa/src/eap_server/eap_server_peap.c (working copy) @@ -95,9 +95,40 @@ static void eap_peap_state(struct eap_peap_data *d 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); +} + + static void eap_peap_req_success(struct eap_sm *sm, struct eap_peap_data *data) { @@ -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,7 +570,7 @@ static Boolean eap_peap_check(struct eap_sm *sm, v 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); @@ -546,8 +577,7 @@ static int eap_peap_phase2_init(struct eap_sm *sm, 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 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 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(struc 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(struc "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(struc 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(struc 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(struc "database", sm->identity, sm->identity_len); eap_peap_req_failure(sm, data); + next_vendor = EAP_VENDOR_IETF; next_type = EAP_TYPE_NONE; break; } @@ -967,6 +1010,7 @@ static void eap_peap_process_phase2_response(struc 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; } @@ -973,12 +1017,15 @@ static void eap_peap_process_phase2_response(struc #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(struc 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 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 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 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, vo 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); } Index: contrib/wpa/src/eap_server/eap_server_pwd.c =================================================================== --- contrib/wpa/src/eap_server/eap_server_pwd.c (revision 289259) +++ contrib/wpa/src/eap_server/eap_server_pwd.c (working copy) @@ -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 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 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 "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, str 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, st 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,11 +942,21 @@ static void eap_pwd_process(struct eap_sm *sm, voi * 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 " @@ -912,6 +963,7 @@ static void eap_pwd_process(struct eap_sm *sm, voi "buffer fragments!"); return; } + data->in_frag_pos = 0; pos += sizeof(u16); len -= sizeof(u16); } Index: contrib/wpa/src/eap_server/eap_server_tls.c =================================================================== --- contrib/wpa/src/eap_server/eap_server_tls.c (revision 289259) +++ contrib/wpa/src/eap_server/eap_server_tls.c (working copy) @@ -48,9 +48,26 @@ static void eap_tls_state(struct eap_tls_data *dat 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); +} + + static void * eap_tls_init(struct eap_sm *sm) { struct eap_tls_data *data; @@ -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 *s 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_s 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, voi 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); } Index: contrib/wpa/src/eap_server/eap_server_tls_common.c =================================================================== --- contrib/wpa/src/eap_server/eap_server_tls_common.c (revision 289259) +++ contrib/wpa/src/eap_server/eap_server_tls_common.c (working copy) @@ -44,8 +44,11 @@ static void eap_server_tls_log_cb(void *ctx, const 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, str #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, 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_prf(sm->ssl_ctx, data->conn, label, 0, 0, + out, len)) { + os_free(out); + return NULL; + } - 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; - - 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_s 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) Index: contrib/wpa/src/eap_server/eap_server_ttls.c =================================================================== --- contrib/wpa/src/eap_server/eap_server_ttls.c (revision 289259) +++ contrib/wpa/src/eap_server/eap_server_ttls.c (working copy) @@ -71,9 +71,39 @@ static void eap_ttls_state(struct eap_ttls_data *d 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); +} + + static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id, int mandatory, size_t len) { @@ -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 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 ea 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 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 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(struc } 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(s 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 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, vo 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); } Index: contrib/wpa/src/eap_server/eap_tls_common.h =================================================================== --- contrib/wpa/src/eap_server/eap_tls_common.h (revision 289259) +++ contrib/wpa/src/eap_server/eap_tls_common.h (working copy) @@ -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); Index: contrib/wpa/src/eapol_auth/eapol_auth_sm.c =================================================================== --- contrib/wpa/src/eapol_auth/eapol_auth_sm.c (revision 289259) +++ contrib/wpa/src/eapol_auth/eapol_auth_sm.c (working copy) @@ -1,6 +1,6 @@ /* * IEEE 802.1X-2004 Authenticator - EAPOL state machine - * Copyright (c) 2002-2014, Jouni Malinen + * Copyright (c) 2002-2015, Jouni Malinen * * 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 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 } -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_m } +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 } dst->erp_send_reauth_start = src->erp_send_reauth_start; dst->erp = src->erp; + dst->tls_session_lifetime = src->tls_session_lifetime; return 0; Index: contrib/wpa/src/eapol_auth/eapol_auth_sm.h =================================================================== --- contrib/wpa/src/eapol_auth/eapol_auth_sm.h (revision 289259) +++ contrib/wpa/src/eapol_auth/eapol_auth_sm.h (working copy) @@ -1,6 +1,6 @@ /* * IEEE 802.1X-2004 Authenticator - EAPOL state machine - * Copyright (c) 2002-2009, Jouni Malinen + * Copyright (c) 2002-2015, Jouni Malinen * * 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 *s 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 */ Index: contrib/wpa/src/eapol_supp/eapol_supp_sm.c =================================================================== --- contrib/wpa/src/eapol_supp/eapol_supp_sm.c (revision 289259) +++ contrib/wpa/src/eapol_supp/eapol_supp_sm.c (working copy) @@ -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 *s 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 *s 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 *s 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 } -static struct eapol_callbacks eapol_cb = +static const struct eapol_callbacks eapol_cb = { eapol_sm_get_config, eapol_sm_get_bool, Index: contrib/wpa/src/fst/Makefile =================================================================== --- contrib/wpa/src/fst/Makefile (revision 0) +++ contrib/wpa/src/fst/Makefile (working copy) @@ -0,0 +1,8 @@ +all: + @echo Nothing to be made. + +clean: + rm -f *~ *.o *.d + +install: + @echo Nothing to be made. Property changes on: contrib/wpa/src/fst/Makefile ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: contrib/wpa/src/fst/fst.c =================================================================== --- contrib/wpa/src/fst/fst.c (revision 0) +++ contrib/wpa/src/fst/fst.c (working copy) @@ -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; + } +} Property changes on: contrib/wpa/src/fst/fst.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: contrib/wpa/src/fst/fst.h =================================================================== --- contrib/wpa/src/fst/fst.h (revision 0) +++ contrib/wpa/src/fst/fst.h (working copy) @@ -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 */ Property changes on: contrib/wpa/src/fst/fst.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: contrib/wpa/src/fst/fst_ctrl_aux.c =================================================================== --- contrib/wpa/src/fst/fst_ctrl_aux.c (revision 0) +++ contrib/wpa/src/fst/fst_ctrl_aux.c (working copy) @@ -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)); +} Property changes on: contrib/wpa/src/fst/fst_ctrl_aux.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: contrib/wpa/src/fst/fst_ctrl_aux.h =================================================================== --- contrib/wpa/src/fst/fst_ctrl_aux.h (revision 0) +++ contrib/wpa/src/fst/fst_ctrl_aux.h (working copy) @@ -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 */ Property changes on: contrib/wpa/src/fst/fst_ctrl_aux.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: contrib/wpa/src/fst/fst_ctrl_defs.h =================================================================== --- contrib/wpa/src/fst/fst_ctrl_defs.h (revision 0) +++ contrib/wpa/src/fst/fst_ctrl_defs.h (working copy) @@ -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 */ Property changes on: contrib/wpa/src/fst/fst_ctrl_defs.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: contrib/wpa/src/fst/fst_ctrl_iface.c =================================================================== --- contrib/wpa/src/fst/fst_ctrl_iface.c (revision 0) +++ contrib/wpa/src/fst/fst_ctrl_iface.c (working copy) @@ -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; +} Property changes on: contrib/wpa/src/fst/fst_ctrl_iface.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: contrib/wpa/src/fst/fst_ctrl_iface.h =================================================================== --- contrib/wpa/src/fst/fst_ctrl_iface.h (revision 0) +++ contrib/wpa/src/fst/fst_ctrl_iface.h (working copy) @@ -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 */ Property changes on: contrib/wpa/src/fst/fst_ctrl_iface.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: contrib/wpa/src/fst/fst_defs.h =================================================================== --- contrib/wpa/src/fst/fst_defs.h (revision 0) +++ contrib/wpa/src/fst/fst_defs.h (working copy) @@ -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 */ Property changes on: contrib/wpa/src/fst/fst_defs.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: contrib/wpa/src/fst/fst_group.c =================================================================== --- contrib/wpa/src/fst/fst_group.c (revision 0) +++ contrib/wpa/src/fst/fst_group.c (working copy) @@ -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); + } +} Property changes on: contrib/wpa/src/fst/fst_group.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: contrib/wpa/src/fst/fst_group.h =================================================================== --- contrib/wpa/src/fst/fst_group.h (revision 0) +++ contrib/wpa/src/fst/fst_group.h (working copy) @@ -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 */ Property changes on: contrib/wpa/src/fst/fst_group.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: contrib/wpa/src/fst/fst_iface.c =================================================================== --- contrib/wpa/src/fst/fst_iface.c (revision 0) +++ contrib/wpa/src/fst/fst_iface.c (working copy) @@ -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); +} Property changes on: contrib/wpa/src/fst/fst_iface.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: contrib/wpa/src/fst/fst_iface.h =================================================================== --- contrib/wpa/src/fst/fst_iface.h (revision 0) +++ contrib/wpa/src/fst/fst_iface.h (working copy) @@ -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 */ Property changes on: contrib/wpa/src/fst/fst_iface.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: contrib/wpa/src/fst/fst_internal.h =================================================================== --- contrib/wpa/src/fst/fst_internal.h (revision 0) +++ contrib/wpa/src/fst/fst_internal.h (working copy) @@ -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 */ Property changes on: contrib/wpa/src/fst/fst_internal.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: contrib/wpa/src/fst/fst_session.c =================================================================== --- contrib/wpa/src/fst/fst_session.c (revision 0) +++ contrib/wpa/src/fst/fst_session.c (working copy) @@ -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 */ Property changes on: contrib/wpa/src/fst/fst_session.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: contrib/wpa/src/fst/fst_session.h =================================================================== --- contrib/wpa/src/fst/fst_session.h (revision 0) +++ contrib/wpa/src/fst/fst_session.h (working copy) @@ -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 */ Property changes on: contrib/wpa/src/fst/fst_session.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: contrib/wpa/src/p2p/p2p.c =================================================================== --- contrib/wpa/src/p2p/p2p.c (revision 289259) +++ contrib/wpa/src/p2p/p2p.c (working copy) @@ -48,9 +48,8 @@ static void p2p_scan_timeout(void *eloop_ctx, void #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_scan_timeout(void *eloop_ctx, void } -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 *p2 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 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 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 * 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,15 +750,22 @@ int p2p_add_device(struct p2p_data *p2p, const u8 /* * 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; } @@ -773,11 +772,13 @@ int p2p_add_device(struct p2p_data *p2p, const u8 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 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, con 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 ti * 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; - } + 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++; + } - os_memcpy(&p2p->query_hash[i * P2PS_HASH_LEN], buf, - P2PS_HASH_LEN); - } + 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 ti /* 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_dat 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, struc } -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 @@ int p2p_match_dev_type(struct p2p_data *p2p, struc 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 @@ int p2p_match_dev_type(struct p2p_data *p2p, struc 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 @@ int p2p_match_dev_type(struct p2p_data *p2p, struc 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,7 +2240,8 @@ static int p2p_service_find_asp(struct p2p_data *p 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; @@ -2240,6 +2248,7 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *ad 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 *ad 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->query_count = 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; + } + 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 *ad 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 *ad * 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 *ad 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 *ad 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 *a 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 *a 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_dat 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 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 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 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 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 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,19 +2957,13 @@ 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); + p2p_service_flush_asp(p2p); - adv = p2p->p2ps_adv_list; - while (adv) { - prev = adv; - adv = adv->next; - os_free(prev); - } - 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_in "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_in "[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 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 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 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 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]); + } +} Index: contrib/wpa/src/p2p/p2p.h =================================================================== --- contrib/wpa/src/p2p/p2p.h (revision 289259) +++ contrib/wpa/src/p2p/p2p.h (working copy) @@ -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,8 +21,14 @@ #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 */ #define P2P_MAX_REG_CLASSES 10 @@ -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- 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 */ Index: contrib/wpa/src/p2p/p2p_build.c =================================================================== --- contrib/wpa/src/p2p/p2p_build.c (revision 289259) +++ contrib/wpa/src/p2p/p2p_build.c (working copy) @@ -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 * } +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, /* 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 *b } -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; + 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; + } -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 (*ie_len) { + int ie_data_len = (*pos - *ie_len) - 1; + + 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; + } - svc_len = os_strlen(adv->svc_name); + 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)]; - if (7 + svc_len + total_len > MAX_SVC_ADV_LEN) { - /* Can't fit... return wildcard */ - total_len = MAX_SVC_ADV_LEN + 1; - break; - } + WPA_PUT_LE32(holder, adv_id); + WPA_PUT_BE16(&holder[sizeof(u32)], config_methods); + holder[sizeof(u32) + sizeof(u16)] = svc_len; - 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)]; + if (front) + wpabuf_put_data(buf, holder, front); - /* 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)); - } + p2p_buf_update_ie_hdr(buf, *ie_len); + *ie_len = p2p_buf_add_ie_hdr(buf); - /* We are guaranteed at least one byte for svc_len */ - wpabuf_put_u8(tmp_buf, svc_len); - remaining -= sizeof(u8); + 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 (remaining < svc_len) { - size_t front = remaining; - size_t back = svc_len - front; + if (remaining < svc_len) { + /* split svc_name between two or three IEs */ + 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); + if (front) + wpabuf_put_data(buf, svc_name, front); - /* 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); - } + p2p_buf_update_ie_hdr(buf, *ie_len); + *ie_len = p2p_buf_add_ie_hdr(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; + /* 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); } - /* adv_id config_methods svc_string */ - total_len += sizeof(u32) + sizeof(u16) + sizeof(u8) + svc_len; + wpabuf_put_data(buf, &svc_name[front], back); + remaining = 255 - 4 - back; + } else { + wpabuf_put_data(buf, svc_name, svc_len); + remaining -= svc_len; } - if (tag_len) - p2p_buf_update_ie_hdr(tmp_buf, tag_len); + p2p_buf_update_ie_hdr(buf, *ie_len); - if (ie_len) - WPA_PUT_LE16(ie_len, (u16) total_len); + /* set *ie_len to NULL if a new IE has to be added on the next call */ + if (!remaining) + *ie_len = NULL; -wild_hash: - /* If all fit, return matching instances, otherwise the wildcard */ - if (total_len <= MAX_SVC_ADV_LEN) { - wpabuf_put_buf(buf, tmp_buf); - } else { - char *wild_card = P2PS_WILD_HASH_STR; - u8 wild_len; + /* set *pos to point to the next byte to update */ + *pos = wpabuf_put(buf, 0); - /* 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)); + *total_len += info_len; + WPA_PUT_LE16(attr_len, (u16) *total_len); + return 0; +} - 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); +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); } Index: contrib/wpa/src/p2p/p2p_dev_disc.c =================================================================== --- contrib/wpa/src/p2p/p2p_dev_disc.c (revision 289259) +++ contrib/wpa/src/p2p/p2p_dev_disc.c (working copy) @@ -314,7 +314,7 @@ void p2p_process_go_disc_req(struct p2p_data *p2p, 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; Index: contrib/wpa/src/p2p/p2p_go_neg.c =================================================================== --- contrib/wpa/src/p2p/p2p_go_neg.c (revision 289259) +++ contrib/wpa/src/p2p/p2p_go_neg.c (working copy) @@ -185,6 +185,9 @@ static struct wpabuf * p2p_build_go_neg_req(struct 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(struc 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, st } +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, 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, 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, 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); Index: contrib/wpa/src/p2p/p2p_group.c =================================================================== --- contrib/wpa/src/p2p/p2p_group.c (revision 289259) +++ contrib/wpa/src/p2p/p2p_group.c (working copy) @@ -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; +} Index: contrib/wpa/src/p2p/p2p_i.h =================================================================== --- contrib/wpa/src/p2p/p2p_i.h (revision 289259) +++ contrib/wpa/src/p2p/p2p_i.h (working copy) @@ -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 wpab 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 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 *p 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 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, Index: contrib/wpa/src/p2p/p2p_invitation.c =================================================================== --- contrib/wpa/src/p2p/p2p_invitation.c (revision 289259) +++ contrib/wpa/src/p2p/p2p_invitation.c (working copy) @@ -85,6 +85,9 @@ static struct wpabuf * p2p_build_invitation_req(st 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(s 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(s 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 *p 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 * 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); Index: contrib/wpa/src/p2p/p2p_parse.c =================================================================== --- contrib/wpa/src/p2p/p2p_parse.c (revision 289259) +++ contrib/wpa/src/p2p/p2p_parse.c (working copy) @@ -149,7 +149,8 @@ static int p2p_parse_attribute(u8 id, const u8 *da 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 *da 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 *da 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 *da 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, stru 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, stru } #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_l 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_ 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_ 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--; } Index: contrib/wpa/src/p2p/p2p_pd.c =================================================================== --- contrib/wpa/src/p2p/p2p_pd.c (revision 289259) +++ contrib/wpa/src/p2p/p2p_pd.c (working copy) @@ -44,7 +44,7 @@ static void p2ps_add_new_group_info(struct p2p_dat { 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_dat 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 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 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_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(st 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(st /* 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(st 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(st 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 *p } +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 *p2 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 *p2 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 *p2 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 *p2 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,10 +576,23 @@ void p2p_process_prov_disc_req(struct p2p_data *p2 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, msg.wps_config_methods); @@ -624,6 +680,15 @@ void p2p_process_prov_disc_req(struct p2p_data *p2 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 *p2 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 + } else if (!resp_fcap.cpt) { + p2p_dbg(p2p, + "Incompatible P2PS feature capability CPT bitmask"); + reject = + P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + } else { reject = P2P_SC_SUCCESS; + } p2p->p2ps_prov->status = reject; p2p->p2ps_prov->conncap = conncap; @@ -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 *p 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 *p 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 *p 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 *p } 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 *p 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 *p 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 *p 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 /* 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; +} Index: contrib/wpa/src/p2p/p2p_utils.c =================================================================== --- contrib/wpa/src/p2p/p2p_utils.c (revision 289259) +++ contrib/wpa/src/p2p/p2p_utils.c (working copy) @@ -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; + if (ieee80211_freq_to_channel_ext(freq, 0, 0, op_class, channel) == + NUM_HOSTAPD_MODES) + 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; - } - - return -1; + return 0; } @@ -497,6 +459,8 @@ int p2p_channels_to_freqs(const struct p2p_channel 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, @@ -503,6 +467,14 @@ int p2p_channels_to_freqs(const struct p2p_channel 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; } } Index: contrib/wpa/src/radius/radius.c =================================================================== --- contrib/wpa/src/radius/radius.c (revision 289259) +++ contrib/wpa/src/radius/radius.c (working copy) @@ -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 @@ struct radius_attr_type { #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 @@ struct radius_attr_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 *ms } /* 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; } Index: contrib/wpa/src/radius/radius_das.c =================================================================== --- contrib/wpa/src/radius/radius_das.c (revision 289259) +++ contrib/wpa/src/radius/radius_das.c (working copy) @@ -245,7 +245,7 @@ static void radius_das_receive(int sock, void *elo (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 " Index: contrib/wpa/src/radius/radius_server.c =================================================================== --- contrib/wpa/src/radius/radius_server.c (revision 289259) +++ contrib/wpa/src/radius/radius_server.c (working copy) @@ -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 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, c 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, st #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, Index: contrib/wpa/src/radius/radius_server.h =================================================================== --- contrib/wpa/src/radius/radius_server.h (revision 289259) +++ contrib/wpa/src/radius/radius_server.h (working copy) @@ -170,6 +170,8 @@ struct radius_server_conf { const char *erp_domain; + unsigned int tls_session_lifetime; + /** * wps - Wi-Fi Protected Setup context * Index: contrib/wpa/src/rsn_supp/tdls.c =================================================================== --- contrib/wpa/src/rsn_supp/tdls.c (revision 289259) +++ contrib/wpa/src/rsn_supp/tdls.c (working copy) @@ -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_ 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_eap 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,7 +2860,7 @@ 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); @@ -2870,7 +2867,7 @@ void wpa_tdls_disassoc(struct wpa_sm *sm) } -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 @@ void wpa_tdls_disassoc(struct wpa_sm *sm) 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 * 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; Index: contrib/wpa/src/rsn_supp/wpa.c =================================================================== --- contrib/wpa/src/rsn_supp/wpa.c (revision 289259) +++ contrib/wpa/src/rsn_supp/wpa.c (working copy) @@ -249,6 +249,17 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *s "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(struc &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(struc 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(struc 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 /* 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 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 || Index: contrib/wpa/src/rsn_supp/wpa_ft.c =================================================================== --- contrib/wpa/src/rsn_supp/wpa_ft.c (revision 289259) +++ contrib/wpa/src/rsn_supp/wpa_ft.c (working copy) @@ -168,9 +168,7 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, 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); Index: contrib/wpa/src/rsn_supp/wpa_ie.c =================================================================== --- contrib/wpa/src/rsn_supp/wpa_ie.c (revision 289259) +++ contrib/wpa/src/rsn_supp/wpa_ie.c (working copy) @@ -30,6 +30,9 @@ int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ { 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 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 } 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) { Index: contrib/wpa/src/rsn_supp/wpa_ie.h =================================================================== --- contrib/wpa/src/rsn_supp/wpa_ie.h (revision 289259) +++ contrib/wpa/src/rsn_supp/wpa_ie.h (working copy) @@ -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; Index: contrib/wpa/src/tls/libtommath.c =================================================================== --- contrib/wpa/src/tls/libtommath.c (revision 289259) +++ contrib/wpa/src/tls/libtommath.c (working copy) @@ -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 * } /* 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; } Index: contrib/wpa/src/tls/tlsv1_client.c =================================================================== --- contrib/wpa/src/tls/tlsv1_client.c (revision 289259) +++ contrib/wpa/src/tls/tlsv1_client.c (working copy) @@ -714,12 +714,12 @@ int tlsv1_client_hello_ext(struct tlsv1_client *co /** - * 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_hello_ext(struct tlsv1_client *co 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; Index: contrib/wpa/src/tls/tlsv1_client.h =================================================================== --- contrib/wpa/src/tls/tlsv1_client.h (revision 289259) +++ contrib/wpa/src/tls/tlsv1_client.h (working copy) @@ -36,7 +36,7 @@ int tlsv1_client_shutdown(struct tlsv1_client *con 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, Index: contrib/wpa/src/tls/tlsv1_server.c =================================================================== --- contrib/wpa/src/tls/tlsv1_server.c (revision 289259) +++ contrib/wpa/src/tls/tlsv1_server.c (working copy) @@ -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_resumed(struct tlsv1_server *conn 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; Index: contrib/wpa/src/tls/tlsv1_server.h =================================================================== --- contrib/wpa/src/tls/tlsv1_server.h (revision 289259) +++ contrib/wpa/src/tls/tlsv1_server.h (working copy) @@ -32,7 +32,7 @@ int tlsv1_server_get_cipher(struct tlsv1_server *c 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); Index: contrib/wpa/src/tls/x509v3.c =================================================================== --- contrib/wpa/src/tls/x509v3.c (revision 289259) +++ contrib/wpa/src/tls/x509v3.c (working copy) @@ -1511,7 +1511,7 @@ struct x509_certificate * x509_certificate_parse(c 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; } Index: contrib/wpa/src/utils/browser-wpadebug.c =================================================================== --- contrib/wpa/src/utils/browser-wpadebug.c (revision 289259) +++ contrib/wpa/src/utils/browser-wpadebug.c (working copy) @@ -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)); Index: contrib/wpa/src/utils/common.c =================================================================== --- contrib/wpa/src/utils/common.c (revision 289259) +++ contrib/wpa/src/utils/common.c (working copy) @@ -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 ch 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 */ 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; + char *token = (char *) cstr_token(str, delim, (const char **) context); - if (*context) - pos = *context; + if (token && **context) + *(*context)++ = '\0'; - while (*pos && os_strchr(delim, *pos)) - pos++; - if (!*pos) - return NULL; - - end = pos + 1; - while (*end && !os_strchr(delim, *end)) - end++; - - if (*end) - *end++ = '\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; +} Index: contrib/wpa/src/utils/common.h =================================================================== --- contrib/wpa/src/utils/common.h (revision 289259) +++ contrib/wpa/src/utils/common.h (working copy) @@ -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 @@ -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 @@ -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 le 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 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); /* Index: contrib/wpa/src/utils/eloop.c =================================================================== --- contrib/wpa/src/utils/eloop.c (revision 289259) +++ contrib/wpa/src/utils/eloop.c (working copy) @@ -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_ 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 el } 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 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; } Index: contrib/wpa/src/utils/http_curl.c =================================================================== --- contrib/wpa/src/utils/http_curl.c (revision 289259) +++ contrib/wpa/src/utils/http_curl.c (working copy) @@ -855,8 +855,10 @@ static int validate_server_cert(struct http_ctx *c 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, X5 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 *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) Index: contrib/wpa/src/utils/includes.h =================================================================== --- contrib/wpa/src/utils/includes.h (revision 289259) +++ contrib/wpa/src/utils/includes.h (working copy) @@ -17,26 +17,22 @@ #include "build_config.h" #include +#include #include #include #include #ifndef _WIN32_WCE -#ifndef CONFIG_TI_COMPILER #include #include -#endif /* CONFIG_TI_COMPILER */ #include #endif /* _WIN32_WCE */ #include -#ifndef CONFIG_TI_COMPILER #ifndef _MSC_VER #include #endif /* _MSC_VER */ -#endif /* CONFIG_TI_COMPILER */ #ifndef CONFIG_NATIVE_WINDOWS -#ifndef CONFIG_TI_COMPILER #include #include #include @@ -44,7 +40,6 @@ #include #include #endif /* __vxworks */ -#endif /* CONFIG_TI_COMPILER */ #endif /* CONFIG_NATIVE_WINDOWS */ #endif /* INCLUDES_H */ Index: contrib/wpa/src/utils/os.h =================================================================== --- contrib/wpa/src/utils/os.h (revision 289259) +++ contrib/wpa/src/utils/os.h (working copy) @@ -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, #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 */ Index: contrib/wpa/src/utils/os_internal.c =================================================================== --- contrib/wpa/src/utils/os_internal.c (revision 289259) +++ contrib/wpa/src/utils/os_internal.c (working copy) @@ -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); Index: contrib/wpa/src/utils/os_none.c =================================================================== --- contrib/wpa/src/utils/os_none.c (revision 289259) +++ contrib/wpa/src/utils/os_none.c (working copy) @@ -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; Index: contrib/wpa/src/utils/os_unix.c =================================================================== --- contrib/wpa/src/utils/os_unix.c (revision 289259) +++ contrib/wpa/src/utils/os_unix.c (working copy) @@ -17,6 +17,12 @@ #include #endif /* ANDROID */ +#ifdef __MACH__ +#include +#include +#include +#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,27 @@ int os_file_exists(const char *fname) } +int os_fdatasync(FILE *stream) +{ + if (!fflush(stream)) { +#ifdef __FreeBSD__ + return fsync(fileno(stream)); +#elif defined(__MACH__) + return fdatasync(fileno(stream)); +#else /* __MACH__ */ +#ifdef F_FULLFSYNC + /* OS X does not implement fdatasync(). */ + return fcntl(fileno(stream), F_FULLFSYNC); +#else /* F_FULLFSYNC */ +#error Neither fdatasync nor F_FULLSYNC are defined +#endif /* F_FULLFSYNC */ +#endif /* __MACH__ */ + } + + return -1; +} + + #ifndef WPA_TRACE void * os_zalloc(size_t size) { @@ -575,6 +623,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) Index: contrib/wpa/src/utils/os_win32.c =================================================================== --- contrib/wpa/src/utils/os_win32.c (revision 289259) +++ contrib/wpa/src/utils/os_win32.c (working copy) @@ -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); Index: contrib/wpa/src/utils/radiotap.c =================================================================== --- contrib/wpa/src/utils/radiotap.c (revision 289259) +++ contrib/wpa/src/utils/radiotap.c (working copy) @@ -123,13 +123,13 @@ int ieee80211_radiotap_iterator_init( /* find payload start allowing for extended bitmap(s) */ - if (iterator->_bitmap_shifter & (1<_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); /* Index: contrib/wpa/src/utils/utils_module_tests.c =================================================================== --- contrib/wpa/src/utils/utils_module_tests.c (revision 289259) +++ contrib/wpa/src/utils/utils_module_tests.c (working copy) @@ -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; Index: contrib/wpa/src/utils/wpa_debug.c =================================================================== --- contrib/wpa/src/utils/wpa_debug.c (revision 289259) +++ contrib/wpa/src/utils/wpa_debug.c (working copy) @@ -307,7 +307,7 @@ static void _wpa_hexdump(int level, const char *ti "%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 *ti 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 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 ch 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, con 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 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, uns 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; +} Index: contrib/wpa/src/utils/wpa_debug.h =================================================================== --- contrib/wpa/src/utils/wpa_debug.h (revision 289259) +++ contrib/wpa/src/utils/wpa_debug.h (working copy) @@ -164,6 +164,7 @@ void wpa_hexdump_ascii_key(int level, const char * #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(v #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 */ Index: contrib/wpa/src/utils/wpabuf.c =================================================================== --- contrib/wpa/src/utils/wpabuf.c (revision 289259) +++ contrib/wpa/src/utils/wpabuf.c (working copy) @@ -17,7 +17,7 @@ struct wpabuf_trace { unsigned int magic; -}; +} __attribute__((aligned(8))); static struct wpabuf_trace * wpabuf_get_trace(const struct wpabuf *buf) { Index: contrib/wpa/src/wps/http_client.c =================================================================== --- contrib/wpa/src/wps/http_client.c (revision 289259) +++ contrib/wpa/src/wps/http_client.c (working copy) @@ -85,15 +85,16 @@ static void http_client_tx_ready(int sock, void *e { 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 *e 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,16 +146,13 @@ struct http_client * http_client_addr(struct socka 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))) { @@ -162,8 +159,7 @@ struct http_client * http_client_addr(struct socka 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 socka } if (eloop_register_sock(c->sd, EVENT_TYPE_WRITE, http_client_tx_ready, - 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; - if (eloop_register_timeout(HTTP_CLIENT_TIMEOUT_SEC, 0, - http_client_timeout, c, NULL)) { - http_client_free(c); - return NULL; - } - c->req = req; return c; + +fail: + http_client_free(c); + return NULL; } Index: contrib/wpa/src/wps/http_server.c =================================================================== --- contrib/wpa/src/wps/http_server.c (revision 289259) +++ contrib/wpa/src/wps/http_server.c (working copy) @@ -277,11 +277,9 @@ struct http_server * http_server_init(struct in_ad "%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; Index: contrib/wpa/src/wps/httpread.c =================================================================== --- contrib/wpa/src/wps/httpread.c (revision 289259) +++ contrib/wpa/src/wps/httpread.c (working copy) @@ -44,17 +44,7 @@ #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) */ struct httpread { @@ -136,8 +126,7 @@ static void httpread_timeout_handler(void *eloop_d */ 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 *el 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 *el * 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 *el 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 *el 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 *el !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 *el 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 *el 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 *el /* 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 *el /* 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 *el */ 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 *el } 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 *el 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 *el 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 */ Index: contrib/wpa/src/wps/ndef.c =================================================================== --- contrib/wpa/src/wps/ndef.c (revision 289259) +++ contrib/wpa/src/wps/ndef.c (working copy) @@ -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 s 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 s 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 st } -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) Index: contrib/wpa/src/wps/wps.c =================================================================== --- contrib/wpa/src/wps/wps.c (revision 289259) +++ contrib/wpa/src/wps/wps.c (working copy) @@ -355,17 +355,17 @@ int wps_is_addr_authorized(const struct wpabuf *ms 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) + sel_a = attr.selected_registrar && *attr.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; - sel_a = attr_a.selected_registrar && *attr_a.selected_registrar != 0; - sel_b = attr_b.selected_registrar && *attr_b.selected_registrar != 0; - if (sel_a && !sel_b) return -1; if (!sel_a && sel_b) @@ -618,7 +618,8 @@ int wps_attr_text(struct wpabuf *data, char *buf, 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]; Index: contrib/wpa/src/wps/wps.h =================================================================== --- contrib/wpa/src/wps/wps.h (revision 289259) +++ contrib/wpa/src/wps/wps.h (working copy) @@ -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 Index: contrib/wpa/src/wps/wps_attr_parse.c =================================================================== --- contrib/wpa/src/wps/wps_attr_parse.c (revision 289259) +++ contrib/wpa/src/wps/wps_attr_parse.c (working copy) @@ -447,25 +447,55 @@ static int wps_set_attr(struct wps_parse_attr *att 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 *att 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; Index: contrib/wpa/src/wps/wps_attr_parse.h =================================================================== --- contrib/wpa/src/wps/wps_attr_parse.h (revision 289259) +++ contrib/wpa/src/wps/wps_attr_parse.h (working copy) @@ -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 +#define MAX_REQ_DEV_TYPE_COUNT 10 + + 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]; - 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; - 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); Index: contrib/wpa/src/wps/wps_common.c =================================================================== --- contrib/wpa/src/wps/wps_common.c (revision 289259) +++ contrib/wpa/src/wps/wps_common.c (working copy) @@ -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, i 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; Index: contrib/wpa/src/wps/wps_defs.h =================================================================== --- contrib/wpa/src/wps/wps_defs.h (revision 289259) +++ contrib/wpa/src/wps/wps_defs.h (working copy) @@ -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 Index: contrib/wpa/src/wps/wps_enrollee.c =================================================================== --- contrib/wpa/src/wps/wps_enrollee.c (revision 289259) +++ contrib/wpa/src/wps/wps_enrollee.c (working copy) @@ -759,7 +759,7 @@ static int wps_process_cred_e(struct wps_data *wps 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_da 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_da 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; } Index: contrib/wpa/src/wps/wps_er.c =================================================================== --- contrib/wpa/src/wps/wps_er.c (revision 289259) +++ contrib/wpa/src/wps/wps_er.c (working copy) @@ -1649,11 +1649,15 @@ static void wps_er_http_put_message_cb(void *ctx, case HTTP_CLIENT_OK: wpa_printf(MSG_DEBUG, "WPS ER: PutMessage OK"); reply = http_client_get_body(c); - if (reply == NULL) + if (reply) + msg = os_zalloc(wpabuf_len(reply) + 1); + if (msg == NULL) { + if (ap->wps) { + wps_deinit(ap->wps); + ap->wps = NULL; + } break; - msg = os_zalloc(wpabuf_len(reply) + 1); - if (msg == NULL) - break; + } os_memcpy(msg, wpabuf_head(reply), wpabuf_len(reply)); break; case HTTP_CLIENT_FAILED: @@ -1709,7 +1713,7 @@ static void wps_er_ap_put_message(struct wps_er_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, @@ -1716,14 +1720,23 @@ static void wps_er_ap_put_message(struct wps_er_ap &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; + } } Index: contrib/wpa/src/wps/wps_er_ssdp.c =================================================================== --- contrib/wpa/src/wps/wps_er_ssdp.c (revision 289259) +++ contrib/wpa/src/wps/wps_er_ssdp.c (working copy) @@ -78,9 +78,7 @@ static void wps_er_ssdp_rx(int sd, void *eloop_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; Index: contrib/wpa/src/wps/wps_module_tests.c =================================================================== --- contrib/wpa/src/wps/wps_module_tests.c (revision 289259) +++ contrib/wpa/src/wps/wps_module_tests.c (working copy) @@ -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; Index: contrib/wpa/src/wps/wps_registrar.c =================================================================== --- contrib/wpa/src/wps/wps_registrar.c (revision 289259) +++ contrib/wpa/src/wps/wps_registrar.c (working copy) @@ -2605,6 +2605,8 @@ static enum wps_process_res wps_process_m1(struct 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); @@ -2611,7 +2613,8 @@ static enum wps_process_res wps_process_m1(struct 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(s 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; Index: contrib/wpa/src/wps/wps_upnp.c =================================================================== --- contrib/wpa/src/wps/wps_upnp.c (revision 289259) +++ contrib/wpa/src/wps/wps_upnp.c (working copy) @@ -695,6 +695,7 @@ struct subscription * subscription_start(struct up 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 up 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; Index: contrib/wpa/src/wps/wps_upnp_ap.c =================================================================== --- contrib/wpa/src/wps/wps_upnp_ap.c (revision 289259) +++ contrib/wpa/src/wps/wps_upnp_ap.c (working copy) @@ -34,12 +34,10 @@ int upnp_er_set_selected_registrar(struct wps_regi wpa_hexdump_buf(MSG_MSGDUMP, "WPS: SetSelectedRegistrar attributes", msg); - if (wps_validate_upnp_set_selected_registrar(msg) < 0) + if (wps_validate_upnp_set_selected_registrar(msg) < 0 || + wps_parse_msg(msg, &attr) < 0) return -1; - if (wps_parse_msg(msg, &attr) < 0) - return -1; - s->reg = reg; eloop_cancel_timeout(upnp_er_set_selected_timeout, s, reg); Index: contrib/wpa/src/wps/wps_upnp_event.c =================================================================== --- contrib/wpa/src/wps/wps_upnp_event.c (revision 289259) +++ contrib/wpa/src/wps/wps_upnp_event.c (working copy) @@ -276,12 +276,10 @@ 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)) + if (dl_list_empty(&s->addr_list) || + s->current_event || + dl_list_empty(&s->event_queue)) return -1; - if (s->current_event) - return -1; - if (dl_list_empty(&s->event_queue)) - return -1; s->current_event = e = event_dequeue(s); Index: contrib/wpa/src/wps/wps_upnp_ssdp.c =================================================================== --- contrib/wpa/src/wps/wps_upnp_ssdp.c (revision 289259) +++ contrib/wpa/src/wps/wps_upnp_ssdp.c (working copy) @@ -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,12 +740,10 @@ int ssdp_listener_open(void) int sd; sd = socket(AF_INET, SOCK_DGRAM, 0); - if (sd < 0) + if (sd < 0 || + fcntl(sd, F_SETFL, O_NONBLOCK) != 0 || + setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) goto fail; - if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0) - goto fail; - if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) - goto fail; os_memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_ANY); @@ -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; Index: contrib/wpa/src/wps/wps_upnp_web.c =================================================================== --- contrib/wpa/src/wps/wps_upnp_web.c (revision 289259) +++ contrib/wpa/src/wps/wps_upnp_web.c (working copy) @@ -1003,6 +1003,8 @@ static void web_connection_parse_subscribe(struct 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(struc } 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; } Index: contrib/wpa/src/wps/wps_validate.c =================================================================== --- contrib/wpa/src/wps/wps_validate.c (revision 289259) +++ contrib/wpa/src/wps/wps_validate.c (working copy) @@ -224,6 +224,8 @@ static int wps_validate_rf_bands(const u8 *rf_band 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); Index: contrib/wpa/wpa_supplicant/ChangeLog =================================================================== --- contrib/wpa/wpa_supplicant/ChangeLog (revision 289259) +++ contrib/wpa/wpa_supplicant/ChangeLog (working copy) @@ -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 " 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) Index: contrib/wpa/wpa_supplicant/ap.c =================================================================== --- contrib/wpa/wpa_supplicant/ap.c (revision 289259) +++ contrib/wpa/wpa_supplicant/ap.c (working copy) @@ -142,6 +142,29 @@ void wpa_supplicant_conf_ap_ht(struct wpa_supplica } } } + + 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 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 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_ 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 * #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_su 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); +} Index: contrib/wpa/wpa_supplicant/ap.h =================================================================== --- contrib/wpa/wpa_supplicant/ap.h (revision 289259) +++ contrib/wpa/wpa_supplicant/ap.h (working copy) @@ -93,4 +93,6 @@ void wpas_event_dfs_cac_aborted(struct wpa_supplic 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 */ Index: contrib/wpa/wpa_supplicant/bss.c =================================================================== --- contrib/wpa/wpa_supplicant/bss.c (revision 289259) +++ contrib/wpa/wpa_supplicant/bss.c (working copy) @@ -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 *wp 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,8 +393,9 @@ static struct wpa_bss * wpa_bss_add(struct wpa_sup 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; } @@ -398,7 +402,7 @@ static struct wpa_bss * wpa_bss_add(struct wpa_sup 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,12 +412,12 @@ 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: @@ -420,7 +424,7 @@ static int are_ies_equal(const struct wpa_bss *old 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_bs 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, struc 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 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 * (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 *w } -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); } Index: contrib/wpa/wpa_supplicant/bss.h =================================================================== --- contrib/wpa/wpa_supplicant/bss.h (revision 289259) +++ contrib/wpa/wpa_supplicant/bss.h (working copy) @@ -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) */ Index: contrib/wpa/wpa_supplicant/config.c =================================================================== --- contrib/wpa/wpa_supplicant/config.c (revision 289259) +++ contrib/wpa/wpa_supplicant/config.c (working copy) @@ -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 par 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 } +#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 pa 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 } +#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 stru return buf; } +#endif /* NO_CONFIG_WRITE */ #endif /* IEEE8021X_EAPOL */ @@ -1810,12 +1822,13 @@ static char * wpa_config_write_mesh_basic_rates(co * 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, c */ 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, con 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 c 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, st } +#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, st #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_field { 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_field { 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_field #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_field { 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_field { 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_field { 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 Index: contrib/wpa/wpa_supplicant/config.h =================================================================== --- contrib/wpa/wpa_supplicant/config.h (revision 289259) +++ contrib/wpa/wpa_supplicant/config.h (working copy) @@ -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; }; Index: contrib/wpa/wpa_supplicant/config_file.c =================================================================== --- contrib/wpa/wpa_supplicant/config_file.c (revision 289259) +++ contrib/wpa/wpa_supplicant/config_file.c (working copy) @@ -501,7 +501,12 @@ static void write_bssid(FILE *f, struct wpa_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, stru 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, struc 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, struc #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, struc 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,7 +1155,7 @@ static void wpa_config_write_global(FILE *f, struc 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"); @@ -1151,7 +1162,7 @@ static void wpa_config_write_global(FILE *f, struc #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, struc 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, struc 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, struc 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,6 +1285,11 @@ static void wpa_config_write_global(FILE *f, struc 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); @@ -1280,6 +1296,9 @@ static void wpa_config_write_global(FILE *f, struc 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_ } #endif /* CONFIG_NO_CONFIG_BLOBS */ + os_fdatasync(f); + fclose(f); if (tmp_name) { Index: contrib/wpa/wpa_supplicant/config_ssid.h =================================================================== --- contrib/wpa/wpa_supplicant/config_ssid.h (revision 289259) +++ contrib/wpa/wpa_supplicant/config_ssid.h (working copy) @@ -13,9 +13,7 @@ #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 | \ EAPOL_FLAG_REQUIRE_KEY_BROADCAST) @@ -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; Index: contrib/wpa/wpa_supplicant/ctrl_iface.c =================================================================== --- contrib/wpa/wpa_supplicant/ctrl_iface.c (revision 289259) +++ contrib/wpa/wpa_supplicant/ctrl_iface.c (working copy) @@ -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 } 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 *wp } +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 wp 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_c 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 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(str } -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(str } } - 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, cha } #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_networ } } + 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_networ 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_networ #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_networ 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,12 +3039,12 @@ 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; } @@ -3017,7 +3051,7 @@ static int wpa_supplicant_ctrl_iface_get_network( 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,9 +3089,11 @@ 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) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find " @@ -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_capabilit } #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 *w 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 *w 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 *w 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 *w 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,18 +4609,30 @@ static int p2p_ctrl_find(struct wpa_supplicant *wp } 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; @@ -4566,19 +4639,53 @@ static int p2p_ctrl_find(struct wpa_supplicant *wp 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_provi 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_provi 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_suppl 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_sup 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_sup 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_sup 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_sup { 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 * 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 wp } 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_suppl 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 * 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,10 +6620,65 @@ static int wpa_supplicant_signal_poll(struct wpa_s 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: "" */ + 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; +} + + static int wpa_supplicant_pktcnt_poll(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { @@ -6602,6 +6801,7 @@ static void wpa_supplicant_ctrl_iface_flush(struct 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 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 * 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 * 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,8 +7180,62 @@ static void wpas_ctrl_scan(struct wpa_supplicant * *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) scan_res_handler = scan_only_handler; else if (wpa_s->scan_res_handler == scan_only_handler) @@ -7030,6 +7297,7 @@ static void wpas_ctrl_scan(struct wpa_supplicant * done: os_free(manual_scan_freqs); + os_free(ssid); } @@ -7231,7 +7499,7 @@ void wpas_data_test_rx(void *ctx, const u8 *src_ad { 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_ad 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 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 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 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_sup #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 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 wp } +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 wp 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 wp 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 wp 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 wp 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 wp 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 wp 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 wp } 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 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]; /* * TABTABTABTAB - * TAB + * TAB[TAB] */ wpa_printf(MSG_DEBUG, "CTRL_IFACE GLOBAL INTERFACE_ADD '%s'", cmd); @@ -8543,12 +8876,54 @@ static int wpa_supplicant_global_iface_add(struct 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; - return wpa_supplicant_add_iface(global, &iface, NULL) ? 0 : -1; + wpa_s = wpa_supplicant_add_iface(global, &iface, NULL); + if (!wpa_s) + goto fail; + wpa_s->added_vif = create_iface; + return 0; + +fail: + if (create_iface) + wpa_drv_if_remove(global->ifaces, WPA_IF_STATION, iface.ifname); + return -1; } @@ -8556,6 +8931,8 @@ static int wpa_supplicant_global_iface_remove(stru char *cmd) { struct wpa_supplicant *wpa_s; + int ret; + unsigned int delete_iface; wpa_printf(MSG_DEBUG, "CTRL_IFACE GLOBAL INTERFACE_REMOVE '%s'", cmd); @@ -8562,7 +8939,14 @@ static int wpa_supplicant_global_iface_remove(stru 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 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_g } +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: " + * */ + + 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 wp } +#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(st } 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(st #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)) Index: contrib/wpa/wpa_supplicant/ctrl_iface_named_pipe.c =================================================================== --- contrib/wpa/wpa_supplicant/ctrl_iface_named_pipe.c (revision 289259) +++ contrib/wpa/wpa_supplicant/ctrl_iface_named_pipe.c (working copy) @@ -423,7 +423,8 @@ static int ctrl_iface_parse(struct ctrl_iface_priv } -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; Index: contrib/wpa/wpa_supplicant/ctrl_iface_udp.c =================================================================== --- contrib/wpa/wpa_supplicant/ctrl_iface_udp.c (revision 289259) +++ contrib/wpa/wpa_supplicant/ctrl_iface_udp.c (working copy) @@ -322,7 +322,8 @@ static void wpa_supplicant_ctrl_iface_receive(int } -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; Index: contrib/wpa/wpa_supplicant/ctrl_iface_unix.c =================================================================== --- contrib/wpa/wpa_supplicant/ctrl_iface_unix.c (revision 289259) +++ contrib/wpa/wpa_supplicant/ctrl_iface_unix.c (working copy) @@ -13,6 +13,10 @@ #include #include #include +#ifdef __linux__ +#include +#include +#endif /* __linux__ */ #ifdef ANDROID #include #endif /* ANDROID */ @@ -72,6 +76,32 @@ static int wpas_ctrl_iface_global_reinit(struct wp 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 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 } 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(struc } -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 char * wpa_supplicant_ctrl_iface_path(struc 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_supplica 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 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_recei 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_recei } 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", Index: contrib/wpa/wpa_supplicant/dbus/dbus_new.c =================================================================== --- contrib/wpa/wpa_supplicant/dbus/dbus_new.c (revision 289259) +++ contrib/wpa/wpa_supplicant/dbus/dbus_new.c (working copy) @@ -137,7 +137,7 @@ static void wpas_dbus_signal_interface(struct wpa_ 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_supplic 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_suppli 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_suppl 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_su 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_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(stru 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(stru #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 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 /** * 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_su 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_su 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_su /** * 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_sup 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_sup /** * 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_supplica 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_sup 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_suppli 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_suppli 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 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(stru 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_su 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_su 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 { 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 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 /** - * - * 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_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 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_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 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_su 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_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(stru 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_remo /** * 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_su 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_su 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_ 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_supplica #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 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 * 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 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 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 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 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 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 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 { "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 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 }; +/** + * 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_db 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_suppl 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_suppl /** * 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 /** * 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_ /** * 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 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_supplican 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_supplican } +/** + * 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 w 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 /* 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 w 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, Index: contrib/wpa/wpa_supplicant/dbus/dbus_new.h =================================================================== --- contrib/wpa/wpa_supplicant/dbus/dbus_new.h (revision 289259) +++ contrib/wpa/wpa_supplicant/dbus/dbus_new.h (working copy) @@ -152,6 +152,7 @@ void wpas_dbus_signal_wps_event_m2d(struct wpa_sup 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(stru 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(stru 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_su 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_succ { } +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 wp { } -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_supp } 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_suppli } 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_ { } +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 */ Index: contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.c =================================================================== --- contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.c (revision 289259) +++ contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.c (working copy) @@ -157,7 +157,8 @@ static struct wpa_supplicant * get_iface_by_dbus_p 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_supp } 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(D 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(DBus 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(DBusMessag 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(DBusMessag 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 *m 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, "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 { + 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); } - } else { - wpa_s->scan_req = MANUAL_SCAN_REQ; - wpa_supplicant_req_scan(wpa_s, 0, 0); + + 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 * #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(DBusMe 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(DBusMessa /** + * 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(DBu 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(DBu 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(DBus 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_ 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(DBusMessa 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(DBusM 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 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(DBusMessageI 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(DBusMessageIt 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(DBusMessageIt #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(DBusMessageIt } #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 * 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) Index: contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.h =================================================================== --- contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.h (revision 289259) +++ contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.h (working copy) @@ -107,6 +107,9 @@ DBusMessage * wpas_dbus_handler_reassociate(DBusMe 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(DB 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); Index: contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.c =================================================================== --- contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.c (revision 289259) +++ contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.c (working copy) @@ -127,8 +127,7 @@ DBusMessage * wpas_dbus_handler_p2p_find(DBusMessa 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(DBu 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(DBusMes 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_reque 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(DBus 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(DBus 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(DBus 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(stru } +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(DBusMess 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(DBusMe 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(DBusMes !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(DBusMes 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( 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(DBu 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(DBu 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(DBusMessage 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(DBusMessage 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,6 +1249,9 @@ dbus_bool_t wpas_dbus_getter_p2p_peergo(DBusMessag 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 @@ -1190,7 +1258,8 @@ dbus_bool_t wpas_dbus_getter_p2p_peergo(DBusMessag 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( } +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(DBusM 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(DBu 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(DBu /* 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_prop /** - * 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_gro 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_gro /* 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_ 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_ 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_persist 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(DBu 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(DBu 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++; } Index: contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.h =================================================================== --- contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.h (revision 289259) +++ contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.h (working copy) @@ -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( 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); Index: contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_wps.c =================================================================== --- contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_wps.c (revision 289259) +++ contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_wps.c (working copy) @@ -53,7 +53,7 @@ static int wpas_dbus_handler_wps_role(DBusMessage 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 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(DBusMess /** + * 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(D 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; Index: contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.c =================================================================== --- contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.c (revision 289259) +++ contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.c (working copy) @@ -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; Index: contrib/wpa/wpa_supplicant/dbus/dbus_new_introspect.c =================================================================== --- contrib/wpa/wpa_supplicant/dbus/dbus_new_introspect.c (revision 289259) +++ contrib/wpa/wpa_supplicant/dbus/dbus_new_introspect.c (working copy) @@ -257,7 +257,7 @@ DBusMessage * wpa_dbus_introspect(DBusMessage *mes DBusMessage *reply; struct wpabuf *xml; - xml = wpabuf_alloc(10000); + xml = wpabuf_alloc(15000); if (xml == NULL) return NULL; Index: contrib/wpa/wpa_supplicant/dbus/dbus_old.c =================================================================== --- contrib/wpa/wpa_supplicant/dbus/dbus_old.c (revision 289259) +++ contrib/wpa/wpa_supplicant/dbus/dbus_old.c (working copy) @@ -383,7 +383,7 @@ void wpa_supplicant_dbus_notify_scan_results(struc 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 wp 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 wp 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(stru 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_b 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; Index: contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers.c =================================================================== --- contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers.c (revision 289259) +++ contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers.c (working copy) @@ -166,7 +166,7 @@ DBusMessage * wpas_dbus_global_add_interface(DBusM 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(DBusM } 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(DBusMes 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(DBusMes /* 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(DBusMess 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(DBusM } /* 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(DBusMess 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(DBusM 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; Index: contrib/wpa/wpa_supplicant/defconfig =================================================================== --- contrib/wpa/wpa_supplicant/defconfig (revision 289259) +++ contrib/wpa/wpa_supplicant/defconfig (working copy) @@ -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 Index: contrib/wpa/wpa_supplicant/driver_i.h =================================================================== --- contrib/wpa/wpa_supplicant/driver_i.h (revision 289259) +++ contrib/wpa/wpa_supplicant/driver_i.h (working copy) @@ -286,11 +286,13 @@ static inline int wpa_drv_set_country(struct 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 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(stru } #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 */ Index: contrib/wpa/wpa_supplicant/eapol_test.c =================================================================== --- contrib/wpa/wpa_supplicant/eapol_test.c (revision 289259) +++ contrib/wpa/wpa_supplicant/eapol_test.c (working copy) @@ -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 * 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, en { 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, cons } +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, 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_dat { 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 e break; case EAP_CODE_FAILURE: os_strlcpy(buf, "EAP Failure", sizeof(buf)); + if (e->ctrl_iface) + break; eloop_terminate(); break; default: @@ -901,7 +944,8 @@ ieee802_1x_receive_auth(struct radius_msg *msg, st 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; @@ -908,18 +952,58 @@ ieee802_1x_receive_auth(struct radius_msg *msg, st } +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 * *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] [-o] [-R] " "[-P] \\\n" - " [-A]\n" + " [-A] [-i] [-T]\n" "eapol_test scard\n" "eapol_test sim [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(); Index: contrib/wpa/wpa_supplicant/eapol_test.py =================================================================== --- contrib/wpa/wpa_supplicant/eapol_test.py (revision 0) +++ contrib/wpa/wpa_supplicant/eapol_test.py (working copy) @@ -0,0 +1,142 @@ +#!/usr/bin/env python2 +# +# eapol_test controller +# Copyright (c) 2015, Jouni Malinen +# +# 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() Property changes on: contrib/wpa/wpa_supplicant/eapol_test.py ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: contrib/wpa/wpa_supplicant/events.c =================================================================== --- contrib/wpa/wpa_supplicant/events.c (revision 289259) +++ contrib/wpa/wpa_supplicant/events.c (working copy) @@ -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_supplican } +/** + * 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(stru 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_suppl 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 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 */ #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,6 +1096,7 @@ struct wpa_bss * wpa_supplicant_pick_network(struc 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) @@ -1017,8 +1103,6 @@ struct wpa_bss * wpa_supplicant_pick_network(struc 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(struc 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 * 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 * #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_ 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,7 +1530,18 @@ static int wpas_select_network_from_last_scan(stru { 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(stru 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_suppl #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 w 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_ { 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_ } #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_ 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_ 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(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_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_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_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_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(struc __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_even 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_even 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_even 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_even 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_even } 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; Index: contrib/wpa/wpa_supplicant/hs20_supplicant.c =================================================================== --- contrib/wpa/wpa_supplicant/hs20_supplicant.c (revision 289259) +++ contrib/wpa/wpa_supplicant/hs20_supplicant.c (working copy) @@ -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,15 +188,17 @@ int hs20_anqp_send_req(struct wpa_supplicant *wpa_ 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 (!bss) { + wpa_printf(MSG_WARNING, + "ANQP: Cannot send query to unknown BSS " + MACSTR, MAC2STR(dst)); + return -1; } - if (freq <= 0) - 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 *wp 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; Index: contrib/wpa/wpa_supplicant/ibss_rsn.c =================================================================== --- contrib/wpa/wpa_supplicant/ibss_rsn.c (revision 289259) +++ contrib/wpa/wpa_supplicant/ibss_rsn.c (working copy) @@ -571,6 +571,9 @@ int ibss_rsn_start(struct ibss_rsn *ibss_rsn, cons 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); } Index: contrib/wpa/wpa_supplicant/interworking.c =================================================================== --- contrib/wpa/wpa_supplicant/interworking.c (revision 289259) +++ contrib/wpa/wpa_supplicant/interworking.c (working copy) @@ -2058,7 +2058,7 @@ static struct wpa_cred * interworking_credentials_ 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,15 +2673,17 @@ int anqp_send_req(struct wpa_supplicant *wpa_s, co 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 (!bss) { + wpa_printf(MSG_WARNING, + "ANQP: Cannot send query to unknown BSS " + MACSTR, MAC2STR(dst)); + return -1; } - if (freq <= 0) - 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)", MAC2STR(dst), (unsigned int) num_ids); Index: contrib/wpa/wpa_supplicant/main.c =================================================================== --- contrib/wpa/wpa_supplicant/main.c (revision 289259) +++ contrib/wpa/wpa_supplicant/main.c (working copy) @@ -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); Index: contrib/wpa/wpa_supplicant/mesh.c =================================================================== --- contrib/wpa/wpa_supplicant/mesh.c (revision 289259) +++ contrib/wpa/wpa_supplicant/mesh.c (working copy) @@ -47,8 +47,8 @@ void wpa_supplicant_mesh_iface_deinit(struct 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_sup 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 } 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,7 +455,7 @@ static int mesh_attr_text(const u8 *ies, size_t ie 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++) { @@ -460,15 +462,16 @@ static int mesh_attr_text(const u8 *ies, size_t ie 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; Index: contrib/wpa/wpa_supplicant/mesh_mpm.c =================================================================== --- contrib/wpa/wpa_supplicant/mesh_mpm.c (revision 289259) +++ contrib/wpa/wpa_supplicant/mesh_mpm.c (working copy) @@ -239,6 +239,9 @@ static void mesh_mpm_send_plink_action(struct wpa_ 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_ 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 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 */ Index: contrib/wpa/wpa_supplicant/mesh_rsn.c =================================================================== --- contrib/wpa/wpa_supplicant/mesh_rsn.c (revision 289259) +++ contrib/wpa/wpa_supplicant/mesh_rsn.c (working copy) @@ -190,7 +190,8 @@ static int __mesh_rsn_auth_init(struct mesh_rsn *r 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,6 +210,7 @@ struct mesh_rsn *mesh_rsn_auth_init(struct wpa_sup if (__mesh_rsn_auth_init(mesh_rsn, wpa_s->own_addr) < 0) { mesh_rsn_deinit(mesh_rsn); + os_free(mesh_rsn); return NULL; } @@ -215,8 +217,8 @@ struct mesh_rsn *mesh_rsn_auth_init(struct wpa_sup 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); Index: contrib/wpa/wpa_supplicant/notify.c =================================================================== --- contrib/wpa/wpa_supplicant/notify.c (revision 289259) +++ contrib/wpa/wpa_supplicant/notify.c (working copy) @@ -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_supplica /* 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,7 +279,17 @@ void wpas_notify_wps_event_success(struct wpa_supp #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 w 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_supp 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,6 +657,14 @@ void wpas_notify_p2p_group_started(struct wpa_supp } +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) { @@ -637,6 +671,16 @@ void wpas_notify_p2p_wps_failed(struct wpa_supplic 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_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 */ Index: contrib/wpa/wpa_supplicant/notify.h =================================================================== --- contrib/wpa/wpa_supplicant/notify.h (revision 289259) +++ contrib/wpa/wpa_supplicant/notify.h (working copy) @@ -45,6 +45,7 @@ void wpas_notify_wps_event_m2d(struct wpa_supplica 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_supp 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 wp 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 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 */ Index: contrib/wpa/wpa_supplicant/p2p_supplicant.c =================================================================== --- contrib/wpa/wpa_supplicant/p2p_supplicant.c (revision 289259) +++ contrib/wpa/wpa_supplicant/p2p_supplicant.c (working copy) @@ -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 *elo 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(struc { 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 inco */ 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_suppli 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_suppli else wpa_drv_deinit_p2p_cli(wpa_s); + os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN); + return 0; } @@ -1109,6 +1143,7 @@ static void wpas_p2p_add_persistent_group_client(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 || @@ -1115,7 +1150,7 @@ static void wpas_p2p_add_persistent_group_client(s !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(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_supp 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 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 *dat 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_supplican 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_suppl 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_suppl 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 { 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, struc 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, struc 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, struc 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, struc } -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,1239 +2438,27 @@ 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); -} + /* + * 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); -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); + wpas_p2p_listen_work_done(wpa_s); } -/* - * 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) +static int wpas_send_probe_resp(void *ctx, const struct wpabuf *buf, + unsigned int freq) { - 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); + return wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1, + freq); } -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); - } - } -} - - -static 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 ", - 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; -} - - static void wpas_prov_disc_local_display(struct wpa_supplicant *wpa_s, const u8 *peer, const char *params, unsigned int generated_pin) @@ -3776,15 +2626,65 @@ static void wpas_prov_disc_fail(void *ctx, const u } -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; +} + + /** * Pick the best frequency to use from all the currently used frequencies. */ @@ -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, co 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, co " 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, co "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 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 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 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 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 @@ struct p2p_oper_class_map { #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_supp 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 * 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,13 +3489,8 @@ struct wpa_supplicant * wpas_get_p2p_client_iface( { 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) + if (ssid && (ssid->mode != WPAS_MODE_INFRA || !ssid->p2p_group)) continue; - if (ssid->mode != WPAS_MODE_INFRA) - continue; - if (wpa_s->wpa_state != WPA_COMPLETED && - wpa_s->wpa_state != WPA_GROUP_HANDSHAKE) - continue; if (os_memcmp(wpa_s->go_dev_addr, peer_dev_addr, ETH_ALEN) == 0) return wpa_s; } @@ -4667,7 +3570,6 @@ int wpas_p2p_add_p2pdev_interface(struct wpa_suppl 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) { @@ -4674,7 +3576,6 @@ int wpas_p2p_add_p2pdev_interface(struct wpa_suppl 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 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, co 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 *intende 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,6 +3715,34 @@ static int wpas_remove_stale_groups(void *ctx, con } +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, @@ -4799,7 +3749,8 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 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]; @@ -4806,6 +3757,7 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 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 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,11 +3780,11 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 " 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 @@ -4836,11 +3791,11 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 " 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,10 +3817,10 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 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; } @@ -4872,6 +3827,14 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 /* 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=%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 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; + os_memcpy(go_ifname, go_wpa_s->ifname, + sizeof(go_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); + 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=%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,11 +3994,11 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 " 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 @@ -5025,11 +4005,11 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 " 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, struc 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, struc */ 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_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_supp 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_supp 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_supp case WPS_PBC: method = WPS_CONFIG_PUSHBUTTON; break; + case WPS_P2PS: + method = WPS_CONFIG_P2PS; + break; default: method = 0; break; @@ -5896,12 +4909,17 @@ static int wpas_p2p_join_start(struct wpa_supplica 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)); if (!freqs) @@ -5965,6 +4983,47 @@ static int wpas_p2p_setup_freqs(struct wpa_supplic 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, 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, 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, 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, } 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_supp { 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_supp } -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_go_freq(struct wpa_supp 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_go_freq(struct wpa_supp 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_go_freq(struct wpa_supp /* 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_go_freq(struct wpa_supp 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,7 +5524,7 @@ static int wpas_p2p_init_go_params(struct wpa_supp 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)); @@ -6418,122 +5531,179 @@ static int wpas_p2p_init_go_params(struct wpa_supp 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; } - wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on forced " - "frequency %d MHz", freq); + + 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: 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,7 +5806,7 @@ int wpas_p2p_group_add(struct wpa_supplicant *wpa_ 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; @@ -6643,6 +5813,8 @@ static int wpas_start_p2p_client(struct wpa_suppli 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_suppli 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_suppl 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_suppl 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_suppl } 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 (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); - if (ssid->mode == WPAS_MODE_INFRA) - return wpas_start_p2p_client(wpa_s, ssid, addr_allocated, freq); + 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 *w } 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 *w 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 *w 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 *w 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, 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, } 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 *w 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 *w 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_supplica "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 } -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_pbc_overlap_cb(void *eloop_ctx, void 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_ */ 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_ 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 { 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(struc 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,24 +8471,87 @@ static void wpas_p2p_optimize_listen_channel(struc } -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; @@ -9275,11 +8558,158 @@ static void wpas_p2p_optimize_listen_channel(struc 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) { Index: contrib/wpa/wpa_supplicant/p2p_supplicant.h =================================================================== --- contrib/wpa/wpa_supplicant/p2p_supplicant.h (revision 289259) +++ contrib/wpa/wpa_supplicant/p2p_supplicant.h (working copy) @@ -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_suppl 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 *w 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_supplican 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 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 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 { } -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) { } Index: contrib/wpa/wpa_supplicant/p2p_supplicant_sd.c =================================================================== --- contrib/wpa/wpa_supplicant/p2p_supplicant_sd.c (revision 0) +++ contrib/wpa/wpa_supplicant/p2p_supplicant_sd.c (working copy) @@ -0,0 +1,1273 @@ +/* + * wpa_supplicant - P2P service discovery + * Copyright (c) 2009-2010, Atheros Communications + * Copyright (c) 2010-2014, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "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 ", + 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; +} Property changes on: contrib/wpa/wpa_supplicant/p2p_supplicant_sd.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: contrib/wpa/wpa_supplicant/preauth_test.c =================================================================== --- contrib/wpa/wpa_supplicant/preauth_test.c (revision 289259) +++ contrib/wpa/wpa_supplicant/preauth_test.c (working copy) @@ -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 { Index: contrib/wpa/wpa_supplicant/scan.c =================================================================== --- contrib/wpa/wpa_supplicant/scan.c (revision 289259) +++ contrib/wpa/wpa_supplicant/scan.c (working copy) @@ -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 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(st 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_supplica } +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, v 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, v } 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, v wpa_dbg(wpa_s, MSG_DEBUG, "Starting AP scan for wildcard " "SSID"); } -#ifdef CONFIG_P2P + ssid_list_set: -#endif /* CONFIG_P2P */ - 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, c 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). */ Index: contrib/wpa/wpa_supplicant/sme.c =================================================================== --- contrib/wpa/wpa_supplicant/sme.c (revision 289259) +++ contrib/wpa/wpa_supplicant/sme.c (working copy) @@ -67,7 +67,7 @@ static int sme_set_sae_group(struct wpa_supplicant 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_sup } #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_wor 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 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 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,9 +817,23 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, #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 || wpa_s->sme.auth_alg == data->auth.auth_type || @@ -831,12 +869,20 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, #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 */ Index: contrib/wpa/wpa_supplicant/wpa_cli.c =================================================================== --- contrib/wpa/wpa_supplicant/wpa_cli.c (revision 289259) +++ contrib/wpa/wpa_supplicant/wpa_cli.c (working copy) @@ -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 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_en 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,6 +101,7 @@ static void wpa_cli_mon_receive(int sock, void *el 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) @@ -105,7 +108,9 @@ static void usage(void) { printf("wpa_cli [-p] [-i] [-hvB] " "[-a] \\\n" - " [-P] [-g] [-G] " + " [-P] [-g] [-G] " + "\\\n" + " [-s] " "[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 * #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 *t 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_addr(struct dl_list *t 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 *ifn } #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,7 +353,7 @@ static int wpa_cli_open_connection(const char *ifn } } - ctrl_conn = wpa_ctrl_open(cfile); + ctrl_conn = wpa_ctrl_open2(cfile, client_socket_dir); if (ctrl_conn == NULL) { os_free(cfile); return -1; @@ -346,7 +360,7 @@ static int wpa_cli_open_connection(const char *ifn } 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 *ctr 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 *st "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, } +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 *ct 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_ct 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,7 +1470,10 @@ static int wpa_cli_cmd_disable_network(struct wpa_ 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; } @@ -1368,7 +1480,10 @@ static int wpa_cli_cmd_add_network(struct wpa_ctrl 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 } +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 } +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_ct 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 TABTABTABTAB - * TAB + * TAB[TAB] */ 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_ct } +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_ct } +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 @@ struct wpa_cli_cmd { { "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, " = get information" }, { "logon", wpa_cli_cmd_logon, NULL, @@ -2686,29 +2939,33 @@ struct wpa_cli_cmd { { "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, " = 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, " = 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, " = 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, " = 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, " = 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, " = 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, " = duplicate network variables" }, @@ -2750,7 +3007,7 @@ struct wpa_cli_cmd { { "get_capability", wpa_cli_cmd_get_capability, NULL, cli_cmd_flag_none, " " - "= 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 @@ struct wpa_cli_cmd { { "tdls_teardown", wpa_cli_cmd_tdls_teardown, NULL, cli_cmd_flag_none, " = tear down TDLS with " }, + { "tdls_link_status", wpa_cli_cmd_tdls_link_status, NULL, + cli_cmd_flag_none, + " = TDLS link status with " }, { "wmm_ac_addts", wpa_cli_cmd_wmm_ac_addts, NULL, cli_cmd_flag_none, " [nominal_msdu_size=#] " @@ -3117,6 +3377,9 @@ struct wpa_cli_cmd { " 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, + " = retrieve preferred freq list for the specified interface type" }, { NULL, NULL, NULL, cli_cmd_flag_none, NULL } }; @@ -3124,7 +3387,7 @@ struct wpa_cli_cmd { /* * 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 *ct 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 * } 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,6 +4127,34 @@ 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 @@ -3829,39 +4161,15 @@ static void wpa_cli_action(struct wpa_ctrl *ctrl) /* 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; Index: contrib/wpa/wpa_supplicant/wpa_priv.c =================================================================== --- contrib/wpa/wpa_supplicant/wpa_priv.c (revision 289259) +++ contrib/wpa/wpa_supplicant/wpa_priv.c (working copy) @@ -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_ 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_ 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_pri 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 w } +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 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_ 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_ 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_pr } 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 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_int } +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_even &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_even 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] " - "[driver:ifname ...]\n"); + " wpa_priv [-Bdd] [-c] [-P] " + " \\\n" + " [driver:ifname ...]\n"); } @@ -982,13 +1103,13 @@ 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); @@ -995,7 +1116,7 @@ int main(int argc, char *argv[]) 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(); Index: contrib/wpa/wpa_supplicant/wpa_supplicant.c =================================================================== --- contrib/wpa/wpa_supplicant/wpa_supplicant.c (revision 289259) +++ contrib/wpa/wpa_supplicant/wpa_supplicant.c (working copy) @@ -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 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,9 +71,9 @@ #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" @@ -79,7 +80,7 @@ "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 @@ " 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 @@ "\"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_supp 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_supp 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_supplican 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_supplican 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 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_supplican } 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_supplican 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_supplican "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_supplican 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_supplican 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_w 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_w 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_w } } +#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_w } 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,7 +2574,10 @@ void wpa_supplicant_enable_network(struct wpa_supp } 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"); @@ -2512,8 +2584,10 @@ void wpa_supplicant_enable_network(struct wpa_supp 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_supp 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_supp 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_supp 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_supplica 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_glo 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 * (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_supplica } } + 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_su 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 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 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(s #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(v #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 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 } #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 *glob 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 *glob 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(stru 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 *w } 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_supplic 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_supplic 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; } Index: contrib/wpa/wpa_supplicant/wpa_supplicant.conf =================================================================== --- contrib/wpa/wpa_supplicant/wpa_supplicant.conf (revision 289259) +++ contrib/wpa/wpa_supplicant/wpa_supplicant.conf (working copy) @@ -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: [:] #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 Index: contrib/wpa/wpa_supplicant/wpa_supplicant_i.h =================================================================== --- contrib/wpa/wpa_supplicant/wpa_supplicant_i.h (revision 289259) +++ contrib/wpa/wpa_supplicant/wpa_supplicant_i.h (working copy) @@ -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,18 +66,7 @@ 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 * * If a configuration file is not used, this variable can be used to @@ -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(struc 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_supplic 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 */ Index: contrib/wpa/wpa_supplicant/wpas_glue.c =================================================================== --- contrib/wpa/wpa_supplicant/wpas_glue.c (revision 289259) +++ contrib/wpa/wpa_supplicant/wpas_glue.c (working copy) @@ -737,6 +737,8 @@ enum wpa_ctrl_req_type wpa_supplicant_ctrl_req_fro 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(enu 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(enu 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 * 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 * 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 *c { 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 Index: contrib/wpa/wpa_supplicant/wpas_glue.h =================================================================== --- contrib/wpa/wpa_supplicant/wpas_glue.h (revision 289259) +++ contrib/wpa/wpa_supplicant/wpas_glue.h (working copy) @@ -22,4 +22,7 @@ const char * wpa_supplicant_ctrl_req_to_string(enu 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 */ Index: contrib/wpa/wpa_supplicant/wps_supplicant.c =================================================================== --- contrib/wpa/wpa_supplicant/wps_supplicant.c (revision 289259) +++ contrib/wpa/wpa_supplicant/wps_supplicant.c (working copy) @@ -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 * 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_suppli } #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_suppli 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 * 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_supplican 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_supplican sel_uuid = NULL; } - dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { - struct wpabuf *ie; - if (bss == selected) + 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; - 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); - 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_ssi 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 (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 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 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 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 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); } Index: contrib/wpa/wpa_supplicant/wps_supplicant.h =================================================================== --- contrib/wpa/wpa_supplicant/wps_supplicant.h (revision 289259) +++ contrib/wpa/wpa_supplicant/wps_supplicant.h (working copy) @@ -33,6 +33,7 @@ int wpas_wps_start_pbc(struct wpa_supplicant *wpa_ 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); Index: contrib/wpa =================================================================== --- contrib/wpa (revision 289259) +++ contrib/wpa (working copy) Property changes on: contrib/wpa ___________________________________________________________________ Modified: svn:mergeinfo Merged /vendor/wpa/dist:r281683-289285