diff options
217 files changed, 13365 insertions, 3036 deletions
diff --git a/hostapd/Android.mk b/hostapd/Android.mk index 107f381c..7f1e6e64 100644 --- a/hostapd/Android.mk +++ b/hostapd/Android.mk @@ -100,6 +100,7 @@ NEED_RC4=y NEED_AES=y NEED_MD5=y NEED_SHA1=y +NEED_SHA256=y OBJS += src/drivers/drivers.c L_CFLAGS += -DHOSTAPD @@ -145,6 +146,7 @@ CONFIG_NO_ACCOUNTING=y else OBJS += src/radius/radius.c OBJS += src/radius/radius_client.c +OBJS += src/radius/radius_das.c endif ifdef CONFIG_NO_ACCOUNTING @@ -157,6 +159,12 @@ ifdef CONFIG_NO_VLAN L_CFLAGS += -DCONFIG_NO_VLAN else OBJS += src/ap/vlan_init.c +ifdef CONFIG_VLAN_NETLINK +ifdef CONFIG_FULL_DYNAMIC_VLAN +OBJS += src/ap/vlan_util.c +endif +L_CFLAGS += -DCONFIG_VLAN_NETLINK +endif endif ifdef CONFIG_NO_CTRL_IFACE @@ -199,10 +207,23 @@ NEED_AES_OMAC1=y NEED_AES_UNWRAP=y endif +ifdef CONFIG_IEEE80211V +L_CFLAGS += -DCONFIG_IEEE80211V +OBJS += src/ap/wnm_ap.c +endif + ifdef CONFIG_IEEE80211N L_CFLAGS += -DCONFIG_IEEE80211N endif +ifdef CONFIG_WNM +L_CFLAGS += -DCONFIG_WNM +endif + +ifdef CONFIG_IEEE80211AC +L_CFLAGS += -DCONFIG_IEEE80211AC +endif + include $(LOCAL_PATH)/src/drivers/drivers.mk OBJS += $(DRV_AP_OBJS) @@ -239,6 +260,14 @@ OBJS += src/eap_server/eap_server_tls.c TLS_FUNCS=y endif +ifdef CONFIG_EAP_UNAUTH_TLS +L_CFLAGS += -DEAP_SERVER_UNAUTH_TLS +ifndef CONFIG_EAP_TLS +OBJS += src/eap_server/eap_server_tls.c +TLS_FUNCS=y +endif +endif + ifdef CONFIG_EAP_PEAP L_CFLAGS += -DEAP_SERVER_PEAP OBJS += src/eap_server/eap_server_peap.c @@ -472,6 +501,15 @@ ifndef CONFIG_TLS CONFIG_TLS=openssl endif +ifdef CONFIG_TLSV11 +L_CFLAGS += -DCONFIG_TLSV11 +endif + +ifdef CONFIG_TLSV12 +L_CFLAGS += -DCONFIG_TLSV12 +NEED_SHA256=y +endif + ifeq ($(CONFIG_TLS), openssl) ifdef TLS_FUNCS OBJS += src/crypto/tls_openssl.c @@ -490,10 +528,6 @@ ifeq ($(CONFIG_TLS), gnutls) ifdef TLS_FUNCS OBJS += src/crypto/tls_gnutls.c LIBS += -lgnutls -lgpg-error -ifdef CONFIG_GNUTLS_EXTRA -L_CFLAGS += -DCONFIG_GNUTLS_EXTRA -LIBS += -lgnutls-extra -endif endif OBJS += src/crypto/crypto_gnutls.c HOBJS += src/crypto/crypto_gnutls.c @@ -555,6 +589,9 @@ OBJS += src/tls/pkcs8.c NEED_SHA256=y NEED_BASE64=y NEED_TLS_PRF=y +ifdef CONFIG_TLSV12 +NEED_TLS_PRF_SHA256=y +endif NEED_MODEXP=y NEED_CIPHER=y L_CFLAGS += -DCONFIG_TLS_INTERNAL @@ -669,14 +706,19 @@ endif SHA1OBJS = ifdef NEED_SHA1 +ifneq ($(CONFIG_TLS), openssl) SHA1OBJS += src/crypto/sha1.c +endif +SHA1OBJS += src/crypto/sha1-prf.c ifdef CONFIG_INTERNAL_SHA1 SHA1OBJS += src/crypto/sha1-internal.c ifdef NEED_FIPS186_2_PRF SHA1OBJS += src/crypto/fips_prf_internal.c endif endif +ifneq ($(CONFIG_TLS), openssl) SHA1OBJS += src/crypto/sha1-pbkdf2.c +endif ifdef NEED_T_PRF SHA1OBJS += src/crypto/sha1-tprf.c endif @@ -715,10 +757,17 @@ endif endif ifdef NEED_SHA256 +L_CFLAGS += -DCONFIG_SHA256 +ifneq ($(CONFIG_TLS), openssl) OBJS += src/crypto/sha256.c +endif +OBJS += src/crypto/sha256-prf.c ifdef CONFIG_INTERNAL_SHA256 OBJS += src/crypto/sha256-internal.c endif +ifdef NEED_TLS_PRF_SHA256 +OBJS += src/crypto/sha256-tlsprf.c +endif endif ifdef NEED_DH_GROUPS @@ -738,6 +787,7 @@ L_CFLAGS += -DCONFIG_NO_RANDOM_POOL else OBJS += src/crypto/random.c HOBJS += src/crypto/random.c +HOBJS += src/utils/eloop.c HOBJS += $(SHA1OBJS) HOBJS += src/crypto/md5.c endif @@ -776,11 +826,27 @@ ifdef CONFIG_IEEE80211N OBJS += src/ap/ieee802_11_ht.c endif +ifdef CONFIG_IEEE80211AC +OBJS += src/ap/ieee802_11_vht.c +endif + ifdef CONFIG_P2P_MANAGER L_CFLAGS += -DCONFIG_P2P_MANAGER OBJS += src/ap/p2p_hostapd.c endif +ifdef CONFIG_HS20 +L_CFLAGS += -DCONFIG_HS20 +OBJS += src/ap/hs20.c +CONFIG_INTERWORKING=y +endif + +ifdef CONFIG_INTERWORKING +L_CFLAGS += -DCONFIG_INTERWORKING +OBJS += src/common/gas.c +OBJS += src/ap/gas_serv.c +endif + OBJS += src/drivers/driver_common.c ifdef CONFIG_NO_STDOUT_DEBUG diff --git a/hostapd/Makefile b/hostapd/Makefile index b43aa75a..6809b076 100644 --- a/hostapd/Makefile +++ b/hostapd/Makefile @@ -123,6 +123,12 @@ ifdef CONFIG_NO_VLAN CFLAGS += -DCONFIG_NO_VLAN else OBJS += ../src/ap/vlan_init.o +ifdef CONFIG_VLAN_NETLINK +ifdef CONFIG_FULL_DYNAMIC_VLAN +OBJS += ../src/ap/vlan_util.o +endif +CFLAGS += -DCONFIG_VLAN_NETLINK +endif endif ifdef CONFIG_NO_CTRL_IFACE @@ -165,6 +171,11 @@ NEED_AES_OMAC1=y NEED_AES_UNWRAP=y endif +ifdef CONFIG_IEEE80211V +CFLAGS += -DCONFIG_IEEE80211V +OBJS += ../src/ap/wnm_ap.o +endif + ifdef CONFIG_IEEE80211N CFLAGS += -DCONFIG_IEEE80211N endif @@ -212,6 +223,14 @@ OBJS += ../src/eap_server/eap_server_tls.o TLS_FUNCS=y endif +ifdef CONFIG_EAP_UNAUTH_TLS +CFLAGS += -DEAP_SERVER_UNAUTH_TLS +ifndef CONFIG_EAP_TLS +OBJS += ../src/eap_server/eap_server_tls.o +TLS_FUNCS=y +endif +endif + ifdef CONFIG_EAP_PEAP CFLAGS += -DEAP_SERVER_PEAP OBJS += ../src/eap_server/eap_server_peap.o @@ -649,14 +668,19 @@ OBJS += $(AESOBJS) endif ifdef NEED_SHA1 +ifneq ($(CONFIG_TLS), openssl) SHA1OBJS += ../src/crypto/sha1.o +endif +SHA1OBJS += ../src/crypto/sha1-prf.o ifdef CONFIG_INTERNAL_SHA1 SHA1OBJS += ../src/crypto/sha1-internal.o ifdef NEED_FIPS186_2_PRF SHA1OBJS += ../src/crypto/fips_prf_internal.o endif endif +ifneq ($(CONFIG_TLS), openssl) SHA1OBJS += ../src/crypto/sha1-pbkdf2.o +endif ifdef NEED_T_PRF SHA1OBJS += ../src/crypto/sha1-tprf.o endif @@ -696,7 +720,10 @@ endif ifdef NEED_SHA256 CFLAGS += -DCONFIG_SHA256 +ifneq ($(CONFIG_TLS), openssl) OBJS += ../src/crypto/sha256.o +endif +OBJS += ../src/crypto/sha256-prf.o ifdef CONFIG_INTERNAL_SHA256 OBJS += ../src/crypto/sha256-internal.o endif @@ -770,6 +797,12 @@ CFLAGS += -DCONFIG_P2P_MANAGER OBJS += ../src/ap/p2p_hostapd.o endif +ifdef CONFIG_HS20 +CFLAGS += -DCONFIG_HS20 +OBJS += ../src/ap/hs20.o +CONFIG_INTERWORKING=y +endif + ifdef CONFIG_INTERWORKING CFLAGS += -DCONFIG_INTERWORKING OBJS += ../src/common/gas.o @@ -859,6 +892,11 @@ ifdef TLS_FUNCS LIBS_n += -lcrypto endif +ifdef CONFIG_SQLITE +CFLAGS += -DCONFIG_SQLITE +LIBS_h += -lsqlite3 +endif + HOBJS += hlr_auc_gw.o ../src/utils/common.o ../src/utils/wpa_debug.o ../src/utils/os_$(CONFIG_OS).o ../src/utils/wpabuf.o ../src/crypto/milenage.o HOBJS += ../src/crypto/aes-encblock.o ifdef CONFIG_INTERNAL_AES diff --git a/hostapd/config_file.c b/hostapd/config_file.c index eebbaa68..0a24ec37 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -167,7 +167,7 @@ static int hostapd_config_read_maclist(const char *fname, if (*pos != '\0') vlan_id = atoi(pos); - newacl = os_realloc(*acl, (*num + 1) * sizeof(**acl)); + newacl = os_realloc_array(*acl, *num + 1, sizeof(**acl)); if (newacl == NULL) { wpa_printf(MSG_ERROR, "MAC list reallocation failed"); fclose(f); @@ -475,7 +475,7 @@ hostapd_config_read_radius_addr(struct hostapd_radius_server **server, int ret; static int server_index = 1; - nserv = os_realloc(*server, (*num_server + 1) * sizeof(*nserv)); + nserv = os_realloc_array(*server, *num_server + 1, sizeof(*nserv)); if (nserv == NULL) return -1; @@ -567,25 +567,21 @@ static int hostapd_parse_das_client(struct hostapd_bss_config *bss, const char *val) { char *secret; - size_t len; secret = os_strchr(val, ' '); if (secret == NULL) return -1; secret++; - len = os_strlen(secret); if (hostapd_parse_ip_addr(val, &bss->radius_das_client_addr)) return -1; os_free(bss->radius_das_shared_secret); - bss->radius_das_shared_secret = os_malloc(len); + bss->radius_das_shared_secret = (u8 *) os_strdup(secret); if (bss->radius_das_shared_secret == NULL) return -1; - - os_memcpy(bss->radius_das_shared_secret, secret, len); - bss->radius_das_shared_secret_len = len; + bss->radius_das_shared_secret_len = os_strlen(secret); return 0; } @@ -673,6 +669,8 @@ static int hostapd_config_parse_cipher(int line, const char *value) *end = '\0'; if (os_strcmp(start, "CCMP") == 0) val |= WPA_CIPHER_CCMP; + else if (os_strcmp(start, "GCMP") == 0) + val |= WPA_CIPHER_GCMP; else if (os_strcmp(start, "TKIP") == 0) val |= WPA_CIPHER_TKIP; else if (os_strcmp(start, "WEP104") == 0) @@ -784,8 +782,8 @@ static int hostapd_config_bss(struct hostapd_config *conf, const char *ifname) if (*ifname == '\0') return -1; - bss = os_realloc(conf->bss, (conf->num_bss + 1) * - sizeof(struct hostapd_bss_config)); + bss = os_realloc_array(conf->bss, conf->num_bss + 1, + sizeof(struct hostapd_bss_config)); if (bss == NULL) { wpa_printf(MSG_ERROR, "Failed to allocate memory for " "multi-BSS entry"); @@ -909,78 +907,6 @@ static int hostapd_config_tx_queue(struct hostapd_config *conf, char *name, } -static int hostapd_config_wmm_ac(struct hostapd_config *conf, char *name, - char *val) -{ - int num, v; - char *pos; - struct hostapd_wmm_ac_params *ac; - - /* skip 'wme_ac_' or 'wmm_ac_' prefix */ - pos = name + 7; - if (os_strncmp(pos, "be_", 3) == 0) { - num = 0; - pos += 3; - } else if (os_strncmp(pos, "bk_", 3) == 0) { - num = 1; - pos += 3; - } else if (os_strncmp(pos, "vi_", 3) == 0) { - num = 2; - pos += 3; - } else if (os_strncmp(pos, "vo_", 3) == 0) { - num = 3; - pos += 3; - } else { - wpa_printf(MSG_ERROR, "Unknown WMM name '%s'", pos); - return -1; - } - - ac = &conf->wmm_ac_params[num]; - - if (os_strcmp(pos, "aifs") == 0) { - v = atoi(val); - if (v < 1 || v > 255) { - wpa_printf(MSG_ERROR, "Invalid AIFS value %d", v); - return -1; - } - ac->aifs = v; - } else if (os_strcmp(pos, "cwmin") == 0) { - v = atoi(val); - if (v < 0 || v > 12) { - wpa_printf(MSG_ERROR, "Invalid cwMin value %d", v); - return -1; - } - ac->cwmin = v; - } else if (os_strcmp(pos, "cwmax") == 0) { - v = atoi(val); - if (v < 0 || v > 12) { - wpa_printf(MSG_ERROR, "Invalid cwMax value %d", v); - return -1; - } - ac->cwmax = v; - } else if (os_strcmp(pos, "txop_limit") == 0) { - v = atoi(val); - if (v < 0 || v > 0xffff) { - wpa_printf(MSG_ERROR, "Invalid txop value %d", v); - return -1; - } - ac->txop_limit = v; - } else if (os_strcmp(pos, "acm") == 0) { - v = atoi(val); - if (v < 0 || v > 1) { - wpa_printf(MSG_ERROR, "Invalid acm value %d", v); - return -1; - } - ac->admission_control_mandatory = v; - } else { - wpa_printf(MSG_ERROR, "Unknown wmm_ac_ field '%s'", pos); - return -1; - } - - return 0; -} - - #ifdef CONFIG_IEEE80211R static int add_r0kh(struct hostapd_bss_config *bss, char *value) { @@ -1268,10 +1194,10 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss, if (conf->ieee80211n && bss->wpa && !(bss->wpa_pairwise & WPA_CIPHER_CCMP) && - !(bss->rsn_pairwise & WPA_CIPHER_CCMP)) { + !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP))) { bss->disable_11n = 1; wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) with WPA/WPA2 " - "requires CCMP to be enabled, disabling HT " + "requires CCMP/GCMP to be enabled, disabling HT " "capabilities"); } #endif /* CONFIG_IEEE80211N */ @@ -1288,8 +1214,27 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss, "disabled"); bss->wps_state = 0; } + + if (bss->wps_state && bss->wpa && + (!(bss->wpa & 2) || + !(bss->rsn_pairwise & WPA_CIPHER_CCMP))) { + wpa_printf(MSG_INFO, "WPS: WPA/TKIP configuration without " + "WPA2/CCMP forced WPS to be disabled"); + bss->wps_state = 0; + } #endif /* CONFIG_WPS2 */ +#ifdef CONFIG_HS20 + if (bss->hs20 && + (!(bss->wpa & 2) || + !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)))) { + wpa_printf(MSG_ERROR, "HS 2.0: WPA2-Enterprise/CCMP " + "configuration is required for Hotspot 2.0 " + "functionality"); + return -1; + } +#endif /* CONFIG_HS20 */ + return 0; } @@ -1330,9 +1275,9 @@ static int parse_roaming_consortium(struct hostapd_bss_config *bss, char *pos, } len /= 2; - rc = os_realloc(bss->roaming_consortium, - sizeof(struct hostapd_roaming_consortium) * - (bss->roaming_consortium_count + 1)); + rc = os_realloc_array(bss->roaming_consortium, + bss->roaming_consortium_count + 1, + sizeof(struct hostapd_roaming_consortium)); if (rc == NULL) return -1; @@ -1346,50 +1291,387 @@ static int parse_roaming_consortium(struct hostapd_bss_config *bss, char *pos, } -static int parse_venue_name(struct hostapd_bss_config *bss, char *pos, - int line) +static int parse_lang_string(struct hostapd_lang_string **array, + unsigned int *count, char *pos) { char *sep; size_t clen, nlen; - struct hostapd_venue_name *vn; + struct hostapd_lang_string *ls; sep = os_strchr(pos, ':'); if (sep == NULL) - goto fail; + return -1; *sep++ = '\0'; clen = os_strlen(pos); if (clen < 2) - goto fail; + return -1; nlen = os_strlen(sep); if (nlen > 252) + return -1; + + ls = os_realloc_array(*array, *count + 1, + sizeof(struct hostapd_lang_string)); + if (ls == NULL) + return -1; + + *array = ls; + ls = &(*array)[*count]; + (*count)++; + + os_memset(ls->lang, 0, sizeof(ls->lang)); + os_memcpy(ls->lang, pos, clen); + ls->name_len = nlen; + os_memcpy(ls->name, sep, nlen); + + return 0; +} + + +static int parse_venue_name(struct hostapd_bss_config *bss, char *pos, + int line) +{ + if (parse_lang_string(&bss->venue_name, &bss->venue_name_count, pos)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid venue_name '%s'", + line, pos); + return -1; + } + return 0; +} + + +static int parse_3gpp_cell_net(struct hostapd_bss_config *bss, char *buf, + int line) +{ + size_t count; + char *pos; + u8 *info = NULL, *ipos; + + /* format: <MCC1,MNC1>[;<MCC2,MNC2>][;...] */ + + count = 1; + for (pos = buf; *pos; pos++) { + if ((*pos < '0' && *pos > '9') && *pos != ';' && *pos != ',') + goto fail; + if (*pos == ';') + count++; + } + if (1 + count * 3 > 0x7f) goto fail; - vn = os_realloc(bss->venue_name, - sizeof(struct hostapd_venue_name) * - (bss->venue_name_count + 1)); - if (vn == NULL) + info = os_zalloc(2 + 3 + count * 3); + if (info == NULL) return -1; - bss->venue_name = vn; - vn = &bss->venue_name[bss->venue_name_count]; - bss->venue_name_count++; + ipos = info; + *ipos++ = 0; /* GUD - Version 1 */ + *ipos++ = 3 + count * 3; /* User Data Header Length (UDHL) */ + *ipos++ = 0; /* PLMN List IEI */ + /* ext(b8) | Length of PLMN List value contents(b7..1) */ + *ipos++ = 1 + count * 3; + *ipos++ = count; /* Number of PLMNs */ + + pos = buf; + while (pos && *pos) { + char *mcc, *mnc; + size_t mnc_len; + + mcc = pos; + mnc = os_strchr(pos, ','); + if (mnc == NULL) + goto fail; + *mnc++ = '\0'; + pos = os_strchr(mnc, ';'); + if (pos) + *pos++ = '\0'; + + mnc_len = os_strlen(mnc); + if (os_strlen(mcc) != 3 || (mnc_len != 2 && mnc_len != 3)) + goto fail; + + /* BC coded MCC,MNC */ + /* MCC digit 2 | MCC digit 1 */ + *ipos++ = ((mcc[1] - '0') << 4) | (mcc[0] - '0'); + /* MNC digit 3 | MCC digit 3 */ + *ipos++ = (((mnc_len == 2) ? 0xf0 : ((mnc[2] - '0') << 4))) | + (mcc[2] - '0'); + /* MNC digit 2 | MNC digit 1 */ + *ipos++ = ((mnc[1] - '0') << 4) | (mnc[0] - '0'); + } - os_memset(vn->lang, 0, sizeof(vn->lang)); - os_memcpy(vn->lang, pos, clen); - vn->name_len = nlen; - os_memcpy(vn->name, sep, nlen); + os_free(bss->anqp_3gpp_cell_net); + bss->anqp_3gpp_cell_net = info; + bss->anqp_3gpp_cell_net_len = 2 + 3 + 3 * count; + wpa_hexdump(MSG_MSGDUMP, "3GPP Cellular Network information", + bss->anqp_3gpp_cell_net, bss->anqp_3gpp_cell_net_len); return 0; fail: - wpa_printf(MSG_ERROR, "Line %d: Invalid venue_name '%s'", - line, pos); + wpa_printf(MSG_ERROR, "Line %d: Invalid anqp_3gpp_cell_net: %s", + line, buf); + os_free(info); return -1; } + + +static int parse_nai_realm(struct hostapd_bss_config *bss, char *buf, int line) +{ + struct hostapd_nai_realm_data *realm; + size_t i, j, len; + int *offsets; + char *pos, *end, *rpos; + + offsets = os_calloc(bss->nai_realm_count * MAX_NAI_REALMS, + sizeof(int)); + if (offsets == NULL) + return -1; + + for (i = 0; i < bss->nai_realm_count; i++) { + realm = &bss->nai_realm_data[i]; + for (j = 0; j < MAX_NAI_REALMS; j++) { + offsets[i * MAX_NAI_REALMS + j] = + realm->realm[j] ? + realm->realm[j] - realm->realm_buf : -1; + } + } + + realm = os_realloc_array(bss->nai_realm_data, bss->nai_realm_count + 1, + sizeof(struct hostapd_nai_realm_data)); + if (realm == NULL) { + os_free(offsets); + return -1; + } + bss->nai_realm_data = realm; + + /* patch the pointers after realloc */ + for (i = 0; i < bss->nai_realm_count; i++) { + realm = &bss->nai_realm_data[i]; + for (j = 0; j < MAX_NAI_REALMS; j++) { + int offs = offsets[i * MAX_NAI_REALMS + j]; + if (offs >= 0) + realm->realm[j] = realm->realm_buf + offs; + else + realm->realm[j] = NULL; + } + } + os_free(offsets); + + realm = &bss->nai_realm_data[bss->nai_realm_count]; + os_memset(realm, 0, sizeof(*realm)); + + pos = buf; + realm->encoding = atoi(pos); + pos = os_strchr(pos, ','); + if (pos == NULL) + goto fail; + pos++; + + end = os_strchr(pos, ','); + if (end) { + len = end - pos; + *end = '\0'; + } else { + len = os_strlen(pos); + } + + if (len > MAX_NAI_REALMLEN) { + wpa_printf(MSG_ERROR, "Too long a realm string (%d > max %d " + "characters)", (int) len, MAX_NAI_REALMLEN); + goto fail; + } + os_memcpy(realm->realm_buf, pos, len); + + if (end) + pos = end + 1; + else + pos = NULL; + + while (pos && *pos) { + struct hostapd_nai_realm_eap *eap; + + if (realm->eap_method_count >= MAX_NAI_EAP_METHODS) { + wpa_printf(MSG_ERROR, "Too many EAP methods"); + goto fail; + } + + eap = &realm->eap_method[realm->eap_method_count]; + realm->eap_method_count++; + + end = os_strchr(pos, ','); + if (end == NULL) + end = pos + os_strlen(pos); + + eap->eap_method = atoi(pos); + for (;;) { + pos = os_strchr(pos, '['); + if (pos == NULL || pos > end) + break; + pos++; + if (eap->num_auths >= MAX_NAI_AUTH_TYPES) { + wpa_printf(MSG_ERROR, "Too many auth params"); + goto fail; + } + eap->auth_id[eap->num_auths] = atoi(pos); + pos = os_strchr(pos, ':'); + if (pos == NULL || pos > end) + goto fail; + pos++; + eap->auth_val[eap->num_auths] = atoi(pos); + pos = os_strchr(pos, ']'); + if (pos == NULL || pos > end) + goto fail; + pos++; + eap->num_auths++; + } + + if (*end != ',') + break; + + pos = end + 1; + } + + /* Split realm list into null terminated realms */ + rpos = realm->realm_buf; + i = 0; + while (*rpos) { + if (i >= MAX_NAI_REALMS) { + wpa_printf(MSG_ERROR, "Too many realms"); + goto fail; + } + realm->realm[i++] = rpos; + rpos = os_strchr(rpos, ';'); + if (rpos == NULL) + break; + *rpos++ = '\0'; + } + + bss->nai_realm_count++; + + return 0; + +fail: + wpa_printf(MSG_ERROR, "Line %d: invalid nai_realm '%s'", line, buf); + return -1; +} + #endif /* CONFIG_INTERWORKING */ +#ifdef CONFIG_HS20 +static int hs20_parse_conn_capab(struct hostapd_bss_config *bss, char *buf, + int line) +{ + u8 *conn_cap; + char *pos; + + if (bss->hs20_connection_capability_len >= 0xfff0) + return -1; + + conn_cap = os_realloc(bss->hs20_connection_capability, + bss->hs20_connection_capability_len + 4); + if (conn_cap == NULL) + return -1; + + bss->hs20_connection_capability = conn_cap; + conn_cap += bss->hs20_connection_capability_len; + pos = buf; + conn_cap[0] = atoi(pos); + pos = os_strchr(pos, ':'); + if (pos == NULL) + return -1; + pos++; + WPA_PUT_LE16(conn_cap + 1, atoi(pos)); + pos = os_strchr(pos, ':'); + if (pos == NULL) + return -1; + pos++; + conn_cap[3] = atoi(pos); + bss->hs20_connection_capability_len += 4; + + return 0; +} + + +static int hs20_parse_wan_metrics(struct hostapd_bss_config *bss, char *buf, + int line) +{ + u8 *wan_metrics; + char *pos; + + /* <WAN Info>:<DL Speed>:<UL Speed>:<DL Load>:<UL Load>:<LMD> */ + + wan_metrics = os_zalloc(13); + if (wan_metrics == NULL) + return -1; + + pos = buf; + /* WAN Info */ + if (hexstr2bin(pos, wan_metrics, 1) < 0) + goto fail; + pos += 2; + if (*pos != ':') + goto fail; + pos++; + + /* Downlink Speed */ + WPA_PUT_LE32(wan_metrics + 1, atoi(pos)); + pos = os_strchr(pos, ':'); + if (pos == NULL) + goto fail; + pos++; + + /* Uplink Speed */ + WPA_PUT_LE32(wan_metrics + 5, atoi(pos)); + pos = os_strchr(pos, ':'); + if (pos == NULL) + goto fail; + pos++; + + /* Downlink Load */ + wan_metrics[9] = atoi(pos); + pos = os_strchr(pos, ':'); + if (pos == NULL) + goto fail; + pos++; + + /* Uplink Load */ + wan_metrics[10] = atoi(pos); + pos = os_strchr(pos, ':'); + if (pos == NULL) + goto fail; + pos++; + + /* LMD */ + WPA_PUT_LE16(wan_metrics + 11, atoi(pos)); + + os_free(bss->hs20_wan_metrics); + bss->hs20_wan_metrics = wan_metrics; + + return 0; + +fail: + wpa_printf(MSG_ERROR, "Line %d: Invalid hs20_wan_metrics '%s'", + line, pos); + os_free(wan_metrics); + return -1; +} + + +static int hs20_parse_oper_friendly_name(struct hostapd_bss_config *bss, + char *pos, int line) +{ + if (parse_lang_string(&bss->hs20_oper_friendly_name, + &bss->hs20_oper_friendly_name_count, pos)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "hs20_oper_friendly_name '%s'", line, pos); + return -1; + } + return 0; +} +#endif /* CONFIG_HS20 */ + + #ifdef CONFIG_WPS_NFC static struct wpabuf * hostapd_parse_bin(const char *buf) { @@ -1470,9 +1752,22 @@ static int hostapd_config_fill(struct hostapd_config *conf, } else { os_memcpy(bss->ssid.ssid, pos, bss->ssid.ssid_len); - bss->ssid.ssid[bss->ssid.ssid_len] = '\0'; bss->ssid.ssid_set = 1; } + } 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) { + wpa_printf(MSG_ERROR, "Line %d: invalid SSID " + "'%s'", line, pos); + errors++; + } else { + os_memcpy(bss->ssid.ssid, str, slen); + bss->ssid.ssid_len = slen; + bss->ssid.ssid_set = 1; + } + os_free(str); } else if (os_strcmp(buf, "macaddr_acl") == 0) { bss->macaddr_acl = atoi(pos); if (bss->macaddr_acl != ACCEPT_UNLESS_DENIED && @@ -2120,6 +2415,15 @@ static int hostapd_config_fill(struct hostapd_config *conf, "read VLAN file '%s'", line, pos); errors++; } + } else if (os_strcmp(buf, "vlan_naming") == 0) { + bss->ssid.vlan_naming = atoi(pos); + if (bss->ssid.vlan_naming >= DYNAMIC_VLAN_NAMING_END || + bss->ssid.vlan_naming < 0) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "naming scheme %d", line, + bss->ssid.vlan_naming); + errors++; + } #ifdef CONFIG_FULL_DYNAMIC_VLAN } else if (os_strcmp(buf, "vlan_tagged_interface") == 0) { bss->ssid.vlan_tagged_interface = os_strdup(pos); @@ -2142,7 +2446,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, bss->wmm_uapsd = atoi(pos); } else if (os_strncmp(buf, "wme_ac_", 7) == 0 || os_strncmp(buf, "wmm_ac_", 7) == 0) { - if (hostapd_config_wmm_ac(conf, buf, pos)) { + if (hostapd_config_wmm_ac(conf->wmm_ac_params, buf, + pos)) { wpa_printf(MSG_ERROR, "Line %d: invalid WMM " "ac item", line); errors++; @@ -2200,8 +2505,13 @@ static int hostapd_config_fill(struct hostapd_config *conf, "vht_capab", line); errors++; } + } else if (os_strcmp(buf, "require_vht") == 0) { + conf->require_vht = atoi(pos); } else if (os_strcmp(buf, "vht_oper_chwidth") == 0) { - conf->vht_oper_chwidth = atoi(pos); + conf->vht_oper_chwidth = atoi(pos); + } else if (os_strcmp(buf, "vht_oper_centr_freq_seg0_idx") == 0) + { + conf->vht_oper_centr_freq_seg0_idx = atoi(pos); #endif /* CONFIG_IEEE80211AC */ } else if (os_strcmp(buf, "max_listen_interval") == 0) { bss->max_listen_interval = atoi(pos); @@ -2429,6 +2739,92 @@ static int hostapd_config_fill(struct hostapd_config *conf, } else if (os_strcmp(buf, "venue_name") == 0) { if (parse_venue_name(bss, pos, line) < 0) errors++; + } else if (os_strcmp(buf, "network_auth_type") == 0) { + u8 auth_type; + u16 redirect_url_len; + if (hexstr2bin(pos, &auth_type, 1)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "network_auth_type '%s'", + line, pos); + errors++; + return errors; + } + if (auth_type == 0 || auth_type == 2) + redirect_url_len = os_strlen(pos + 2); + else + redirect_url_len = 0; + os_free(bss->network_auth_type); + bss->network_auth_type = + os_malloc(redirect_url_len + 3 + 1); + if (bss->network_auth_type == NULL) { + errors++; + return errors; + } + *bss->network_auth_type = auth_type; + WPA_PUT_LE16(bss->network_auth_type + 1, + redirect_url_len); + if (redirect_url_len) + os_memcpy(bss->network_auth_type + 3, + pos + 2, redirect_url_len); + bss->network_auth_type_len = 3 + redirect_url_len; + } else if (os_strcmp(buf, "ipaddr_type_availability") == 0) { + if (hexstr2bin(pos, &bss->ipaddr_type_availability, 1)) + { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "ipaddr_type_availability '%s'", + line, pos); + bss->ipaddr_type_configured = 0; + errors++; + return errors; + } + bss->ipaddr_type_configured = 1; + } else if (os_strcmp(buf, "domain_name") == 0) { + int j, num_domains, domain_len, domain_list_len = 0; + char *tok_start, *tok_prev; + u8 *domain_list, *domain_ptr; + + domain_list_len = os_strlen(pos) + 1; + domain_list = os_malloc(domain_list_len); + if (domain_list == NULL) { + errors++; + return errors; + } + + domain_ptr = domain_list; + tok_prev = pos; + num_domains = 1; + while ((tok_prev = os_strchr(tok_prev, ','))) { + num_domains++; + tok_prev++; + } + tok_prev = pos; + for (j = 0; j < num_domains; j++) { + tok_start = os_strchr(tok_prev, ','); + if (tok_start) { + domain_len = tok_start - tok_prev; + *domain_ptr = domain_len; + os_memcpy(domain_ptr + 1, tok_prev, + domain_len); + domain_ptr += domain_len + 1; + tok_prev = ++tok_start; + } else { + domain_len = os_strlen(tok_prev); + *domain_ptr = domain_len; + os_memcpy(domain_ptr + 1, tok_prev, + domain_len); + domain_ptr += domain_len + 1; + } + } + + os_free(bss->domain_name); + bss->domain_name = domain_list; + bss->domain_name_len = domain_list_len; + } else if (os_strcmp(buf, "anqp_3gpp_cell_net") == 0) { + if (parse_3gpp_cell_net(bss, pos, line) < 0) + errors++; + } else if (os_strcmp(buf, "nai_realm") == 0) { + if (parse_nai_realm(bss, pos, line) < 0) + errors++; } else if (os_strcmp(buf, "gas_frag_limit") == 0) { bss->gas_frag_limit = atoi(pos); } else if (os_strcmp(buf, "gas_comeback_delay") == 0) { @@ -2439,6 +2835,81 @@ static int hostapd_config_fill(struct hostapd_config *conf, os_free(bss->dump_msk_file); bss->dump_msk_file = os_strdup(pos); #endif /* CONFIG_RADIUS_TEST */ +#ifdef CONFIG_HS20 + } else if (os_strcmp(buf, "hs20") == 0) { + bss->hs20 = atoi(pos); + } else if (os_strcmp(buf, "disable_dgaf") == 0) { + bss->disable_dgaf = atoi(pos); + } else if (os_strcmp(buf, "hs20_oper_friendly_name") == 0) { + if (hs20_parse_oper_friendly_name(bss, pos, line) < 0) + errors++; + } else if (os_strcmp(buf, "hs20_wan_metrics") == 0) { + if (hs20_parse_wan_metrics(bss, pos, line) < 0) { + errors++; + return errors; + } + } else if (os_strcmp(buf, "hs20_conn_capab") == 0) { + if (hs20_parse_conn_capab(bss, pos, line) < 0) { + errors++; + return errors; + } + } else if (os_strcmp(buf, "hs20_operating_class") == 0) { + u8 *oper_class; + size_t oper_class_len; + oper_class_len = os_strlen(pos); + if (oper_class_len < 2 || (oper_class_len & 0x01)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "hs20_operating_class '%s'", + line, pos); + errors++; + return errors; + } + oper_class_len /= 2; + oper_class = os_malloc(oper_class_len); + if (oper_class == NULL) { + errors++; + return errors; + } + if (hexstr2bin(pos, oper_class, oper_class_len)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "hs20_operating_class '%s'", + line, pos); + os_free(oper_class); + errors++; + return errors; + } + os_free(bss->hs20_operating_class); + bss->hs20_operating_class = oper_class; + bss->hs20_operating_class_len = oper_class_len; +#endif /* CONFIG_HS20 */ + } else if (os_strcmp(buf, "vendor_elements") == 0) { + struct wpabuf *elems; + size_t len = os_strlen(pos); + if (len & 0x01) { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "vendor_elements '%s'", line, pos); + return 1; + } + len /= 2; + if (len == 0) { + wpabuf_free(bss->vendor_elements); + bss->vendor_elements = NULL; + return 0; + } + + elems = wpabuf_alloc(len); + if (elems == NULL) + return 1; + + if (hexstr2bin(pos, wpabuf_put(elems, len), len)) { + wpabuf_free(elems); + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "vendor_elements '%s'", line, pos); + return 1; + } + + wpabuf_free(bss->vendor_elements); + bss->vendor_elements = elems; } else { wpa_printf(MSG_ERROR, "Line %d: unknown configuration " "item '%s'", line, buf); @@ -2472,6 +2943,9 @@ static void hostapd_set_security_params(struct hostapd_bss_config *bss) } if (pairwise & WPA_CIPHER_TKIP) bss->wpa_group = WPA_CIPHER_TKIP; + else if ((pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) == + WPA_CIPHER_GCMP) + bss->wpa_group = WPA_CIPHER_GCMP; else bss->wpa_group = WPA_CIPHER_CCMP; diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c index 7587e032..1b8bede1 100644 --- a/hostapd/ctrl_iface.c +++ b/hostapd/ctrl_iface.c @@ -523,7 +523,8 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd, ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n" "ssid=%s\n", MAC2STR(hapd->own_addr), - hapd->conf->ssid.ssid); + wpa_ssid_txt(hapd->conf->ssid.ssid, + hapd->conf->ssid.ssid_len)); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; @@ -618,6 +619,12 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd, return pos - buf; pos += ret; } else if (hapd->conf->wpa && + hapd->conf->wpa_group == WPA_CIPHER_GCMP) { + ret = os_snprintf(pos, end - pos, "group_cipher=GCMP\n"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } else if (hapd->conf->wpa && hapd->conf->wpa_group == WPA_CIPHER_TKIP) { ret = os_snprintf(pos, end - pos, "group_cipher=TKIP\n"); if (ret < 0 || ret >= end - pos) @@ -637,6 +644,12 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd, return pos - buf; pos += ret; } + if (hapd->conf->rsn_pairwise & WPA_CIPHER_GCMP) { + ret = os_snprintf(pos, end - pos, "GCMP "); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } if (hapd->conf->rsn_pairwise & WPA_CIPHER_TKIP) { ret = os_snprintf(pos, end - pos, "TKIP "); if (ret < 0 || ret >= end - pos) @@ -662,6 +675,12 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd, return pos - buf; pos += ret; } + if (hapd->conf->wpa_pairwise & WPA_CIPHER_GCMP) { + ret = os_snprintf(pos, end - pos, "GCMP "); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } if (hapd->conf->wpa_pairwise & WPA_CIPHER_TKIP) { ret = os_snprintf(pos, end - pos, "TKIP "); if (ret < 0 || ret >= end - pos) @@ -746,6 +765,36 @@ static int hostapd_ctrl_iface_get(struct hostapd_data *hapd, char *cmd, } +static int hostapd_ctrl_iface_enable(struct hostapd_iface *iface) +{ + if (hostapd_enable_iface(iface) < 0) { + wpa_printf(MSG_ERROR, "Enabling of interface failed"); + return -1; + } + return 0; +} + + +static int hostapd_ctrl_iface_reload(struct hostapd_iface *iface) +{ + if (hostapd_reload_iface(iface) < 0) { + wpa_printf(MSG_ERROR, "Reloading of interface failed"); + return -1; + } + return 0; +} + + +static int hostapd_ctrl_iface_disable(struct hostapd_iface *iface) +{ + if (hostapd_disable_iface(iface) < 0) { + wpa_printf(MSG_ERROR, "Disabling of interface failed"); + return -1; + } + return 0; +} + + static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, void *sock_ctx) { @@ -898,6 +947,15 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, } else if (os_strncmp(buf, "GET ", 4) == 0) { reply_len = hostapd_ctrl_iface_get(hapd, buf + 4, reply, reply_size); + } else if (os_strncmp(buf, "ENABLE", 6) == 0) { + if (hostapd_ctrl_iface_enable(hapd->iface)) + reply_len = -1; + } else if (os_strncmp(buf, "RELOAD", 6) == 0) { + if (hostapd_ctrl_iface_reload(hapd->iface)) + reply_len = -1; + } else if (os_strncmp(buf, "DISABLE", 7) == 0) { + if (hostapd_ctrl_iface_disable(hapd->iface)) + reply_len = -1; } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; @@ -1091,6 +1149,220 @@ void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd) } +static int hostapd_ctrl_iface_add(struct hapd_interfaces *interfaces, + char *buf) +{ + if (hostapd_add_iface(interfaces, buf) < 0) { + wpa_printf(MSG_ERROR, "Adding interface %s failed", buf); + return -1; + } + return 0; +} + + +static int hostapd_ctrl_iface_remove(struct hapd_interfaces *interfaces, + char *buf) +{ + if (hostapd_remove_iface(interfaces, buf) < 0) { + wpa_printf(MSG_ERROR, "Removing interface %s failed", buf); + return -1; + } + return 0; +} + + +static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx, + void *sock_ctx) +{ + void *interfaces = eloop_ctx; + char buf[256]; + int res; + struct sockaddr_un from; + socklen_t fromlen = sizeof(from); + char reply[24]; + int reply_len; + + res = recvfrom(sock, buf, sizeof(buf) - 1, 0, + (struct sockaddr *) &from, &fromlen); + if (res < 0) { + perror("recvfrom(ctrl_iface)"); + return; + } + buf[res] = '\0'; + + os_memcpy(reply, "OK\n", 3); + reply_len = 3; + + if (os_strcmp(buf, "PING") == 0) { + os_memcpy(reply, "PONG\n", 5); + reply_len = 5; + } else if (os_strncmp(buf, "ADD ", 4) == 0) { + if (hostapd_ctrl_iface_add(interfaces, buf + 4) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "REMOVE ", 7) == 0) { + if (hostapd_ctrl_iface_remove(interfaces, buf + 7) < 0) + reply_len = -1; + } else { + wpa_printf(MSG_DEBUG, "Unrecognized global ctrl_iface command " + "ignored"); + reply_len = -1; + } + + if (reply_len < 0) { + os_memcpy(reply, "FAIL\n", 5); + reply_len = 5; + } + + sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen); +} + + +static char * hostapd_global_ctrl_iface_path(struct hapd_interfaces *interface) +{ + char *buf; + size_t len; + + if (interface->global_iface_path == NULL) + return NULL; + + len = os_strlen(interface->global_iface_path) + + os_strlen(interface->global_iface_name) + 2; + buf = os_malloc(len); + if (buf == NULL) + return NULL; + + os_snprintf(buf, len, "%s/%s", interface->global_iface_path, + interface->global_iface_name); + buf[len - 1] = '\0'; + return buf; +} + + +int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface) +{ + struct sockaddr_un addr; + int s = -1; + char *fname = NULL; + + if (interface->global_iface_path == NULL) { + wpa_printf(MSG_DEBUG, "ctrl_iface not configured!"); + return 0; + } + + if (mkdir(interface->global_iface_path, S_IRWXU | S_IRWXG) < 0) { + if (errno == EEXIST) { + wpa_printf(MSG_DEBUG, "Using existing control " + "interface directory."); + } else { + perror("mkdir[ctrl_interface]"); + goto fail; + } + } + + if (os_strlen(interface->global_iface_path) + 1 + + os_strlen(interface->global_iface_name) >= sizeof(addr.sun_path)) + goto fail; + + s = socket(PF_UNIX, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket(PF_UNIX)"); + goto fail; + } + + os_memset(&addr, 0, sizeof(addr)); +#ifdef __FreeBSD__ + addr.sun_len = sizeof(addr); +#endif /* __FreeBSD__ */ + addr.sun_family = AF_UNIX; + fname = hostapd_global_ctrl_iface_path(interface); + if (fname == NULL) + goto fail; + os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path)); + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + wpa_printf(MSG_DEBUG, "ctrl_iface bind(PF_UNIX) failed: %s", + strerror(errno)); + if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not" + " allow connections - assuming it was left" + "over from forced program termination"); + if (unlink(fname) < 0) { + perror("unlink[ctrl_iface]"); + wpa_printf(MSG_ERROR, "Could not unlink " + "existing ctrl_iface socket '%s'", + fname); + goto fail; + } + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < + 0) { + perror("bind(PF_UNIX)"); + goto fail; + } + wpa_printf(MSG_DEBUG, "Successfully replaced leftover " + "ctrl_iface socket '%s'", fname); + } else { + wpa_printf(MSG_INFO, "ctrl_iface exists and seems to " + "be in use - cannot override it"); + wpa_printf(MSG_INFO, "Delete '%s' manually if it is " + "not used anymore", fname); + os_free(fname); + fname = NULL; + goto fail; + } + } + + if (chmod(fname, S_IRWXU | S_IRWXG) < 0) { + perror("chmod[ctrl_interface/ifname]"); + goto fail; + } + os_free(fname); + + interface->global_ctrl_sock = s; + eloop_register_read_sock(s, hostapd_global_ctrl_iface_receive, + interface, NULL); + + return 0; + +fail: + if (s >= 0) + close(s); + if (fname) { + unlink(fname); + os_free(fname); + } + return -1; +} + + +void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interfaces) +{ + char *fname = NULL; + + if (interfaces->global_ctrl_sock > -1) { + eloop_unregister_read_sock(interfaces->global_ctrl_sock); + close(interfaces->global_ctrl_sock); + interfaces->global_ctrl_sock = -1; + fname = hostapd_global_ctrl_iface_path(interfaces); + if (fname) { + unlink(fname); + os_free(fname); + } + + if (interfaces->global_iface_path && + rmdir(interfaces->global_iface_path) < 0) { + if (errno == ENOTEMPTY) { + wpa_printf(MSG_DEBUG, "Control interface " + "directory not empty - leaving it " + "behind"); + } else { + perror("rmdir[ctrl_interface]"); + } + } + os_free(interfaces->global_iface_path); + interfaces->global_iface_path = NULL; + } +} + + static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, const char *buf, size_t len) { diff --git a/hostapd/ctrl_iface.h b/hostapd/ctrl_iface.h index 64a50803..3341a66b 100644 --- a/hostapd/ctrl_iface.h +++ b/hostapd/ctrl_iface.h @@ -12,6 +12,8 @@ #ifndef CONFIG_NO_CTRL_IFACE int hostapd_ctrl_iface_init(struct hostapd_data *hapd); void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd); +int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface); +void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interface); #else /* CONFIG_NO_CTRL_IFACE */ static inline int hostapd_ctrl_iface_init(struct hostapd_data *hapd) { @@ -21,6 +23,17 @@ static inline int hostapd_ctrl_iface_init(struct hostapd_data *hapd) static inline void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd) { } + +static inline int +hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface) +{ + return 0; +} + +static inline void +hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interface) +{ +} #endif /* CONFIG_NO_CTRL_IFACE */ #endif /* CTRL_IFACE_H */ diff --git a/hostapd/defconfig b/hostapd/defconfig index 9c5b13a4..204aa768 100644 --- a/hostapd/defconfig +++ b/hostapd/defconfig @@ -167,6 +167,10 @@ CONFIG_IPV6=y # automatically create bridge and VLAN interfaces if necessary. #CONFIG_FULL_DYNAMIC_VLAN=y +# Use netlink-based kernel API for VLAN operations instead of ioctl() +# Note: This requires libnl 3.1 or newer. +#CONFIG_VLAN_NETLINK=y + # Remove support for dumping state into a file on SIGUSR1 signal # This can be used to reduce binary size at the cost of disabling a debugging # option. @@ -257,3 +261,9 @@ CONFIG_IPV6=y # This can be used to enable functionality to improve interworking with # external networks. #CONFIG_INTERWORKING=y + +# Hotspot 2.0 +#CONFIG_HS20=y + +# Enable SQLite database support in hlr_auc_gw +#CONFIG_SQLITE=y diff --git a/hostapd/eap_register.c b/hostapd/eap_register.c index 089c7b23..0a7ff918 100644 --- a/hostapd/eap_register.c +++ b/hostapd/eap_register.c @@ -39,6 +39,11 @@ int eap_server_register_methods(void) ret = eap_server_tls_register(); #endif /* EAP_SERVER_TLS */ +#ifdef EAP_SERVER_UNAUTH_TLS + if (ret == 0) + ret = eap_server_unauth_tls_register(); +#endif /* EAP_SERVER_TLS */ + #ifdef EAP_SERVER_MSCHAPV2 if (ret == 0) ret = eap_server_mschapv2_register(); diff --git a/hostapd/hlr_auc_gw.c b/hostapd/hlr_auc_gw.c index 516d859d..e27ddaba 100644 --- a/hostapd/hlr_auc_gw.c +++ b/hostapd/hlr_auc_gw.c @@ -43,6 +43,9 @@ #include "includes.h" #include <sys/un.h> +#ifdef CONFIG_SQLITE +#include <sqlite3.h> +#endif /* CONFIG_SQLITE */ #include "common.h" #include "crypto/milenage.h" @@ -89,6 +92,140 @@ static struct milenage_parameters *milenage_db = NULL; #define EAP_AKA_CK_LEN 16 +#ifdef CONFIG_SQLITE + +static sqlite3 *sqlite_db = NULL; +static struct milenage_parameters db_tmp_milenage; + + +static int db_table_exists(sqlite3 *db, const char *name) +{ + char cmd[128]; + os_snprintf(cmd, sizeof(cmd), "SELECT 1 FROM %s;", name); + return sqlite3_exec(db, cmd, NULL, NULL, NULL) == SQLITE_OK; +} + + +static int db_table_create_milenage(sqlite3 *db) +{ + char *err = NULL; + const char *sql = + "CREATE TABLE milenage(" + " imsi INTEGER PRIMARY KEY NOT NULL," + " ki CHAR(32) NOT NULL," + " opc CHAR(32) NOT NULL," + " amf CHAR(4) NOT NULL," + " sqn CHAR(12) NOT NULL" + ");"; + + printf("Adding database table for milenage information\n"); + if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) { + printf("SQLite error: %s\n", err); + sqlite3_free(err); + return -1; + } + + return 0; +} + + +static sqlite3 * db_open(const char *db_file) +{ + sqlite3 *db; + + if (sqlite3_open(db_file, &db)) { + printf("Failed to open database %s: %s\n", + db_file, sqlite3_errmsg(db)); + sqlite3_close(db); + return NULL; + } + + if (!db_table_exists(db, "milenage") && + db_table_create_milenage(db) < 0) { + sqlite3_close(db); + return NULL; + } + + return db; +} + + +static int get_milenage_cb(void *ctx, int argc, char *argv[], char *col[]) +{ + struct milenage_parameters *m = ctx; + int i; + + for (i = 0; i < argc; i++) { + if (os_strcmp(col[i], "ki") == 0 && argv[i] && + hexstr2bin(argv[i], m->ki, sizeof(m->ki))) { + printf("Invalid ki value in database\n"); + return -1; + } + + if (os_strcmp(col[i], "opc") == 0 && argv[i] && + hexstr2bin(argv[i], m->opc, sizeof(m->opc))) { + printf("Invalid opcvalue in database\n"); + return -1; + } + + if (os_strcmp(col[i], "amf") == 0 && argv[i] && + hexstr2bin(argv[i], m->amf, sizeof(m->amf))) { + printf("Invalid amf value in database\n"); + return -1; + } + + if (os_strcmp(col[i], "sqn") == 0 && argv[i] && + hexstr2bin(argv[i], m->sqn, sizeof(m->sqn))) { + printf("Invalid sqn value in database\n"); + return -1; + } + } + + return 0; +} + + +static struct milenage_parameters * db_get_milenage(const char *imsi_txt) +{ + char cmd[128]; + unsigned long long imsi; + + os_memset(&db_tmp_milenage, 0, sizeof(db_tmp_milenage)); + imsi = atoll(imsi_txt); + os_snprintf(db_tmp_milenage.imsi, sizeof(db_tmp_milenage.imsi), + "%llu", imsi); + os_snprintf(cmd, sizeof(cmd), + "SELECT ki,opc,amf,sqn FROM milenage WHERE imsi=%llu;", + imsi); + if (sqlite3_exec(sqlite_db, cmd, get_milenage_cb, &db_tmp_milenage, + NULL) != SQLITE_OK) + return NULL; + + return &db_tmp_milenage; +} + + +static int db_update_milenage_sqn(struct milenage_parameters *m) +{ + char cmd[128], val[13], *pos; + + pos = val; + pos += wpa_snprintf_hex(pos, sizeof(val), m->sqn, 6); + *pos = '\0'; + os_snprintf(cmd, sizeof(cmd), + "UPDATE milenage SET sqn='%s' WHERE imsi=%s;", + val, m->imsi); + if (sqlite3_exec(sqlite_db, cmd, NULL, NULL, NULL) != SQLITE_OK) { + printf("Failed to update SQN in database for IMSI %s\n", + m->imsi); + return -1; + } + return 0; +} + +#endif /* CONFIG_SQLITE */ + + static int open_socket(const char *path) { struct sockaddr_un addr; @@ -460,6 +597,11 @@ static struct milenage_parameters * get_milenage(const char *imsi) m = m->next; } +#ifdef CONFIG_SQLITE + if (!m) + m = db_get_milenage(imsi); +#endif /* CONFIG_SQLITE */ + return m; } @@ -577,6 +719,7 @@ static void aka_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen, size_t res_len; int ret; struct milenage_parameters *m; + int failed = 0; m = get_milenage(imsi); if (m) { @@ -584,6 +727,9 @@ static void aka_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen, return; res_len = EAP_AKA_RES_MAX_LEN; inc_sqn(m->sqn); +#ifdef CONFIG_SQLITE + db_update_milenage_sqn(m); +#endif /* CONFIG_SQLITE */ sqn_changes = 1; printf("AKA: Milenage with SQN=%02x%02x%02x%02x%02x%02x\n", m->sqn[0], m->sqn[1], m->sqn[2], @@ -601,7 +747,7 @@ static void aka_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen, memset(res, '2', EAP_AKA_RES_MAX_LEN); res_len = EAP_AKA_RES_MAX_LEN; #else /* AKA_USE_FIXED_TEST_VALUES */ - return; + failed = 1; #endif /* AKA_USE_FIXED_TEST_VALUES */ } @@ -611,6 +757,13 @@ static void aka_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen, if (ret < 0 || ret >= end - pos) return; pos += ret; + if (failed) { + ret = snprintf(pos, end - pos, "FAILURE"); + if (ret < 0 || ret >= end - pos) + return; + pos += ret; + goto done; + } pos += wpa_snprintf_hex(pos, end - pos, _rand, EAP_AKA_RAND_LEN); *pos++ = ' '; pos += wpa_snprintf_hex(pos, end - pos, autn, EAP_AKA_AUTN_LEN); @@ -621,6 +774,7 @@ static void aka_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen, *pos++ = ' '; pos += wpa_snprintf_hex(pos, end - pos, res, res_len); +done: printf("Send: %s\n", reply); if (sendto(s, reply, pos - reply, 0, (struct sockaddr *) from, @@ -668,6 +822,9 @@ static void aka_auts(int s, struct sockaddr_un *from, socklen_t fromlen, printf("AKA-AUTS: Re-synchronized: " "SQN=%02x%02x%02x%02x%02x%02x\n", sqn[0], sqn[1], sqn[2], sqn[3], sqn[4], sqn[5]); +#ifdef CONFIG_SQLITE + db_update_milenage_sqn(m); +#endif /* CONFIG_SQLITE */ sqn_changes = 1; } } @@ -734,6 +891,13 @@ static void cleanup(void) close(serv_sock); unlink(socket_path); + +#ifdef CONFIG_SQLITE + if (sqlite_db) { + sqlite3_close(sqlite_db); + sqlite_db = NULL; + } +#endif /* CONFIG_SQLITE */ } @@ -753,7 +917,7 @@ static void usage(void) "usage:\n" "hlr_auc_gw [-hu] [-s<socket path>] [-g<triplet file>] " "[-m<milenage file>] \\\n" - " [-i<IND len in bits>]\n" + " [-D<DB file>] [-i<IND len in bits>]\n" "\n" "options:\n" " -h = show this usage help\n" @@ -762,6 +926,7 @@ static void usage(void) " (default: %s)\n" " -g<triplet file> = path for GSM authentication triplets\n" " -m<milenage file> = path for Milenage keys\n" + " -D<DB file> = path to SQLite database\n" " -i<IND len in bits> = IND length for SQN (default: 5)\n", default_socket_path); } @@ -771,6 +936,7 @@ int main(int argc, char *argv[]) { int c; char *gsm_triplet_file = NULL; + char *sqlite_db_file = NULL; if (os_program_init()) return -1; @@ -778,10 +944,18 @@ int main(int argc, char *argv[]) socket_path = default_socket_path; for (;;) { - c = getopt(argc, argv, "g:hi:m:s:u"); + c = getopt(argc, argv, "D:g:hi:m:s:u"); if (c < 0) break; switch (c) { + case 'D': +#ifdef CONFIG_SQLITE + sqlite_db_file = optarg; + break; +#else /* CONFIG_SQLITE */ + printf("No SQLite support included in the build\n"); + return -1; +#endif /* CONFIG_SQLITE */ case 'g': gsm_triplet_file = optarg; break; @@ -810,6 +984,16 @@ int main(int argc, char *argv[]) } } + if (!gsm_triplet_file && !milenage_file && !sqlite_db_file) { + usage(); + return -1; + } + +#ifdef CONFIG_SQLITE + if (sqlite_db_file && (sqlite_db = db_open(sqlite_db_file)) == NULL) + return -1; +#endif /* CONFIG_SQLITE */ + if (gsm_triplet_file && read_gsm_triplets(gsm_triplet_file) < 0) return -1; @@ -829,6 +1013,13 @@ int main(int argc, char *argv[]) for (;;) process(serv_sock); +#ifdef CONFIG_SQLITE + if (sqlite_db) { + sqlite3_close(sqlite_db); + sqlite_db = NULL; + } +#endif /* CONFIG_SQLITE */ + os_program_deinit(); return 0; diff --git a/hostapd/hlr_auc_gw.txt b/hostapd/hlr_auc_gw.txt new file mode 100644 index 00000000..e4b67831 --- /dev/null +++ b/hostapd/hlr_auc_gw.txt @@ -0,0 +1,98 @@ +HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator + +hlr_auc_gw is an example implementation of the EAP-SIM/AKA/AKA' +database/authentication gateway interface to HLR/AuC. It could be +replaced with an implementation of SS7 gateway to GSM/UMTS +authentication center (HLR/AuC). hostapd will send SIM/AKA +authentication queries over a UNIX domain socket to and external +program, e.g., hlr_auc_gw. + +hlr_auc_gw can be configured with GSM and UMTS authentication data with +text files: GSM triplet file (see hostapd.sim_db) and Milenage file (see +hlr_auc_gw.milenage_db). Milenage parameters can be used to generate +dynamic authentication data for EAP-SIM, EAP-AKA, and EAP-AKA' while the +GSM triplet data is used for a more static configuration (e.g., triplets +extracted from a SIM card). + +Alternatively, hlr_auc_gw can be built with support for an SQLite +database for more dynamic operations. This is enabled by adding +"CONFIG_SQLITE=y" into hostapd/.config before building hlr_auc_gw ("make +clean; make hlr_auc_gw" in this directory). + +hostapd is configured to use hlr_auc_gw with the eap_sim_db parameter in +hostapd.conf (e.g., "eap_sim_db=unix:/tmp/hlr_auc_gw.sock"). hlr_auc_gw +is configured with command line parameters: + +hlr_auc_gw [-hu] [-s<socket path>] [-g<triplet file>] [-m<milenage file>] \ + [-D<DB file>] [-i<IND len in bits>] + +options: + -h = show this usage help + -u = update SQN in Milenage file on exit + -s<socket path> = path for UNIX domain socket + (default: /tmp/hlr_auc_gw.sock) + -g<triplet file> = path for GSM authentication triplets + -m<milenage file> = path for Milenage keys + -D<DB file> = path to SQLite database + -i<IND len in bits> = IND length for SQN (default: 5) + + +The SQLite database can be initialized with sqlite, e.g., by running +following commands in "sqlite3 /path/to/hlr_auc_gw.db": + +CREATE TABLE milenage( + imsi INTEGER PRIMARY KEY NOT NULL, + ki CHAR(32) NOT NULL, + opc CHAR(32) NOT NULL, + amf CHAR(4) NOT NULL, + sqn CHAR(12) NOT NULL +); +INSERT INTO milenage(imsi,ki,opc,amf,sqn) VALUES( + 232010000000000, + '90dca4eda45b53cf0f12d7c9c3bc6a89', + 'cb9cccc4b9258e6dca4760379fb82581', + '61df', + '000000000000' +); +INSERT INTO milenage(imsi,ki,opc,amf,sqn) VALUES( + 555444333222111, + '5122250214c33e723a5dd523fc145fc0', + '981d464c7c52eb6e5036234984ad0bcf', + 'c3ab', + '16f3b3f70fc1' +); + + +"hlr_auc_gw -D /path/to/hlr_auc_gw.db" can then be used to fetch +Milenage parameters based on IMSI from the database. The database can be +updated dynamically while hlr_auc_gw is running to add/remove/modify +entries. + + +Example configuration files for hostapd to operate as a RADIUS +authentication server for EAP-SIM/AKA/AKA': + +hostapd.conf: + +driver=none +radius_server_clients=hostapd.radius_clients +eap_server=1 +eap_user_file=hostapd.eap_user +eap_sim_db=unix:/tmp/hlr_auc_gw.sock +eap_sim_aka_result_ind=1 + +hostapd.radius_clients: + +0.0.0.0/0 radius + +hostapd.eap_user: + +"0"* AKA +"1"* SIM +"2"* AKA +"3"* SIM +"4"* AKA +"5"* SIM +"6"* AKA' +"7"* AKA' +"8"* AKA' diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index daa03d21..5a2c2ea7 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -84,6 +84,11 @@ ctrl_interface_group=0 # SSID to be used in IEEE 802.11 management frames ssid=test +# Alternative formats for configuring SSID +# (double quoted string, hexdump, printf-escaped string) +#ssid2="test" +#ssid2=74657374 +#ssid2=P"hello\nthere" # Country code (ISO/IEC 3166-1). Used to set regulatory domain. # Set as needed to indicate country in which device is operating. @@ -196,6 +201,13 @@ auth_algs=3 # requests for broadcast SSID ignore_broadcast_ssid=0 +# Additional vendor specfic elements for Beacon and Probe Response frames +# This parameter can be used to add additional vendor specific element(s) into +# the end of the Beacon and Probe Response frames. The format for these +# element(s) is a hexdump of the raw information elements (id+len+payload for +# one or more elements) +#vendor_elements=dd0411223301 + # TX queue parameters (EDCF / bursting) # tx_queue_<queue name>_<param> # queues: data0, data1, data2, data3, after_beacon, beacon @@ -489,7 +501,7 @@ wmm_ac_vo_acm=0 # else reserved (default) # # Number of Sounding Dimensions: [SOUNDING-DIMENSION-2] -# Beamformer’s capability indicating the maximum value of the NUM_STS parameter +# 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 # else reserved (default) @@ -522,7 +534,7 @@ wmm_ac_vo_acm=0 # Indicates the maximum length of A-MPDU pre-EOF padding that the STA can recv # This field is an integer in the range of 0 to 7. # The length defined by this field is equal to -# 2 pow(13 + Maximum A-MPDU Length Exponent) –1 octets +# 2 pow(13 + Maximum A-MPDU Length Exponent) -1 octets # # VHT Link Adaptation Capable: [VHT-LINK-ADAPT2] [VHT-LINK-ADAPT3] # Indicates whether or not the STA supports link adaptation using VHT variant @@ -545,7 +557,21 @@ wmm_ac_vo_acm=0 # 0 = Tx antenna pattern might change during the lifetime of an association # 1 = Tx antenna pattern does not change during the lifetime of an association #vht_capab=[SHORT-GI-80][HTC-VHT] +# +# Require stations to support VHT PHY (reject association if they do not) +#require_vht=1 + +# 0 = 20 or 40 MHz operating Channel width +# 1 = 80 MHz channel width +# 2 = 160 MHz channel width +# 3 = 80+80 MHz channel width #vht_oper_chwidth=1 +# +# center freq = 5 GHz + (5 * index) +# So index 42 gives center freq 5.210 GHz +# which is channel 42 in 5G band +# +#vht_oper_centr_freq_seg0_idx=42 ##### IEEE 802.1X-2004 related configuration ################################## @@ -796,6 +822,12 @@ own_ip_addr=127.0.0.1 # to the bridge. #vlan_tagged_interface=eth0 +# When hostapd creates a VLAN interface on vlan_tagged_interfaces, it needs +# to know how to name it. +# 0 = vlan<XXX>, e.g., vlan1 +# 1 = <vlan_tagged_interface>.<XXX>, e.g. eth0.1 +#vlan_naming=0 + # Arbitrary RADIUS attributes can be added into Access-Request and # Accounting-Request packets by specifying the contents of the attributes with # the following configuration parameters. There can be multiple of these to @@ -1311,7 +1343,7 @@ own_ip_addr=127.0.0.1 # Arbitrary number of Roaming Consortium OIs can be configured with each line # adding a new OI to the list. The first three entries are available through # Beacon and Probe Response frames. Any additional entry will be available only -# through ANQP queries. Each OI is between 3 and 15 octets and is configured a +# through ANQP queries. Each OI is between 3 and 15 octets and is configured as # a hexstring. #roaming_consortium=021122 #roaming_consortium=2233445566 @@ -1325,6 +1357,124 @@ own_ip_addr=127.0.0.1 #venue_name=eng:Example venue #venue_name=fin:Esimerkkipaikka +# Network Authentication Type +# This parameter indicates what type of network authentication is used in the +# network. +# format: <network auth type indicator (1-octet hex str)> [redirect URL] +# Network Authentication Type Indicator values: +# 00 = Acceptance of terms and conditions +# 01 = On-line enrollment supported +# 02 = http/https redirection +# 03 = DNS redirection +#network_auth_type=00 +#network_auth_type=02http://www.example.com/redirect/me/here/ + +# IP Address Type Availability +# format: <1-octet encoded value as hex str> +# (ipv4_type & 0x3f) << 2 | (ipv6_type & 0x3) +# ipv4_type: +# 0 = Address type not available +# 1 = Public IPv4 address available +# 2 = Port-restricted IPv4 address available +# 3 = Single NATed private IPv4 address available +# 4 = Double NATed private IPv4 address available +# 5 = Port-restricted IPv4 address and single NATed IPv4 address available +# 6 = Port-restricted IPv4 address and double NATed IPv4 address available +# 7 = Availability of the address type is not known +# ipv6_type: +# 0 = Address type not available +# 1 = Address type available +# 2 = Availability of the address type not known +#ipaddr_type_availability=14 + +# Domain Name +# format: <variable-octet str>[,<variable-octet str>] +#domain_name=example.com,another.example.com,yet-another.example.com + +# 3GPP Cellular Network information +# format: <MCC1,MNC1>[;<MCC2,MNC2>][;...] +#anqp_3gpp_cell_net=244,91;310,026;234,56 + +# NAI Realm information +# One or more realm can be advertised. Each nai_realm line adds a new realm to +# the set. These parameters provide information for stations using Interworking +# network selection to allow automatic connection to a network based on +# credentials. +# format: <encoding>,<NAI Realm(s)>[,<EAP Method 1>][,<EAP Method 2>][,...] +# encoding: +# 0 = Realm formatted in accordance with IETF RFC 4282 +# 1 = UTF-8 formatted character string that is not formatted in +# accordance with IETF RFC 4282 +# NAI Realm(s): Semi-colon delimited NAI Realm(s) +# EAP Method: <EAP Method>[:<[AuthParam1:Val1]>][<[AuthParam2:Val2]>][...] +# AuthParam (Table 8-188 in IEEE Std 802.11-2012): +# ID 2 = Non-EAP Inner Authentication Type +# 1 = PAP, 2 = CHAP, 3 = MSCHAP, 4 = MSCHAPV2 +# ID 3 = Inner authentication EAP Method Type +# ID 5 = Credential Type +# 1 = SIM, 2 = USIM, 3 = NFC Secure Element, 4 = Hardware Token, +# 5 = Softoken, 6 = Certificate, 7 = username/password, 9 = Anonymous, +# 10 = Vendor Specific +#nai_realm=0,example.com;example.net +# EAP methods EAP-TLS with certificate and EAP-TTLS/MSCHAPv2 with +# username/password +#nai_realm=0,example.org,13[5:6],21[2:4][5:7] + +##### Hotspot 2.0 ############################################################# + +# Enable Hotspot 2.0 support +#hs20=1 + +# Disable Downstream Group-Addressed Forwarding (DGAF) +# This can be used to configure a network where no group-addressed frames are +# allowed. The AP will not forward any group-address frames to the stations and +# random GTKs are issued for each station to prevent associated stations from +# forging such frames to other stations in the BSS. +#disable_dgaf=1 + +# Operator Friendly Name +# This parameter can be used to configure one or more Operator Friendly Name +# Duples. Each entry has a two or three character language code (ISO-639) +# separated by colon from the operator friendly name string. +#hs20_oper_friendly_name=eng:Example operator +#hs20_oper_friendly_name=fin:Esimerkkioperaattori + +# Connection Capability +# This can be used to advertise what type of IP traffic can be sent through the +# hotspot (e.g., due to firewall allowing/blocking protocols/ports). +# format: <IP Protocol>:<Port Number>:<Status> +# IP Protocol: 1 = ICMP, 6 = TCP, 17 = UDP +# Port Number: 0..65535 +# Status: 0 = Closed, 1 = Open, 2 = Unknown +# Each hs20_conn_capab line is added to the list of advertised tuples. +#hs20_conn_capab=1:0:2 +#hs20_conn_capab=6:22:1 +#hs20_conn_capab=17:5060:0 + +# WAN Metrics +# format: <WAN Info>:<DL Speed>:<UL Speed>:<DL Load>:<UL Load>:<LMD> +# WAN Info: B0-B1: Link Status, B2: Symmetric Link, B3: At Capabity +# (encoded as two hex digits) +# Link Status: 1 = Link up, 2 = Link down, 3 = Link in test state +# Downlink Speed: Estimate of WAN backhaul link current downlink speed in kbps; +# 1..4294967295; 0 = unknown +# Uplink Speed: Estimate of WAN backhaul link current uplink speed in kbps +# 1..4294967295; 0 = unknown +# Downlink Load: Current load of downlink WAN connection (scaled to 255 = 100%) +# Uplink Load: Current load of uplink WAN connection (scaled to 255 = 100%) +# Load Measurement Duration: Duration for measuring downlink/uplink load in +# tenths of a second (1..65535); 0 if load cannot be determined +#hs20_wan_metrics=01:8000:1000:80:240:3000 + +# Operating Class Indication +# List of operating classes the BSSes in this ESS use. The Global operating +# classes in Table E-4 of IEEE Std 802.11-2012 Annex E define the values that +# can be used in this. +# format: hexdump of operating class octets +# for example, operating classes 81 (2.4 GHz channels 1-13) and 115 (5 GHz +# channels 36-48): +#hs20_operating_class=5173 + ##### Multiple BSSID support ################################################## # # Above configuration is using the default interface (wlan#, or multi-SSID VLAN diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c index 0c33d5bc..aed981c4 100644 --- a/hostapd/hostapd_cli.c +++ b/hostapd/hostapd_cli.c @@ -71,6 +71,7 @@ static const char *commands_help = " wps_pin <uuid> <pin> [timeout] [addr] add WPS Enrollee PIN\n" " wps_check_pin <PIN> verify PIN checksum\n" " wps_pbc indicate button pushed to initiate PBC\n" +" wps_cancel cancel the pending WPS operation\n" #ifdef CONFIG_WPS_OOB " wps_oob <type> <path> <method> use WPS with out-of-band (UFD)\n" #endif /* CONFIG_WPS_OOB */ @@ -977,7 +978,7 @@ static void hostapd_cli_interactive(void) eloop_register_signal_terminate(hostapd_cli_eloop_terminate, NULL); edit_init(hostapd_cli_edit_cmd_cb, hostapd_cli_edit_eof_cb, - NULL, NULL, NULL); + NULL, NULL, NULL, NULL); eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL); eloop_run(); diff --git a/hostapd/main.c b/hostapd/main.c index d8c27765..56f00023 100644 --- a/hostapd/main.c +++ b/hostapd/main.c @@ -166,14 +166,9 @@ static struct hostapd_iface * hostapd_init(const char *config_file) if (hapd_iface == NULL) goto fail; - hapd_iface->reload_config = hostapd_reload_config; - hapd_iface->config_read_cb = hostapd_config_read; hapd_iface->config_fname = os_strdup(config_file); if (hapd_iface->config_fname == NULL) goto fail; - hapd_iface->ctrl_iface_init = hostapd_ctrl_iface_init; - hapd_iface->ctrl_iface_deinit = hostapd_ctrl_iface_deinit; - hapd_iface->for_each_interface = hostapd_for_each_interface; conf = hostapd_config_read(hapd_iface->config_fname); if (conf == NULL) @@ -181,7 +176,7 @@ static struct hostapd_iface * hostapd_init(const char *config_file) hapd_iface->conf = conf; hapd_iface->num_bss = conf->num_bss; - hapd_iface->bss = os_zalloc(conf->num_bss * + hapd_iface->bss = os_calloc(conf->num_bss, sizeof(struct hostapd_data *)); if (hapd_iface->bss == NULL) goto fail; @@ -248,13 +243,13 @@ static int hostapd_driver_init(struct hostapd_iface *iface) } params.bssid = b; params.ifname = hapd->conf->iface; - params.ssid = (const u8 *) hapd->conf->ssid.ssid; + params.ssid = hapd->conf->ssid.ssid; params.ssid_len = hapd->conf->ssid.ssid_len; params.test_socket = hapd->conf->test_socket; params.use_pae_group_addr = hapd->conf->use_pae_group_addr; params.num_bridge = hapd->iface->num_bss; - params.bridge = os_zalloc(hapd->iface->num_bss * sizeof(char *)); + params.bridge = os_calloc(hapd->iface->num_bss, sizeof(char *)); if (params.bridge == NULL) return -1; for (i = 0; i < hapd->iface->num_bss; i++) { @@ -284,21 +279,6 @@ static int hostapd_driver_init(struct hostapd_iface *iface) } -static void hostapd_interface_deinit_free(struct hostapd_iface *iface) -{ - const struct wpa_driver_ops *driver; - void *drv_priv; - if (iface == NULL) - return; - driver = iface->bss[0]->driver; - drv_priv = iface->bss[0]->drv_priv; - hostapd_interface_deinit(iface); - if (driver && driver->hapd_deinit && drv_priv) - driver->hapd_deinit(drv_priv); - hostapd_interface_free(iface); -} - - static struct hostapd_iface * hostapd_interface_init(struct hapd_interfaces *interfaces, const char *config_fname, int debug) @@ -411,7 +391,7 @@ static int hostapd_global_init(struct hapd_interfaces *interfaces, wpa_printf(MSG_ERROR, "No drivers enabled"); return -1; } - global.drv_priv = os_zalloc(global.drv_count * sizeof(void *)); + global.drv_priv = os_calloc(global.drv_count, sizeof(void *)); if (global.drv_priv == NULL) return -1; @@ -499,13 +479,15 @@ static void usage(void) fprintf(stderr, "\n" "usage: hostapd [-hdBKtv] [-P <PID file>] [-e <entropy file>] " - "<configuration file(s)>\n" + "\\\n" + " [-g <global ctrl_iface>] <configuration file(s)>\n" "\n" "options:\n" " -h show this usage\n" " -d show more debug messages (-dd for even more)\n" " -B run daemon in the background\n" " -e entropy file\n" + " -g global control interface path\n" " -P PID file\n" " -K include key data in debug messages\n" #ifdef CONFIG_DEBUG_FILE @@ -527,6 +509,28 @@ static const char * hostapd_msg_ifname_cb(void *ctx) } +static int hostapd_get_global_ctrl_iface(struct hapd_interfaces *interfaces, + const char *path) +{ + char *pos; + os_free(interfaces->global_iface_path); + interfaces->global_iface_path = os_strdup(path); + if (interfaces->global_iface_path == NULL) + return -1; + pos = os_strrchr(interfaces->global_iface_path, '/'); + if (pos == NULL) { + os_free(interfaces->global_iface_path); + interfaces->global_iface_path = NULL; + return -1; + } + + *pos = '\0'; + interfaces->global_iface_name = pos + 1; + + return 0; +} + + int main(int argc, char *argv[]) { struct hapd_interfaces interfaces; @@ -540,8 +544,19 @@ int main(int argc, char *argv[]) if (os_program_init()) return -1; + os_memset(&interfaces, 0, sizeof(interfaces)); + interfaces.reload_config = hostapd_reload_config; + interfaces.config_read_cb = hostapd_config_read; + interfaces.for_each_interface = hostapd_for_each_interface; + interfaces.ctrl_iface_init = hostapd_ctrl_iface_init; + interfaces.ctrl_iface_deinit = hostapd_ctrl_iface_deinit; + interfaces.driver_init = hostapd_driver_init; + interfaces.global_iface_path = NULL; + interfaces.global_iface_name = NULL; + interfaces.global_ctrl_sock = -1; + for (;;) { - c = getopt(argc, argv, "Bde:f:hKP:tv"); + c = getopt(argc, argv, "Bde:f:hKP:tvg:"); if (c < 0) break; switch (c) { @@ -576,6 +591,9 @@ int main(int argc, char *argv[]) show_version(); exit(1); break; + case 'g': + hostapd_get_global_ctrl_iface(&interfaces, optarg); + break; default: usage(); @@ -583,7 +601,7 @@ int main(int argc, char *argv[]) } } - if (optind == argc) + if (optind == argc && interfaces.global_iface_path == NULL) usage(); wpa_msg_register_ifname_cb(hostapd_msg_ifname_cb); @@ -592,11 +610,13 @@ int main(int argc, char *argv[]) wpa_debug_open_file(log_file); interfaces.count = argc - optind; - interfaces.iface = os_zalloc(interfaces.count * - sizeof(struct hostapd_iface *)); - if (interfaces.iface == NULL) { - wpa_printf(MSG_ERROR, "malloc failed"); - return -1; + if (interfaces.count) { + interfaces.iface = os_calloc(interfaces.count, + sizeof(struct hostapd_iface *)); + if (interfaces.iface == NULL) { + wpa_printf(MSG_ERROR, "malloc failed"); + return -1; + } } if (hostapd_global_init(&interfaces, entropy_file)) @@ -611,12 +631,15 @@ int main(int argc, char *argv[]) goto out; } + hostapd_global_ctrl_iface_init(&interfaces); + if (hostapd_global_run(&interfaces, daemonize, pid_file)) goto out; ret = 0; out: + hostapd_global_ctrl_iface_deinit(&interfaces); /* Deinitialize all interfaces */ for (i = 0; i < interfaces.count; i++) hostapd_interface_deinit_free(interfaces.iface[i]); diff --git a/src/ap/accounting.c b/src/ap/accounting.c index 2c3a6d9f..7563b52e 100644 --- a/src/ap/accounting.c +++ b/src/ap/accounting.c @@ -28,6 +28,8 @@ static void accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta); +static void accounting_sta_interim(struct hostapd_data *hapd, + struct sta_info *sta); static struct radius_msg * accounting_msg(struct hostapd_data *hapd, @@ -40,7 +42,6 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, size_t len; int i; struct wpabuf *b; - struct hostapd_radius_attr *attr; msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST, radius_client_get_id(hapd->radius)); @@ -80,7 +81,17 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, } if (sta) { + /* Use 802.1X identity if available */ val = ieee802_1x_get_identity(sta->eapol_sm, &len); + + /* Use RADIUS ACL identity if 802.1X provides no identity */ + if (!val && sta->identity) { + val = (u8 *) sta->identity; + len = os_strlen(sta->identity); + } + + /* Use STA MAC if neither 802.1X nor RADIUS ACL provided + * identity */ if (!val) { os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, MAC2STR(sta->addr)); @@ -95,86 +106,11 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, } } - if (!hostapd_config_get_radius_attr(hapd->conf->radius_acct_req_attr, - RADIUS_ATTR_NAS_IP_ADDRESS) && - hapd->conf->own_ip_addr.af == AF_INET && - !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS, - (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) { - printf("Could not add NAS-IP-Address\n"); - goto fail; - } - -#ifdef CONFIG_IPV6 - if (!hostapd_config_get_radius_attr(hapd->conf->radius_acct_req_attr, - RADIUS_ATTR_NAS_IPV6_ADDRESS) && - hapd->conf->own_ip_addr.af == AF_INET6 && - !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS, - (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) { - printf("Could not add NAS-IPv6-Address\n"); + if (add_common_radius_attr(hapd, hapd->conf->radius_acct_req_attr, sta, + msg) < 0) goto fail; - } -#endif /* CONFIG_IPV6 */ - - if (!hostapd_config_get_radius_attr(hapd->conf->radius_acct_req_attr, - RADIUS_ATTR_NAS_IDENTIFIER) && - hapd->conf->nas_identifier && - !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER, - (u8 *) hapd->conf->nas_identifier, - os_strlen(hapd->conf->nas_identifier))) { - printf("Could not add NAS-Identifier\n"); - goto fail; - } - - if (!hostapd_config_get_radius_attr(hapd->conf->radius_acct_req_attr, - RADIUS_ATTR_NAS_PORT) && - sta && - !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) { - printf("Could not add NAS-Port\n"); - goto fail; - } - - os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", - MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid); - if (!hostapd_config_get_radius_attr(hapd->conf->radius_acct_req_attr, - RADIUS_ATTR_CALLED_STATION_ID) && - !radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID, - (u8 *) buf, os_strlen(buf))) { - printf("Could not add Called-Station-Id\n"); - goto fail; - } if (sta) { - os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, - MAC2STR(sta->addr)); - if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID, - (u8 *) buf, os_strlen(buf))) { - printf("Could not add Calling-Station-Id\n"); - goto fail; - } - - if (!hostapd_config_get_radius_attr( - hapd->conf->radius_acct_req_attr, - RADIUS_ATTR_NAS_PORT_TYPE) && - !radius_msg_add_attr_int32( - msg, RADIUS_ATTR_NAS_PORT_TYPE, - RADIUS_NAS_PORT_TYPE_IEEE_802_11)) { - printf("Could not add NAS-Port-Type\n"); - goto fail; - } - - os_snprintf(buf, sizeof(buf), "CONNECT %d%sMbps %s", - radius_sta_rate(hapd, sta) / 2, - (radius_sta_rate(hapd, sta) & 1) ? ".5" : "", - radius_mode_txt(hapd)); - if (!hostapd_config_get_radius_attr( - hapd->conf->radius_acct_req_attr, - RADIUS_ATTR_CONNECT_INFO) && - !radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, - (u8 *) buf, os_strlen(buf))) { - printf("Could not add Connect-Info\n"); - goto fail; - } - for (i = 0; ; i++) { val = ieee802_1x_get_radius_class(sta->eapol_sm, &len, i); @@ -196,15 +132,13 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, wpa_printf(MSG_ERROR, "Could not add CUI"); goto fail; } - } - for (attr = hapd->conf->radius_acct_req_attr; attr; attr = attr->next) - { - if (!radius_msg_add_attr(msg, attr->type, - wpabuf_head(attr->val), - wpabuf_len(attr->val))) { - wpa_printf(MSG_ERROR, "Could not add RADIUS " - "attribute"); + if (!b && sta->radius_cui && + !radius_msg_add_attr(msg, + RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, + (u8 *) sta->radius_cui, + os_strlen(sta->radius_cui))) { + wpa_printf(MSG_ERROR, "Could not add CUI from ACL"); goto fail; } } @@ -415,7 +349,8 @@ static void accounting_sta_report(struct hostapd_data *hapd, * @hapd: hostapd BSS data * @sta: The station */ -void accounting_sta_interim(struct hostapd_data *hapd, struct sta_info *sta) +static void accounting_sta_interim(struct hostapd_data *hapd, + struct sta_info *sta) { if (sta->acct_session_started) accounting_sta_report(hapd, sta, 0); diff --git a/src/ap/accounting.h b/src/ap/accounting.h index 797e24d7..9d13d011 100644 --- a/src/ap/accounting.h +++ b/src/ap/accounting.h @@ -9,7 +9,6 @@ #ifndef ACCOUNTING_H #define ACCOUNTING_H -void accounting_sta_interim(struct hostapd_data *hapd, struct sta_info *sta); #ifdef CONFIG_NO_ACCOUNTING static inline void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta) diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index 2af2a8e5..fd2d4d56 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -104,9 +104,9 @@ struct hostapd_config * hostapd_config_defaults(void) const struct hostapd_wmm_ac_params ac_be = { aCWmin, aCWmax, 3, 0, 0 }; /* best effort traffic */ const struct hostapd_wmm_ac_params ac_vi = /* video traffic */ - { aCWmin - 1, aCWmin, 2, 3000 / 32, 1 }; + { aCWmin - 1, aCWmin, 2, 3000 / 32, 0 }; const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */ - { aCWmin - 2, aCWmin - 1, 2, 1500 / 32, 1 }; + { aCWmin - 2, aCWmin - 1, 2, 1500 / 32, 0 }; const struct hostapd_tx_queue_params txq_bk = { 7, ecw2cw(aCWmin), ecw2cw(aCWmax), 0 }; const struct hostapd_tx_queue_params txq_be = @@ -506,10 +506,23 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(conf->roaming_consortium); os_free(conf->venue_name); + os_free(conf->nai_realm_data); + os_free(conf->network_auth_type); + os_free(conf->anqp_3gpp_cell_net); + os_free(conf->domain_name); #ifdef CONFIG_RADIUS_TEST os_free(conf->dump_msk_file); #endif /* CONFIG_RADIUS_TEST */ + +#ifdef CONFIG_HS20 + os_free(conf->hs20_oper_friendly_name); + os_free(conf->hs20_wan_metrics); + os_free(conf->hs20_connection_capability); + os_free(conf->hs20_operating_class); +#endif /* CONFIG_HS20 */ + + wpabuf_free(conf->vendor_elements); } diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index ca4fe58c..f5e4a6a5 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -12,6 +12,7 @@ #include "common/defs.h" #include "ip_addr.h" #include "common/wpa_common.h" +#include "common/ieee802_11_common.h" #include "wps/wps.h" #define MAX_STA_COUNT 2007 @@ -48,7 +49,7 @@ typedef enum hostap_security_policy { } secpolicy; struct hostapd_ssid { - char ssid[HOSTAPD_MAX_SSID_LEN + 1]; + u8 ssid[HOSTAPD_MAX_SSID_LEN]; size_t ssid_len; int ssid_set; @@ -65,6 +66,10 @@ struct hostapd_ssid { #define DYNAMIC_VLAN_OPTIONAL 1 #define DYNAMIC_VLAN_REQUIRED 2 int dynamic_vlan; +#define DYNAMIC_VLAN_NAMING_WITHOUT_DEVICE 0 +#define DYNAMIC_VLAN_NAMING_WITH_DEVICE 1 +#define DYNAMIC_VLAN_NAMING_END 2 + int vlan_naming; #ifdef CONFIG_FULL_DYNAMIC_VLAN char *vlan_tagged_interface; #endif /* CONFIG_FULL_DYNAMIC_VLAN */ @@ -132,14 +137,6 @@ struct hostapd_tx_queue_params { int burst; /* maximum burst time in 0.1 ms, i.e., 10 = 1 ms */ }; -struct hostapd_wmm_ac_params { - int cwmin; - int cwmax; - int aifs; - int txop_limit; /* in units of 32us */ - int admission_control_mandatory; -}; - #define MAX_ROAMING_CONSORTIUM_LEN 15 @@ -148,12 +145,29 @@ struct hostapd_roaming_consortium { u8 oi[MAX_ROAMING_CONSORTIUM_LEN]; }; -struct hostapd_venue_name { +struct hostapd_lang_string { u8 lang[3]; u8 name_len; u8 name[252]; }; +#define MAX_NAI_REALMS 10 +#define MAX_NAI_REALMLEN 255 +#define MAX_NAI_EAP_METHODS 5 +#define MAX_NAI_AUTH_TYPES 4 +struct hostapd_nai_realm_data { + u8 encoding; + char realm_buf[MAX_NAI_REALMLEN + 1]; + char *realm[MAX_NAI_REALMS]; + u8 eap_method_count; + struct hostapd_nai_realm_eap { + u8 eap_method; + u8 num_auths; + u8 auth_id[MAX_NAI_AUTH_TYPES]; + u8 auth_val[MAX_NAI_AUTH_TYPES]; + } eap_method[MAX_NAI_EAP_METHODS]; +}; + /** * struct hostapd_bss_config - Per-BSS configuration */ @@ -389,16 +403,49 @@ struct hostapd_bss_config { /* IEEE 802.11u - Venue Name duples */ unsigned int venue_name_count; - struct hostapd_venue_name *venue_name; + struct hostapd_lang_string *venue_name; + + /* IEEE 802.11u - Network Authentication Type */ + u8 *network_auth_type; + size_t network_auth_type_len; + + /* IEEE 802.11u - IP Address Type Availability */ + u8 ipaddr_type_availability; + u8 ipaddr_type_configured; + + /* IEEE 802.11u - 3GPP Cellular Network */ + u8 *anqp_3gpp_cell_net; + size_t anqp_3gpp_cell_net_len; + + /* IEEE 802.11u - Domain Name */ + u8 *domain_name; + size_t domain_name_len; + + unsigned int nai_realm_count; + struct hostapd_nai_realm_data *nai_realm_data; u16 gas_comeback_delay; int gas_frag_limit; +#ifdef CONFIG_HS20 + int hs20; + int disable_dgaf; + unsigned int hs20_oper_friendly_name_count; + struct hostapd_lang_string *hs20_oper_friendly_name; + u8 *hs20_wan_metrics; + u8 *hs20_connection_capability; + size_t hs20_connection_capability_len; + u8 *hs20_operating_class; + u8 hs20_operating_class_len; +#endif /* CONFIG_HS20 */ + u8 wps_rf_bands; /* RF bands for WPS (WPS_RF_*) */ #ifdef CONFIG_RADIUS_TEST char *dump_msk_file; #endif /* CONFIG_RADIUS_TEST */ + + struct wpabuf *vendor_elements; }; @@ -455,7 +502,9 @@ struct hostapd_config { int require_ht; u32 vht_capab; int ieee80211ac; + int require_vht; u8 vht_oper_chwidth; + u8 vht_oper_centr_freq_seg0_idx; }; diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c index 859b529b..02da25b7 100644 --- a/src/ap/ap_drv_ops.c +++ b/src/ap/ap_drv_ops.c @@ -12,11 +12,13 @@ #include "drivers/driver.h" #include "common/ieee802_11_defs.h" #include "wps/wps.h" +#include "p2p/p2p.h" #include "hostapd.h" #include "ieee802_11.h" #include "sta_info.h" #include "ap_config.h" #include "p2p_hostapd.h" +#include "hs20.h" #include "ap_drv_ops.h" @@ -147,6 +149,30 @@ int hostapd_build_ap_extra_ies(struct hostapd_data *hapd, } #endif /* CONFIG_P2P_MANAGER */ +#ifdef CONFIG_WIFI_DISPLAY + if (hapd->p2p_group) { + struct wpabuf *a; + a = p2p_group_assoc_resp_ie(hapd->p2p_group, P2P_SC_SUCCESS); + if (a && wpabuf_resize(&assocresp, wpabuf_len(a)) == 0) + wpabuf_put_buf(assocresp, a); + wpabuf_free(a); + } +#endif /* CONFIG_WIFI_DISPLAY */ + +#ifdef CONFIG_HS20 + pos = buf; + pos = hostapd_eid_hs20_indication(hapd, pos); + if (pos != buf) { + if (wpabuf_resize(&beacon, pos - buf) != 0) + goto fail; + wpabuf_put_data(beacon, buf, pos - buf); + + if (wpabuf_resize(&proberesp, pos - buf) != 0) + goto fail; + wpabuf_put_data(proberesp, buf, pos - buf); + } +#endif /* CONFIG_HS20 */ + *beacon_ret = beacon; *proberesp_ret = proberesp; *assocresp_ret = assocresp; @@ -586,6 +612,16 @@ int hostapd_drv_sta_disassoc(struct hostapd_data *hapd, } +int hostapd_drv_wnm_oper(struct hostapd_data *hapd, enum wnm_oper oper, + const u8 *peer, u8 *buf, u16 *buf_len) +{ + if (hapd->driver == NULL || hapd->driver->wnm_oper == NULL) + return 0; + return hapd->driver->wnm_oper(hapd->drv_priv, oper, peer, buf, + buf_len); +} + + int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq, unsigned int wait, const u8 *dst, const u8 *data, size_t len) diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h index 169c91b7..9c53b99d 100644 --- a/src/ap/ap_drv_ops.h +++ b/src/ap/ap_drv_ops.h @@ -101,6 +101,10 @@ int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr, #include "drivers/driver.h" +int hostapd_drv_wnm_oper(struct hostapd_data *hapd, + enum wnm_oper oper, const u8 *peer, + u8 *buf, u16 *buf_len); + static inline int hostapd_drv_set_countermeasures(struct hostapd_data *hapd, int enabled) { diff --git a/src/ap/ap_list.c b/src/ap/ap_list.c index 933b1585..18090ca1 100644 --- a/src/ap/ap_list.c +++ b/src/ap/ap_list.c @@ -251,23 +251,9 @@ void ap_list_process_beacon(struct hostapd_iface *iface, ap->ssid_len = len; } - os_memset(ap->supported_rates, 0, WLAN_SUPP_RATES_MAX); - len = 0; - if (elems->supp_rates) { - len = elems->supp_rates_len; - if (len > WLAN_SUPP_RATES_MAX) - len = WLAN_SUPP_RATES_MAX; - os_memcpy(ap->supported_rates, elems->supp_rates, len); - } - if (elems->ext_supp_rates) { - int len2; - if (len + elems->ext_supp_rates_len > WLAN_SUPP_RATES_MAX) - len2 = WLAN_SUPP_RATES_MAX - len; - else - len2 = elems->ext_supp_rates_len; - os_memcpy(ap->supported_rates + len, elems->ext_supp_rates, - len2); - } + merge_byte_arrays(ap->supported_rates, WLAN_SUPP_RATES_MAX, + elems->supp_rates, elems->supp_rates_len, + elems->ext_supp_rates, elems->ext_supp_rates_len); ap->wpa = elems->wpa_ie != NULL; diff --git a/src/ap/beacon.c b/src/ap/beacon.c index 0f29ccdb..2f813f37 100644 --- a/src/ap/beacon.c +++ b/src/ap/beacon.c @@ -33,6 +33,7 @@ #include "p2p_hostapd.h" #include "ap_drv_ops.h" #include "beacon.h" +#include "hs20.h" #ifdef NEED_AP_MLME @@ -205,6 +206,8 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, if (hapd->p2p_probe_resp_ie) buflen += wpabuf_len(hapd->p2p_probe_resp_ie); #endif /* CONFIG_P2P */ + if (hapd->conf->vendor_elements) + buflen += wpabuf_len(hapd->conf->vendor_elements); resp = os_zalloc(buflen); if (resp == NULL) return NULL; @@ -292,6 +295,16 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, pos = hostapd_eid_p2p_manage(hapd, pos); #endif /* CONFIG_P2P_MANAGER */ +#ifdef CONFIG_HS20 + pos = hostapd_eid_hs20_indication(hapd, pos); +#endif /* CONFIG_HS20 */ + + if (hapd->conf->vendor_elements) { + os_memcpy(pos, wpabuf_head(hapd->conf->vendor_elements), + wpabuf_len(hapd->conf->vendor_elements)); + pos += wpabuf_len(hapd->conf->vendor_elements); + } + *resp_len = pos - (u8 *) resp; return (u8 *) resp; } @@ -523,6 +536,8 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd) if (hapd->p2p_beacon_ie) tail_len += wpabuf_len(hapd->p2p_beacon_ie); #endif /* CONFIG_P2P */ + if (hapd->conf->vendor_elements) + tail_len += wpabuf_len(hapd->conf->vendor_elements); tailpos = tail = os_malloc(tail_len); if (head == NULL || tail == NULL) { wpa_printf(MSG_ERROR, "Failed to set beacon data"); @@ -629,6 +644,16 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd) tailpos = hostapd_eid_p2p_manage(hapd, tailpos); #endif /* CONFIG_P2P_MANAGER */ +#ifdef CONFIG_HS20 + tailpos = hostapd_eid_hs20_indication(hapd, tailpos); +#endif /* CONFIG_HS20 */ + + if (hapd->conf->vendor_elements) { + os_memcpy(tailpos, wpabuf_head(hapd->conf->vendor_elements), + wpabuf_len(hapd->conf->vendor_elements)); + tailpos += wpabuf_len(hapd->conf->vendor_elements); + } + tail_len = tailpos > tail ? tailpos - tail : 0; resp = hostapd_probe_resp_offloads(hapd, &resp_len); @@ -644,7 +669,7 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd) params.dtim_period = hapd->conf->dtim_period; params.beacon_int = hapd->iconf->beacon_int; params.basic_rates = hapd->iconf->basic_rates; - params.ssid = (u8 *) hapd->conf->ssid.ssid; + params.ssid = hapd->conf->ssid.ssid; params.ssid_len = hapd->conf->ssid.ssid_len; params.pairwise_ciphers = hapd->conf->rsn_pairwise ? hapd->conf->rsn_pairwise : hapd->conf->wpa_pairwise; @@ -694,6 +719,9 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd) params.hessid = hapd->conf->hessid; params.access_network_type = hapd->conf->access_network_type; params.ap_max_inactivity = hapd->conf->ap_max_inactivity; +#ifdef CONFIG_HS20 + params.disable_dgaf = hapd->conf->disable_dgaf; +#endif /* CONFIG_HS20 */ if (hostapd_drv_set_ap(hapd, ¶ms)) wpa_printf(MSG_ERROR, "Failed to set beacon parameters"); hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp); diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c index cf06a4fb..23fa241a 100644 --- a/src/ap/drv_callbacks.c +++ b/src/ap/drv_callbacks.c @@ -16,6 +16,7 @@ #include "crypto/random.h" #include "p2p/p2p.h" #include "wps/wps.h" +#include "wnm_ap.h" #include "hostapd.h" #include "ieee802_11.h" #include "sta_info.h" @@ -37,7 +38,12 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, struct ieee802_11_elems elems; const u8 *ie; size_t ielen; +#ifdef CONFIG_IEEE80211R + u8 buf[sizeof(struct ieee80211_mgmt) + 1024]; + u8 *p = buf; +#endif /* CONFIG_IEEE80211R */ u16 reason = WLAN_REASON_UNSPECIFIED; + u16 status = WLAN_STATUS_SUCCESS; if (addr == NULL) { /* @@ -146,27 +152,85 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, return -1; } res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, - ie, ielen, NULL, 0); + 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_hexdump(MSG_DEBUG, "IE", ie, ielen); - if (res == WPA_INVALID_GROUP) + if (res == WPA_INVALID_GROUP) { reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID; - else if (res == WPA_INVALID_PAIRWISE) + status = WLAN_STATUS_GROUP_CIPHER_NOT_VALID; + } else if (res == WPA_INVALID_PAIRWISE) { reason = WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID; - else if (res == WPA_INVALID_AKMP) + status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID; + } else if (res == WPA_INVALID_AKMP) { reason = WLAN_REASON_AKMP_NOT_VALID; + status = WLAN_STATUS_AKMP_NOT_VALID; + } #ifdef CONFIG_IEEE80211W - else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION) + else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION) { reason = WLAN_REASON_INVALID_IE; - else if (res == WPA_INVALID_MGMT_GROUP_CIPHER) + status = WLAN_STATUS_INVALID_IE; + } else if (res == WPA_INVALID_MGMT_GROUP_CIPHER) { reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID; + status = WLAN_STATUS_GROUP_CIPHER_NOT_VALID; + } #endif /* CONFIG_IEEE80211W */ - else + else { reason = WLAN_REASON_INVALID_IE; + status = WLAN_STATUS_INVALID_IE; + } goto fail; } +#ifdef CONFIG_IEEE80211W + if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out && + sta->sa_query_count > 0) + ap_check_sa_query_timeout(hapd, sta); + if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out && + (sta->auth_alg != WLAN_AUTH_FT)) { + /* + * STA has already been associated with MFP and SA + * Query timeout has not been reached. Reject the + * association attempt temporarily and start SA Query, + * if one is not pending. + */ + + if (sta->sa_query_count == 0) + ap_sta_start_sa_query(hapd, sta); + +#ifdef CONFIG_IEEE80211R + status = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY; + + p = hostapd_eid_assoc_comeback_time(hapd, sta, p); + + hostapd_sta_assoc(hapd, addr, reassoc, status, buf, + p - buf); +#endif /* CONFIG_IEEE80211R */ + return 0; + } + + if (wpa_auth_uses_mfp(sta->wpa_sm)) + sta->flags |= WLAN_STA_MFP; + else + sta->flags &= ~WLAN_STA_MFP; +#endif /* CONFIG_IEEE80211W */ + +#ifdef CONFIG_IEEE80211R + if (sta->auth_alg == WLAN_AUTH_FT) { + status = wpa_ft_validate_reassoc(sta->wpa_sm, req_ies, + req_ies_len); + if (status != WLAN_STATUS_SUCCESS) { + if (status == WLAN_STATUS_INVALID_PMKID) + reason = WLAN_REASON_INVALID_IE; + if (status == WLAN_STATUS_INVALID_MDIE) + reason = WLAN_REASON_INVALID_IE; + if (status == WLAN_STATUS_INVALID_FTIE) + reason = WLAN_REASON_INVALID_IE; + goto fail; + } + } +#endif /* CONFIG_IEEE80211R */ } else if (hapd->conf->wps_state) { #ifdef CONFIG_WPS struct wpabuf *wps; @@ -178,6 +242,7 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, #ifdef CONFIG_WPS_STRICT if (wps && wps_validate_assoc_req(wps) < 0) { reason = WLAN_REASON_INVALID_IE; + status = WLAN_STATUS_INVALID_IE; wpabuf_free(wps); goto fail; } @@ -198,9 +263,24 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, skip_wpa_check: #endif /* CONFIG_WPS */ +#ifdef CONFIG_IEEE80211R + p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, buf, sizeof(buf), + sta->auth_alg, req_ies, req_ies_len); + + hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf); +#else /* CONFIG_IEEE80211R */ + /* Keep compiler silent about unused variables */ + if (status) { + } +#endif /* CONFIG_IEEE80211R */ + new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0; sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC; - wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC); + + if (reassoc && (sta->auth_alg == WLAN_AUTH_FT)) + wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT); + else + wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC); hostapd_new_assoc_sta(hapd, sta, !new_assoc); @@ -216,6 +296,9 @@ skip_wpa_check: return 0; fail: +#ifdef CONFIG_IEEE80211R + hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf); +#endif /* CONFIG_IEEE80211R */ hostapd_drv_sta_disassoc(hapd, sta->addr, reason); ap_free_sta(hapd, sta); return -1; @@ -326,6 +409,110 @@ int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da, #ifdef HOSTAPD +#ifdef CONFIG_IEEE80211R +static void hostapd_notify_auth_ft_finish(void *ctx, const u8 *dst, + const u8 *bssid, + u16 auth_transaction, u16 status, + const u8 *ies, size_t ies_len) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + + sta = ap_get_sta(hapd, dst); + if (sta == NULL) + return; + + hostapd_logger(hapd, dst, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "authentication OK (FT)"); + sta->flags |= WLAN_STA_AUTH; + + hostapd_sta_auth(hapd, dst, auth_transaction, status, ies, ies_len); +} +#endif /* CONFIG_IEEE80211R */ + + +static void hostapd_notif_auth(struct hostapd_data *hapd, + struct auth_info *rx_auth) +{ + struct sta_info *sta; + u16 status = WLAN_STATUS_SUCCESS; + u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN]; + size_t resp_ies_len = 0; + + sta = ap_get_sta(hapd, rx_auth->peer); + if (!sta) { + sta = ap_sta_add(hapd, rx_auth->peer); + if (sta == NULL) { + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + } + sta->flags &= ~WLAN_STA_PREAUTH; + ieee802_1x_notify_pre_auth(sta->eapol_sm, 0); +#ifdef CONFIG_IEEE80211R + if (rx_auth->auth_type == WLAN_AUTH_FT && hapd->wpa_auth) { + sta->auth_alg = WLAN_AUTH_FT; + if (sta->wpa_sm == NULL) + sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, + sta->addr); + if (sta->wpa_sm == NULL) { + wpa_printf(MSG_DEBUG, "FT: Failed to initialize WPA " + "state machine"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + wpa_ft_process_auth(sta->wpa_sm, rx_auth->bssid, + rx_auth->auth_transaction, rx_auth->ies, + rx_auth->ies_len, + hostapd_notify_auth_ft_finish, hapd); + return; + } +#endif /* CONFIG_IEEE80211R */ +fail: + hostapd_sta_auth(hapd, rx_auth->peer, rx_auth->auth_transaction + 1, + status, resp_ies, resp_ies_len); +} + + +static void hostapd_action_rx(struct hostapd_data *hapd, + struct rx_action *action) +{ + struct sta_info *sta; + + wpa_printf(MSG_DEBUG, "RX_ACTION cat %d action plen %d", + action->category, (int) action->len); + + sta = ap_get_sta(hapd, action->sa); + if (sta == NULL) { + wpa_printf(MSG_DEBUG, "%s: station not found", __func__); + return; + } +#ifdef CONFIG_IEEE80211R + if (action->category == WLAN_ACTION_FT) { + wpa_printf(MSG_DEBUG, "%s: FT_ACTION length %d", + __func__, (int) action->len); + wpa_ft_action_rx(sta->wpa_sm, action->data, action->len); + } +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + if (action->category == WLAN_ACTION_SA_QUERY && action->len >= 4) { + wpa_printf(MSG_DEBUG, "%s: SA_QUERY_ACTION length %d", + __func__, (int) action->len); + ieee802_11_sa_query_action(hapd, action->sa, + *(action->data + 1), + action->data + 2); + } +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_IEEE80211V + if (action->category == WLAN_ACTION_WNM) { + wpa_printf(MSG_DEBUG, "%s: WNM_ACTION length %d", + __func__, (int) action->len); + ieee802_11_rx_wnm_action_ap(hapd, action); + } +#endif /* CONFIG_IEEE80211V */ +} + + #ifdef NEED_AP_MLME #define HAPD_BROADCAST ((struct hostapd_data *) -1) @@ -505,7 +692,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, #ifndef CONFIG_NO_STDOUT_DEBUG int level = MSG_DEBUG; - if (event == EVENT_RX_MGMT && data && data->rx_mgmt.frame && + if (event == EVENT_RX_MGMT && data->rx_mgmt.frame && data->rx_mgmt.frame_len >= 24) { const struct ieee80211_hdr *hdr; u16 fc; @@ -610,14 +797,18 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, break; hostapd_event_sta_low_ack(hapd, data->low_ack.addr); break; -#ifdef NEED_AP_MLME case EVENT_RX_ACTION: if (data->rx_action.da == NULL || data->rx_action.sa == NULL || data->rx_action.bssid == NULL) break; +#ifdef NEED_AP_MLME hostapd_rx_action(hapd, &data->rx_action); - break; #endif /* NEED_AP_MLME */ + hostapd_action_rx(hapd, &data->rx_action); + break; + case EVENT_AUTH: + hostapd_notif_auth(hapd, &data->auth); + break; case EVENT_CH_SWITCH: if (!data) break; diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c index 2177b02a..53e6cbbe 100644 --- a/src/ap/gas_serv.c +++ b/src/ap/gas_serv.c @@ -128,6 +128,31 @@ static void gas_serv_free_dialogs(struct hostapd_data *hapd, } +static void anqp_add_hs_capab_list(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + u8 *len; + + len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); + wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST); + wpabuf_put_u8(buf, 0); /* Reserved */ + wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST); + if (hapd->conf->hs20_oper_friendly_name) + wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME); + if (hapd->conf->hs20_wan_metrics) + wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS); + if (hapd->conf->hs20_connection_capability) + wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY); + if (hapd->conf->nai_realm_data) + wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY); + if (hapd->conf->hs20_operating_class) + wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS); + gas_anqp_set_element_len(buf, len); +} + + static void anqp_add_capab_list(struct hostapd_data *hapd, struct wpabuf *buf) { @@ -137,8 +162,19 @@ static void anqp_add_capab_list(struct hostapd_data *hapd, wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST); if (hapd->conf->venue_name) wpabuf_put_le16(buf, ANQP_VENUE_NAME); + if (hapd->conf->network_auth_type) + wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE); if (hapd->conf->roaming_consortium) wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM); + if (hapd->conf->ipaddr_type_configured) + wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY); + if (hapd->conf->nai_realm_data) + wpabuf_put_le16(buf, ANQP_NAI_REALM); + if (hapd->conf->anqp_3gpp_cell_net) + wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK); + if (hapd->conf->domain_name) + wpabuf_put_le16(buf, ANQP_DOMAIN_NAME); + anqp_add_hs_capab_list(hapd, buf); gas_anqp_set_element_len(buf, len); } @@ -152,7 +188,7 @@ static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf) wpabuf_put_u8(buf, hapd->conf->venue_group); wpabuf_put_u8(buf, hapd->conf->venue_type); for (i = 0; i < hapd->conf->venue_name_count; i++) { - struct hostapd_venue_name *vn; + struct hostapd_lang_string *vn; vn = &hapd->conf->venue_name[i]; wpabuf_put_u8(buf, 3 + vn->name_len); wpabuf_put_data(buf, vn->lang, 3); @@ -163,6 +199,18 @@ static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf) } +static void anqp_add_network_auth_type(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + if (hapd->conf->network_auth_type) { + wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE); + wpabuf_put_le16(buf, hapd->conf->network_auth_type_len); + wpabuf_put_data(buf, hapd->conf->network_auth_type, + hapd->conf->network_auth_type_len); + } +} + + static void anqp_add_roaming_consortium(struct hostapd_data *hapd, struct wpabuf *buf) { @@ -180,10 +228,283 @@ static void anqp_add_roaming_consortium(struct hostapd_data *hapd, } +static void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + if (hapd->conf->ipaddr_type_configured) { + wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY); + wpabuf_put_le16(buf, 1); + wpabuf_put_u8(buf, hapd->conf->ipaddr_type_availability); + } +} + + +static void anqp_add_nai_realm_eap(struct wpabuf *buf, + struct hostapd_nai_realm_data *realm) +{ + unsigned int i, j; + + wpabuf_put_u8(buf, realm->eap_method_count); + + for (i = 0; i < realm->eap_method_count; i++) { + struct hostapd_nai_realm_eap *eap = &realm->eap_method[i]; + wpabuf_put_u8(buf, 2 + (3 * eap->num_auths)); + wpabuf_put_u8(buf, eap->eap_method); + wpabuf_put_u8(buf, eap->num_auths); + for (j = 0; j < eap->num_auths; j++) { + wpabuf_put_u8(buf, eap->auth_id[j]); + wpabuf_put_u8(buf, 1); + wpabuf_put_u8(buf, eap->auth_val[j]); + } + } +} + + +static void anqp_add_nai_realm_data(struct wpabuf *buf, + struct hostapd_nai_realm_data *realm, + unsigned int realm_idx) +{ + u8 *realm_data_len; + + wpa_printf(MSG_DEBUG, "realm=%s, len=%d", realm->realm[realm_idx], + (int) os_strlen(realm->realm[realm_idx])); + realm_data_len = wpabuf_put(buf, 2); + wpabuf_put_u8(buf, realm->encoding); + wpabuf_put_u8(buf, os_strlen(realm->realm[realm_idx])); + wpabuf_put_str(buf, realm->realm[realm_idx]); + anqp_add_nai_realm_eap(buf, realm); + gas_anqp_set_element_len(buf, realm_data_len); +} + + +static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd, + struct wpabuf *buf, + const u8 *home_realm, + size_t home_realm_len) +{ + unsigned int i, j, k; + u8 num_realms, num_matching = 0, encoding, realm_len, *realm_list_len; + struct hostapd_nai_realm_data *realm; + const u8 *pos, *realm_name, *end; + struct { + unsigned int realm_data_idx; + unsigned int realm_idx; + } matches[10]; + + pos = home_realm; + end = pos + home_realm_len; + if (pos + 1 > end) { + wpa_hexdump(MSG_DEBUG, "Too short NAI Home Realm Query", + home_realm, home_realm_len); + return -1; + } + num_realms = *pos++; + + for (i = 0; i < num_realms && num_matching < 10; i++) { + if (pos + 2 > end) { + wpa_hexdump(MSG_DEBUG, + "Truncated NAI Home Realm Query", + home_realm, home_realm_len); + return -1; + } + encoding = *pos++; + realm_len = *pos++; + if (pos + realm_len > end) { + wpa_hexdump(MSG_DEBUG, + "Truncated NAI Home Realm Query", + home_realm, home_realm_len); + return -1; + } + realm_name = pos; + for (j = 0; j < hapd->conf->nai_realm_count && + num_matching < 10; j++) { + const u8 *rpos, *rend; + realm = &hapd->conf->nai_realm_data[j]; + if (encoding != realm->encoding) + continue; + + rpos = realm_name; + while (rpos < realm_name + realm_len && + num_matching < 10) { + for (rend = rpos; + rend < realm_name + realm_len; rend++) { + if (*rend == ';') + break; + } + for (k = 0; k < MAX_NAI_REALMS && + realm->realm[k] && + num_matching < 10; k++) { + if ((int) os_strlen(realm->realm[k]) != + rend - rpos || + os_strncmp((char *) rpos, + realm->realm[k], + rend - rpos) != 0) + continue; + matches[num_matching].realm_data_idx = + j; + matches[num_matching].realm_idx = k; + num_matching++; + } + rpos = rend + 1; + } + } + pos += realm_len; + } + + realm_list_len = gas_anqp_add_element(buf, ANQP_NAI_REALM); + wpabuf_put_le16(buf, num_matching); + + /* + * There are two ways to format. 1. each realm in a NAI Realm Data unit + * 2. all realms that share the same EAP methods in a NAI Realm Data + * unit. The first format is likely to be bigger in size than the + * second, but may be easier to parse and process by the receiver. + */ + for (i = 0; i < num_matching; i++) { + wpa_printf(MSG_DEBUG, "realm_idx %d, realm_data_idx %d", + matches[i].realm_data_idx, matches[i].realm_idx); + realm = &hapd->conf->nai_realm_data[matches[i].realm_data_idx]; + anqp_add_nai_realm_data(buf, realm, matches[i].realm_idx); + } + gas_anqp_set_element_len(buf, realm_list_len); + return 0; +} + + +static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf, + const u8 *home_realm, size_t home_realm_len, + int nai_realm, int nai_home_realm) +{ + if (nai_realm && hapd->conf->nai_realm_data) { + u8 *len; + unsigned int i, j; + len = gas_anqp_add_element(buf, ANQP_NAI_REALM); + wpabuf_put_le16(buf, hapd->conf->nai_realm_count); + for (i = 0; i < hapd->conf->nai_realm_count; i++) { + u8 *realm_data_len, *realm_len; + struct hostapd_nai_realm_data *realm; + + realm = &hapd->conf->nai_realm_data[i]; + realm_data_len = wpabuf_put(buf, 2); + wpabuf_put_u8(buf, realm->encoding); + realm_len = wpabuf_put(buf, 1); + for (j = 0; realm->realm[j]; j++) { + if (j > 0) + wpabuf_put_u8(buf, ';'); + wpabuf_put_str(buf, realm->realm[j]); + } + *realm_len = (u8 *) wpabuf_put(buf, 0) - realm_len - 1; + anqp_add_nai_realm_eap(buf, realm); + gas_anqp_set_element_len(buf, realm_data_len); + } + gas_anqp_set_element_len(buf, len); + } else if (nai_home_realm && hapd->conf->nai_realm_data) { + hs20_add_nai_home_realm_matches(hapd, buf, home_realm, + home_realm_len); + } +} + + +static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + if (hapd->conf->anqp_3gpp_cell_net) { + wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK); + wpabuf_put_le16(buf, + hapd->conf->anqp_3gpp_cell_net_len); + wpabuf_put_data(buf, hapd->conf->anqp_3gpp_cell_net, + hapd->conf->anqp_3gpp_cell_net_len); + } +} + + +static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf) +{ + if (hapd->conf->domain_name) { + wpabuf_put_le16(buf, ANQP_DOMAIN_NAME); + wpabuf_put_le16(buf, hapd->conf->domain_name_len); + wpabuf_put_data(buf, hapd->conf->domain_name, + hapd->conf->domain_name_len); + } +} + + +static void anqp_add_operator_friendly_name(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + if (hapd->conf->hs20_oper_friendly_name) { + u8 *len; + unsigned int i; + len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); + wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME); + wpabuf_put_u8(buf, 0); /* Reserved */ + for (i = 0; i < hapd->conf->hs20_oper_friendly_name_count; i++) + { + struct hostapd_lang_string *vn; + vn = &hapd->conf->hs20_oper_friendly_name[i]; + wpabuf_put_u8(buf, 3 + vn->name_len); + wpabuf_put_data(buf, vn->lang, 3); + wpabuf_put_data(buf, vn->name, vn->name_len); + } + gas_anqp_set_element_len(buf, len); + } +} + + +static void anqp_add_wan_metrics(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + if (hapd->conf->hs20_wan_metrics) { + u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); + wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS); + wpabuf_put_u8(buf, 0); /* Reserved */ + wpabuf_put_data(buf, hapd->conf->hs20_wan_metrics, 13); + gas_anqp_set_element_len(buf, len); + } +} + + +static void anqp_add_connection_capability(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + if (hapd->conf->hs20_connection_capability) { + u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); + wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY); + wpabuf_put_u8(buf, 0); /* Reserved */ + wpabuf_put_data(buf, hapd->conf->hs20_connection_capability, + hapd->conf->hs20_connection_capability_len); + gas_anqp_set_element_len(buf, len); + } +} + + +static void anqp_add_operating_class(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + if (hapd->conf->hs20_operating_class) { + u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); + wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS); + wpabuf_put_u8(buf, 0); /* Reserved */ + wpabuf_put_data(buf, hapd->conf->hs20_operating_class, + hapd->conf->hs20_operating_class_len); + gas_anqp_set_element_len(buf, len); + } +} + + static struct wpabuf * gas_serv_build_gas_resp_payload(struct hostapd_data *hapd, unsigned int request, - struct gas_dialog_info *di) + struct gas_dialog_info *di, + const u8 *home_realm, size_t home_realm_len) { struct wpabuf *buf; @@ -195,8 +516,31 @@ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd, anqp_add_capab_list(hapd, buf); if (request & ANQP_REQ_VENUE_NAME) anqp_add_venue_name(hapd, buf); + if (request & ANQP_REQ_NETWORK_AUTH_TYPE) + anqp_add_network_auth_type(hapd, buf); if (request & ANQP_REQ_ROAMING_CONSORTIUM) anqp_add_roaming_consortium(hapd, buf); + if (request & ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY) + anqp_add_ip_addr_type_availability(hapd, buf); + if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM)) + anqp_add_nai_realm(hapd, buf, home_realm, home_realm_len, + request & ANQP_REQ_NAI_REALM, + request & ANQP_REQ_NAI_HOME_REALM); + if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK) + anqp_add_3gpp_cellular_network(hapd, buf); + if (request & ANQP_REQ_DOMAIN_NAME) + anqp_add_domain_name(hapd, buf); + + if (request & ANQP_REQ_HS_CAPABILITY_LIST) + anqp_add_hs_capab_list(hapd, buf); + if (request & ANQP_REQ_OPERATOR_FRIENDLY_NAME) + anqp_add_operator_friendly_name(hapd, buf); + if (request & ANQP_REQ_WAN_METRICS) + anqp_add_wan_metrics(hapd, buf); + if (request & ANQP_REQ_CONNECTION_CAPABILITY) + anqp_add_connection_capability(hapd, buf); + if (request & ANQP_REQ_OPERATING_CLASS) + anqp_add_operating_class(hapd, buf); return buf; } @@ -216,8 +560,8 @@ static void gas_serv_clear_cached_ies(void *eloop_data, void *user_ctx) struct anqp_query_info { unsigned int request; unsigned int remote_request; - const void *param; - u32 param_arg; + const u8 *home_realm_query; + size_t home_realm_query_len; u16 remote_delay; }; @@ -252,10 +596,37 @@ static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id, set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name", hapd->conf->venue_name != NULL, 0, 0, qi); break; + case ANQP_NETWORK_AUTH_TYPE: + set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type", + hapd->conf->network_auth_type != NULL, + 0, 0, qi); + break; case ANQP_ROAMING_CONSORTIUM: set_anqp_req(ANQP_REQ_ROAMING_CONSORTIUM, "Roaming Consortium", hapd->conf->roaming_consortium != NULL, 0, 0, qi); break; + case ANQP_IP_ADDR_TYPE_AVAILABILITY: + set_anqp_req(ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY, + "IP Addr Type Availability", + hapd->conf->ipaddr_type_configured, + 0, 0, qi); + break; + case ANQP_NAI_REALM: + set_anqp_req(ANQP_REQ_NAI_REALM, "NAI Realm", + hapd->conf->nai_realm_data != NULL, + 0, 0, qi); + break; + case ANQP_3GPP_CELLULAR_NETWORK: + set_anqp_req(ANQP_REQ_3GPP_CELLULAR_NETWORK, + "3GPP Cellular Network", + hapd->conf->anqp_3gpp_cell_net != NULL, + 0, 0, qi); + break; + case ANQP_DOMAIN_NAME: + set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name", + hapd->conf->domain_name != NULL, + 0, 0, qi); + break; default: wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u", info_id); @@ -278,13 +649,122 @@ static void rx_anqp_query_list(struct hostapd_data *hapd, } +static void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype, + struct anqp_query_info *qi) +{ + switch (subtype) { + case HS20_STYPE_CAPABILITY_LIST: + set_anqp_req(ANQP_REQ_HS_CAPABILITY_LIST, "HS Capability List", + 1, 0, 0, qi); + break; + case HS20_STYPE_OPERATOR_FRIENDLY_NAME: + set_anqp_req(ANQP_REQ_OPERATOR_FRIENDLY_NAME, + "Operator Friendly Name", + hapd->conf->hs20_oper_friendly_name != NULL, + 0, 0, qi); + break; + case HS20_STYPE_WAN_METRICS: + set_anqp_req(ANQP_REQ_WAN_METRICS, "WAN Metrics", + hapd->conf->hs20_wan_metrics != NULL, + 0, 0, qi); + break; + case HS20_STYPE_CONNECTION_CAPABILITY: + set_anqp_req(ANQP_REQ_CONNECTION_CAPABILITY, + "Connection Capability", + hapd->conf->hs20_connection_capability != NULL, + 0, 0, qi); + break; + case HS20_STYPE_OPERATING_CLASS: + set_anqp_req(ANQP_REQ_OPERATING_CLASS, "Operating Class", + hapd->conf->hs20_operating_class != NULL, + 0, 0, qi); + break; + default: + wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u", + subtype); + break; + } +} + + +static void rx_anqp_hs_nai_home_realm(struct hostapd_data *hapd, + const u8 *pos, const u8 *end, + struct anqp_query_info *qi) +{ + qi->request |= ANQP_REQ_NAI_HOME_REALM; + qi->home_realm_query = pos; + qi->home_realm_query_len = end - pos; + if (hapd->conf->nai_realm_data != NULL) { + wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query " + "(local)"); + } else { + wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query not " + "available"); + } +} + + +static void rx_anqp_vendor_specific(struct hostapd_data *hapd, + const u8 *pos, const u8 *end, + struct anqp_query_info *qi) +{ + u32 oui; + u8 subtype; + + if (pos + 4 > end) { + wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP " + "Query element"); + return; + } + + oui = WPA_GET_BE24(pos); + pos += 3; + if (oui != OUI_WFA) { + wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x", + oui); + return; + } + + if (*pos != HS20_ANQP_OUI_TYPE) { + wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u", + *pos); + return; + } + pos++; + + if (pos + 1 >= end) + return; + + subtype = *pos++; + pos++; /* Reserved */ + switch (subtype) { + case HS20_STYPE_QUERY_LIST: + wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Query List"); + while (pos < end) { + rx_anqp_hs_query_list(hapd, *pos, qi); + pos++; + } + break; + case HS20_STYPE_NAI_HOME_REALM_QUERY: + rx_anqp_hs_nai_home_realm(hapd, pos, end, qi); + break; + default: + wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype " + "%u", subtype); + break; + } +} + + static void gas_serv_req_local_processing(struct hostapd_data *hapd, const u8 *sa, u8 dialog_token, struct anqp_query_info *qi) { struct wpabuf *buf, *tx_buf; - buf = gas_serv_build_gas_resp_payload(hapd, qi->request, NULL); + buf = gas_serv_build_gas_resp_payload(hapd, qi->request, NULL, + qi->home_realm_query, + qi->home_realm_query_len); wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses", buf); if (!buf) @@ -419,6 +899,9 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, case ANQP_QUERY_LIST: rx_anqp_query_list(hapd, pos, pos + elen, &qi); break; + case ANQP_VENDOR_SPECIFIC: + rx_anqp_vendor_specific(hapd, pos, pos + elen, &qi); + break; default: wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query " "Request element %u", info_id); @@ -442,7 +925,7 @@ void gas_serv_tx_gas_response(struct hostapd_data *hapd, const u8 *dst, if (dialog->sd_resp == NULL) { buf = gas_serv_build_gas_resp_payload(hapd, dialog->all_requested, - dialog); + dialog, NULL, 0); wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses", buf); if (!buf) @@ -571,7 +1054,7 @@ static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd, buf = gas_serv_build_gas_resp_payload(hapd, dialog->all_requested, - dialog); + dialog, NULL, 0); wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses", buf); if (!buf) diff --git a/src/ap/gas_serv.h b/src/ap/gas_serv.h index 0e2eaf66..4213cf6d 100644 --- a/src/ap/gas_serv.h +++ b/src/ap/gas_serv.h @@ -13,8 +13,30 @@ (1 << (ANQP_CAPABILITY_LIST - ANQP_QUERY_LIST)) #define ANQP_REQ_VENUE_NAME \ (1 << (ANQP_VENUE_NAME - ANQP_QUERY_LIST)) +#define ANQP_REQ_NETWORK_AUTH_TYPE \ + (1 << (ANQP_NETWORK_AUTH_TYPE - ANQP_QUERY_LIST)) #define ANQP_REQ_ROAMING_CONSORTIUM \ (1 << (ANQP_ROAMING_CONSORTIUM - ANQP_QUERY_LIST)) +#define ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY \ + (1 << (ANQP_IP_ADDR_TYPE_AVAILABILITY - ANQP_QUERY_LIST)) +#define ANQP_REQ_NAI_REALM \ + (1 << (ANQP_NAI_REALM - ANQP_QUERY_LIST)) +#define ANQP_REQ_3GPP_CELLULAR_NETWORK \ + (1 << (ANQP_3GPP_CELLULAR_NETWORK - ANQP_QUERY_LIST)) +#define ANQP_REQ_DOMAIN_NAME \ + (1 << (ANQP_DOMAIN_NAME - ANQP_QUERY_LIST)) +#define ANQP_REQ_HS_CAPABILITY_LIST \ + (0x10000 << HS20_STYPE_CAPABILITY_LIST) +#define ANQP_REQ_OPERATOR_FRIENDLY_NAME \ + (0x10000 << HS20_STYPE_OPERATOR_FRIENDLY_NAME) +#define ANQP_REQ_WAN_METRICS \ + (0x10000 << HS20_STYPE_WAN_METRICS) +#define ANQP_REQ_CONNECTION_CAPABILITY \ + (0x10000 << HS20_STYPE_CONNECTION_CAPABILITY) +#define ANQP_REQ_NAI_HOME_REALM \ + (0x10000 << HS20_STYPE_NAI_HOME_REALM_QUERY) +#define ANQP_REQ_OPERATING_CLASS \ + (0x10000 << HS20_STYPE_OPERATING_CLASS) /* To account for latencies between hostapd and external ANQP processor */ #define GAS_SERV_COMEBACK_DELAY_FUDGE 10 diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index 22c5e654..34292587 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -39,6 +39,7 @@ static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd); static int hostapd_broadcast_wep_clear(struct hostapd_data *hapd); extern int wpa_debug_level; +extern struct wpa_driver_ops *wpa_drivers[]; int hostapd_for_each_interface(struct hapd_interfaces *interfaces, @@ -98,7 +99,7 @@ static void hostapd_reload_bss(struct hostapd_data *hapd) hostapd_update_wps(hapd); if (hapd->conf->ssid.ssid_set && - hostapd_set_ssid(hapd, (u8 *) hapd->conf->ssid.ssid, + hostapd_set_ssid(hapd, hapd->conf->ssid.ssid, hapd->conf->ssid.ssid_len)) { wpa_printf(MSG_ERROR, "Could not set SSID for kernel driver"); /* try to continue */ @@ -113,9 +114,10 @@ int hostapd_reload_config(struct hostapd_iface *iface) struct hostapd_config *newconf, *oldconf; size_t j; - if (iface->config_read_cb == NULL) + if (iface->interfaces == NULL || + iface->interfaces->config_read_cb == NULL) return -1; - newconf = iface->config_read_cb(iface->config_fname); + newconf = iface->interfaces->config_read_cb(iface->config_fname); if (newconf == NULL) return -1; @@ -286,8 +288,9 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd) */ static void hostapd_cleanup(struct hostapd_data *hapd) { - if (hapd->iface->ctrl_iface_deinit) - hapd->iface->ctrl_iface_deinit(hapd); + if (hapd->iface->interfaces && + hapd->iface->interfaces->ctrl_iface_deinit) + hapd->iface->interfaces->ctrl_iface_deinit(hapd); hostapd_free_hapd_data(hapd); } @@ -679,14 +682,14 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) set_ssid = 0; conf->ssid.ssid_len = ssid_len; os_memcpy(conf->ssid.ssid, ssid, conf->ssid.ssid_len); - conf->ssid.ssid[conf->ssid.ssid_len] = '\0'; } if (!hostapd_drv_none(hapd)) { wpa_printf(MSG_ERROR, "Using interface %s with hwaddr " MACSTR - " and ssid '%s'", + " and ssid \"%s\"", hapd->conf->iface, MAC2STR(hapd->own_addr), - hapd->conf->ssid.ssid); + wpa_ssid_txt(hapd->conf->ssid.ssid, + hapd->conf->ssid.ssid_len)); } if (hostapd_setup_wpa_psk(conf)) { @@ -696,7 +699,7 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) /* Set SSID for the kernel driver (to be used in beacon and probe * response frames) */ - if (set_ssid && hostapd_set_ssid(hapd, (u8 *) conf->ssid.ssid, + if (set_ssid && hostapd_set_ssid(hapd, conf->ssid.ssid, conf->ssid.ssid_len)) { wpa_printf(MSG_ERROR, "Could not set SSID for kernel driver"); return -1; @@ -770,8 +773,9 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) } #endif /* CONFIG_INTERWORKING */ - if (hapd->iface->ctrl_iface_init && - hapd->iface->ctrl_iface_init(hapd)) { + if (hapd->iface->interfaces && + hapd->iface->interfaces->ctrl_iface_init && + hapd->iface->interfaces->ctrl_iface_init(hapd)) { wpa_printf(MSG_ERROR, "Failed to setup control interface"); return -1; } @@ -1045,6 +1049,292 @@ void hostapd_interface_free(struct hostapd_iface *iface) } +#ifdef HOSTAPD + +void hostapd_interface_deinit_free(struct hostapd_iface *iface) +{ + const struct wpa_driver_ops *driver; + void *drv_priv; + if (iface == NULL) + return; + driver = iface->bss[0]->driver; + drv_priv = iface->bss[0]->drv_priv; + hostapd_interface_deinit(iface); + if (driver && driver->hapd_deinit && drv_priv) + driver->hapd_deinit(drv_priv); + hostapd_interface_free(iface); +} + + +int hostapd_enable_iface(struct hostapd_iface *hapd_iface) +{ + if (hapd_iface->bss[0]->drv_priv != NULL) { + wpa_printf(MSG_ERROR, "Interface %s already enabled", + hapd_iface->conf->bss[0].iface); + return -1; + } + + wpa_printf(MSG_DEBUG, "Enable interface %s", + hapd_iface->conf->bss[0].iface); + + if (hapd_iface->interfaces == NULL || + hapd_iface->interfaces->driver_init == NULL || + hapd_iface->interfaces->driver_init(hapd_iface) || + hostapd_setup_interface(hapd_iface)) { + hostapd_interface_deinit_free(hapd_iface); + return -1; + } + return 0; +} + + +int hostapd_reload_iface(struct hostapd_iface *hapd_iface) +{ + size_t j; + + wpa_printf(MSG_DEBUG, "Reload interface %s", + hapd_iface->conf->bss[0].iface); + for (j = 0; j < hapd_iface->num_bss; j++) { + hostapd_flush_old_stations(hapd_iface->bss[j], + WLAN_REASON_PREV_AUTH_NOT_VALID); + +#ifndef CONFIG_NO_RADIUS + /* TODO: update dynamic data based on changed configuration + * items (e.g., open/close sockets, etc.) */ + radius_client_flush(hapd_iface->bss[j]->radius, 0); +#endif /* CONFIG_NO_RADIUS */ + + hostapd_reload_bss(hapd_iface->bss[j]); + } + return 0; +} + + +int hostapd_disable_iface(struct hostapd_iface *hapd_iface) +{ + size_t j; + struct hostapd_bss_config *bss = hapd_iface->bss[0]->conf; + const struct wpa_driver_ops *driver; + void *drv_priv; + + if (hapd_iface == NULL) + return -1; + driver = hapd_iface->bss[0]->driver; + drv_priv = hapd_iface->bss[0]->drv_priv; + + /* whatever hostapd_interface_deinit does */ + for (j = 0; j < hapd_iface->num_bss; j++) { + struct hostapd_data *hapd = hapd_iface->bss[j]; + hostapd_free_stas(hapd); + hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING); + hostapd_clear_wep(hapd); + hostapd_free_hapd_data(hapd); + } + + if (driver && driver->hapd_deinit && drv_priv) { + driver->hapd_deinit(drv_priv); + hapd_iface->bss[0]->drv_priv = NULL; + } + + /* From hostapd_cleanup_iface: These were initialized in + * hostapd_setup_interface and hostapd_setup_interface_complete + */ + hostapd_cleanup_iface_partial(hapd_iface); + bss->wpa = 0; + bss->wpa_key_mgmt = -1; + bss->wpa_pairwise = -1; + + wpa_printf(MSG_DEBUG, "Interface %s disabled", bss->iface); + return 0; +} + + +static struct hostapd_iface * +hostapd_iface_alloc(struct hapd_interfaces *interfaces) +{ + struct hostapd_iface **iface, *hapd_iface; + + iface = os_realloc_array(interfaces->iface, interfaces->count + 1, + sizeof(struct hostapd_iface *)); + if (iface == NULL) + return NULL; + interfaces->iface = iface; + hapd_iface = interfaces->iface[interfaces->count] = + os_zalloc(sizeof(*hapd_iface)); + if (hapd_iface == NULL) { + wpa_printf(MSG_ERROR, "%s: Failed to allocate memory for " + "the interface", __func__); + return NULL; + } + interfaces->count++; + hapd_iface->interfaces = interfaces; + + return hapd_iface; +} + + +static struct hostapd_config * +hostapd_config_alloc(struct hapd_interfaces *interfaces, const char *ifname, + const char *ctrl_iface) +{ + struct hostapd_bss_config *bss; + struct hostapd_config *conf; + + /* Allocates memory for bss and conf */ + conf = hostapd_config_defaults(); + if (conf == NULL) { + wpa_printf(MSG_ERROR, "%s: Failed to allocate memory for " + "configuration", __func__); + return NULL; + } + + conf->driver = wpa_drivers[0]; + if (conf->driver == NULL) { + wpa_printf(MSG_ERROR, "No driver wrappers registered!"); + hostapd_config_free(conf); + return NULL; + } + + bss = conf->last_bss = conf->bss; + + os_strlcpy(bss->iface, ifname, sizeof(bss->iface)); + bss->ctrl_interface = os_strdup(ctrl_iface); + if (bss->ctrl_interface == NULL) { + hostapd_config_free(conf); + return NULL; + } + + /* Reading configuration file skipped, will be done in SET! + * From reading the configuration till the end has to be done in + * SET + */ + return conf; +} + + +static struct hostapd_iface * hostapd_data_alloc( + struct hapd_interfaces *interfaces, struct hostapd_config *conf) +{ + size_t i; + struct hostapd_iface *hapd_iface = + interfaces->iface[interfaces->count - 1]; + struct hostapd_data *hapd; + + hapd_iface->conf = conf; + hapd_iface->num_bss = conf->num_bss; + + hapd_iface->bss = os_zalloc(conf->num_bss * + sizeof(struct hostapd_data *)); + if (hapd_iface->bss == NULL) + return NULL; + + for (i = 0; i < conf->num_bss; i++) { + hapd = hapd_iface->bss[i] = + hostapd_alloc_bss_data(hapd_iface, conf, + &conf->bss[i]); + if (hapd == NULL) + return NULL; + hapd->msg_ctx = hapd; + } + + hapd_iface->interfaces = interfaces; + + return hapd_iface; +} + + +int hostapd_add_iface(struct hapd_interfaces *interfaces, char *buf) +{ + struct hostapd_config *conf = NULL; + struct hostapd_iface *hapd_iface = NULL; + char *ptr; + size_t i; + + ptr = os_strchr(buf, ' '); + if (ptr == NULL) + return -1; + *ptr++ = '\0'; + + for (i = 0; i < interfaces->count; i++) { + if (!os_strcmp(interfaces->iface[i]->conf->bss[0].iface, + buf)) { + wpa_printf(MSG_INFO, "Cannot add interface - it " + "already exists"); + return -1; + } + } + + hapd_iface = hostapd_iface_alloc(interfaces); + if (hapd_iface == NULL) { + wpa_printf(MSG_ERROR, "%s: Failed to allocate memory " + "for interface", __func__); + goto fail; + } + + conf = hostapd_config_alloc(interfaces, buf, ptr); + if (conf == NULL) { + wpa_printf(MSG_ERROR, "%s: Failed to allocate memory " + "for configuration", __func__); + goto fail; + } + + hapd_iface = hostapd_data_alloc(interfaces, conf); + if (hapd_iface == NULL) { + wpa_printf(MSG_ERROR, "%s: Failed to allocate memory " + "for hostapd", __func__); + goto fail; + } + + if (hapd_iface->interfaces && + hapd_iface->interfaces->ctrl_iface_init && + hapd_iface->interfaces->ctrl_iface_init(hapd_iface->bss[0])) { + wpa_printf(MSG_ERROR, "%s: Failed to setup control " + "interface", __func__); + goto fail; + } + wpa_printf(MSG_INFO, "Add interface '%s'", conf->bss[0].iface); + + return 0; + +fail: + if (conf) + hostapd_config_free(conf); + if (hapd_iface) { + os_free(hapd_iface->bss[interfaces->count]); + os_free(hapd_iface); + } + return -1; +} + + +int hostapd_remove_iface(struct hapd_interfaces *interfaces, char *buf) +{ + struct hostapd_iface *hapd_iface; + size_t i, k = 0; + + for (i = 0; i < interfaces->count; i++) { + hapd_iface = interfaces->iface[i]; + if (hapd_iface == NULL) + return -1; + if (!os_strcmp(hapd_iface->conf->bss[0].iface, buf)) { + wpa_printf(MSG_INFO, "Remove interface '%s'", buf); + hostapd_interface_deinit_free(hapd_iface); + k = i; + while (k < (interfaces->count - 1)) { + interfaces->iface[k] = + interfaces->iface[k + 1]; + k++; + } + interfaces->count--; + return 0; + } + } + return -1; +} + +#endif /* HOSTAPD */ + + /** * hostapd_new_assoc_sta - Notify that a new station associated with the AP * @hapd: Pointer to BSS data diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index f7ed3110..71f476c0 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -23,8 +23,22 @@ struct full_dynamic_vlan; enum wps_event; union wps_event_data; +struct hostapd_iface; + struct hapd_interfaces { + int (*reload_config)(struct hostapd_iface *iface); + struct hostapd_config * (*config_read_cb)(const char *config_fname); + int (*ctrl_iface_init)(struct hostapd_data *hapd); + void (*ctrl_iface_deinit)(struct hostapd_data *hapd); + int (*for_each_interface)(struct hapd_interfaces *interfaces, + int (*cb)(struct hostapd_iface *iface, + void *ctx), void *ctx); + int (*driver_init)(struct hostapd_iface *iface); + size_t count; + int global_ctrl_sock; + char *global_iface_path; + char *global_iface_name; struct hostapd_iface **iface; }; @@ -182,8 +196,6 @@ struct hostapd_data { struct hostapd_iface { struct hapd_interfaces *interfaces; void *owner; - int (*reload_config)(struct hostapd_iface *iface); - struct hostapd_config * (*config_read_cb)(const char *config_fname); char *config_fname; struct hostapd_config *conf; @@ -241,13 +253,6 @@ struct hostapd_iface { u16 ht_op_mode; void (*scan_cb)(struct hostapd_iface *iface); - - int (*ctrl_iface_init)(struct hostapd_data *hapd); - void (*ctrl_iface_deinit)(struct hostapd_data *hapd); - - int (*for_each_interface)(struct hapd_interfaces *interfaces, - int (*cb)(struct hostapd_iface *iface, - void *ctx), void *ctx); }; /* hostapd.c */ @@ -265,6 +270,12 @@ void hostapd_interface_deinit(struct hostapd_iface *iface); void hostapd_interface_free(struct hostapd_iface *iface); void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, int reassoc); +void hostapd_interface_deinit_free(struct hostapd_iface *iface); +int hostapd_enable_iface(struct hostapd_iface *hapd_iface); +int hostapd_reload_iface(struct hostapd_iface *hapd_iface); +int hostapd_disable_iface(struct hostapd_iface *hapd_iface); +int hostapd_add_iface(struct hapd_interfaces *ifaces, char *buf); +int hostapd_remove_iface(struct hapd_interfaces *ifaces, char *buf); /* utils.c */ int hostapd_register_probereq_cb(struct hostapd_data *hapd, diff --git a/src/ap/hs20.c b/src/ap/hs20.c new file mode 100644 index 00000000..45d518bc --- /dev/null +++ b/src/ap/hs20.c @@ -0,0 +1,31 @@ +/* + * Hotspot 2.0 AP ANQP processing + * Copyright (c) 2009, Atheros Communications, Inc. + * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "common/ieee802_11_defs.h" +#include "hostapd.h" +#include "ap_config.h" +#include "hs20.h" + + +u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid) +{ + if (!hapd->conf->hs20) + return eid; + *eid++ = WLAN_EID_VENDOR_SPECIFIC; + *eid++ = 5; + WPA_PUT_BE24(eid, OUI_WFA); + eid += 3; + *eid++ = HS20_INDICATION_OUI_TYPE; + /* Hotspot Configuration: DGAF Enabled */ + *eid++ = hapd->conf->disable_dgaf ? 0x01 : 0x00; + return eid; +} diff --git a/src/ap/hs20.h b/src/ap/hs20.h new file mode 100644 index 00000000..98698ce2 --- /dev/null +++ b/src/ap/hs20.h @@ -0,0 +1,16 @@ +/* + * Hotspot 2.0 AP ANQP processing + * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef HS20_H +#define HS20_H + +struct hostapd_data; + +u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid); + +#endif /* HS20_H */ diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c index 76c42118..76aff775 100644 --- a/src/ap/hw_features.c +++ b/src/ap/hw_features.c @@ -138,7 +138,7 @@ int hostapd_prepare_rates(struct hostapd_iface *iface, iface->num_rates = 0; iface->current_rates = - os_zalloc(mode->num_rates * sizeof(struct hostapd_rate_data)); + os_calloc(mode->num_rates, sizeof(struct hostapd_rate_data)); if (!iface->current_rates) { wpa_printf(MSG_ERROR, "Failed to allocate memory for rate " "table."); @@ -470,7 +470,7 @@ static void ieee80211n_scan_channels_2g4(struct hostapd_iface *iface, affected_start, affected_end); mode = iface->current_mode; - params->freqs = os_zalloc((mode->num_channels + 1) * sizeof(int)); + params->freqs = os_calloc(mode->num_channels + 1, sizeof(int)); if (params->freqs == NULL) return; pos = 0; diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 3996c90d..211ee1bc 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -311,6 +311,8 @@ static void handle_auth(struct hostapd_data *hapd, int has_psk = 0; u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN]; size_t resp_ies_len = 0; + char *identity = NULL; + char *radius_cui = NULL; if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { printf("handle_auth - too short payload (len=%lu)\n", @@ -372,7 +374,7 @@ static void handle_auth(struct hostapd_data *hapd, res = hostapd_allowed_address(hapd, mgmt->sa, (u8 *) mgmt, len, &session_timeout, &acct_interim_interval, &vlan_id, - psk, &has_psk); + psk, &has_psk, &identity, &radius_cui); if (res == HOSTAPD_ACL_REJECT) { printf("Station " MACSTR " not allowed to authenticate.\n", @@ -421,6 +423,11 @@ static void handle_auth(struct hostapd_data *hapd, sta->psk = NULL; } + sta->identity = identity; + identity = NULL; + sta->radius_cui = radius_cui; + radius_cui = NULL; + sta->flags &= ~WLAN_STA_PREAUTH; ieee802_1x_notify_pre_auth(sta->eapol_sm, 0); @@ -482,6 +489,9 @@ static void handle_auth(struct hostapd_data *hapd, } fail: + os_free(identity); + os_free(radius_cui); + send_auth_reply(hapd, mgmt->sa, mgmt->bssid, auth_alg, auth_transaction + 1, resp, resp_ies, resp_ies_len); } @@ -576,35 +586,20 @@ static u16 copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta, return WLAN_STATUS_UNSPECIFIED_FAILURE; } - if (elems->supp_rates_len > sizeof(sta->supported_rates)) { + if (elems->supp_rates_len + elems->ext_supp_rates_len > + sizeof(sta->supported_rates)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, - "Invalid supported rates element length %d", - elems->supp_rates_len); + "Invalid supported rates element length %d+%d", + elems->supp_rates_len, + elems->ext_supp_rates_len); return WLAN_STATUS_UNSPECIFIED_FAILURE; } - os_memset(sta->supported_rates, 0, sizeof(sta->supported_rates)); - os_memcpy(sta->supported_rates, elems->supp_rates, - elems->supp_rates_len); - sta->supported_rates_len = elems->supp_rates_len; - - if (elems->ext_supp_rates) { - if (elems->supp_rates_len + elems->ext_supp_rates_len > - sizeof(sta->supported_rates)) { - hostapd_logger(hapd, sta->addr, - HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, - "Invalid supported rates element length" - " %d+%d", elems->supp_rates_len, - elems->ext_supp_rates_len); - return WLAN_STATUS_UNSPECIFIED_FAILURE; - } - - os_memcpy(sta->supported_rates + elems->supp_rates_len, - elems->ext_supp_rates, elems->ext_supp_rates_len); - sta->supported_rates_len += elems->ext_supp_rates_len; - } + sta->supported_rates_len = merge_byte_arrays( + sta->supported_rates, sizeof(sta->supported_rates), + elems->supp_rates, elems->supp_rates_len, + elems->ext_supp_rates, elems->ext_supp_rates_len); return WLAN_STATUS_SUCCESS; } @@ -648,6 +643,20 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, } #endif /* CONFIG_IEEE80211N */ +#ifdef CONFIG_IEEE80211AC + resp = copy_sta_vht_capab(hapd, sta, elems.vht_capabilities, + elems.vht_capabilities_len); + if (resp != WLAN_STATUS_SUCCESS) + return resp; + if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht && + !(sta->flags & WLAN_STA_VHT)) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "Station does not support " + "mandatory VHT PHY - reject association"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } +#endif /* CONFIG_IEEE80211AC */ + if ((hapd->conf->wpa & WPA_PROTO_RSN) && elems.rsn_ie) { wpa_ie = elems.rsn_ie; wpa_ie_len = elems.rsn_ie_len; @@ -771,7 +780,7 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211N - if ((sta->flags & WLAN_STA_HT) && + if ((sta->flags & (WLAN_STA_HT | WLAN_STA_VHT)) && wpa_auth_get_pairwise(sta->wpa_sm) == WPA_CIPHER_TKIP) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, @@ -875,6 +884,11 @@ static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, p = hostapd_eid_ht_operation(hapd, p); #endif /* CONFIG_IEEE80211N */ +#ifdef CONFIG_IEEE80211AC + p = hostapd_eid_vht_capabilities(hapd, p); + p = hostapd_eid_vht_operation(hapd, p); +#endif /* CONFIG_IEEE80211AC */ + p = hostapd_eid_ext_capab(hapd, p); p = hostapd_eid_bss_max_idle_period(hapd, p); @@ -1337,7 +1351,10 @@ static void handle_action(struct hostapd_data *hapd, os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN); resp->u.action.category |= 0x80; - hostapd_drv_send_mlme(hapd, resp, len, 0); + if (hostapd_drv_send_mlme(hapd, resp, len, 0) < 0) { + wpa_printf(MSG_ERROR, "IEEE 802.11: Failed to send " + "Action frame"); + } os_free(resp); } } diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h index 9993bee1..1e5800d0 100644 --- a/src/ap/ieee802_11.h +++ b/src/ap/ieee802_11.h @@ -56,6 +56,8 @@ void hostapd_get_ht_capab(struct hostapd_data *hapd, u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ht_capab, size_t ht_capab_len); void update_ht_state(struct hostapd_data *hapd, 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); void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr, const u8 *buf, size_t len, int ack); void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst, diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c index 0c4c5f32..63ae3452 100644 --- a/src/ap/ieee802_11_auth.c +++ b/src/ap/ieee802_11_auth.c @@ -1,6 +1,6 @@ /* * hostapd / IEEE 802.11 authentication (ACL) - * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -22,6 +22,7 @@ #include "ap_config.h" #include "ap_drv_ops.h" #include "ieee802_11.h" +#include "ieee802_1x.h" #include "ieee802_11_auth.h" #define RADIUS_ACL_TIMEOUT 30 @@ -37,6 +38,8 @@ struct hostapd_cached_radius_acl { int vlan_id; int has_psk; u8 psk[PMK_LEN]; + char *identity; + char *radius_cui; }; @@ -51,6 +54,14 @@ struct hostapd_acl_query_data { #ifndef CONFIG_NO_RADIUS +static void hostapd_acl_cache_free_entry(struct hostapd_cached_radius_acl *e) +{ + os_free(e->identity); + os_free(e->radius_cui); + os_free(e); +} + + static void hostapd_acl_cache_free(struct hostapd_cached_radius_acl *acl_cache) { struct hostapd_cached_radius_acl *prev; @@ -58,7 +69,7 @@ static void hostapd_acl_cache_free(struct hostapd_cached_radius_acl *acl_cache) while (acl_cache) { prev = acl_cache; acl_cache = acl_cache->next; - os_free(prev); + hostapd_acl_cache_free_entry(prev); } } @@ -66,35 +77,45 @@ static void hostapd_acl_cache_free(struct hostapd_cached_radius_acl *acl_cache) static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr, u32 *session_timeout, u32 *acct_interim_interval, int *vlan_id, - u8 *psk, int *has_psk) + u8 *psk, int *has_psk, char **identity, + char **radius_cui) { struct hostapd_cached_radius_acl *entry; struct os_time now; os_get_time(&now); - entry = hapd->acl_cache; - while (entry) { - if (os_memcmp(entry->addr, addr, ETH_ALEN) == 0) { - if (now.sec - entry->timestamp > RADIUS_ACL_TIMEOUT) - return -1; /* entry has expired */ - if (entry->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT) - if (session_timeout) - *session_timeout = - entry->session_timeout; - if (acct_interim_interval) - *acct_interim_interval = - entry->acct_interim_interval; - if (vlan_id) - *vlan_id = entry->vlan_id; - if (psk) - os_memcpy(psk, entry->psk, PMK_LEN); - if (has_psk) - *has_psk = entry->has_psk; - return entry->accepted; - } + for (entry = hapd->acl_cache; entry; entry = entry->next) { + if (os_memcmp(entry->addr, addr, ETH_ALEN) != 0) + continue; - entry = entry->next; + if (now.sec - entry->timestamp > RADIUS_ACL_TIMEOUT) + return -1; /* entry has expired */ + if (entry->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT) + if (session_timeout) + *session_timeout = entry->session_timeout; + if (acct_interim_interval) + *acct_interim_interval = + entry->acct_interim_interval; + if (vlan_id) + *vlan_id = entry->vlan_id; + if (psk) + os_memcpy(psk, entry->psk, PMK_LEN); + if (has_psk) + *has_psk = entry->has_psk; + if (identity) { + if (entry->identity) + *identity = os_strdup(entry->identity); + else + *identity = NULL; + } + if (radius_cui) { + if (entry->radius_cui) + *radius_cui = os_strdup(entry->radius_cui); + else + *radius_cui = NULL; + } + return entry->accepted; } return -1; @@ -140,37 +161,9 @@ static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr, goto fail; } - if (hapd->conf->own_ip_addr.af == AF_INET && - !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS, - (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) { - wpa_printf(MSG_DEBUG, "Could not add NAS-IP-Address"); - goto fail; - } - -#ifdef CONFIG_IPV6 - if (hapd->conf->own_ip_addr.af == AF_INET6 && - !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS, - (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) { - wpa_printf(MSG_DEBUG, "Could not add NAS-IPv6-Address"); - goto fail; - } -#endif /* CONFIG_IPV6 */ - - if (hapd->conf->nas_identifier && - !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER, - (u8 *) hapd->conf->nas_identifier, - os_strlen(hapd->conf->nas_identifier))) { - wpa_printf(MSG_DEBUG, "Could not add NAS-Identifier"); + if (add_common_radius_attr(hapd, hapd->conf->radius_auth_req_attr, + NULL, msg) < 0) goto fail; - } - - os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", - MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid); - if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID, - (u8 *) buf, os_strlen(buf))) { - wpa_printf(MSG_DEBUG, "Could not add Called-Station-Id"); - goto fail; - } os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, MAC2STR(addr)); @@ -180,12 +173,6 @@ static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr, goto fail; } - if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE, - RADIUS_NAS_PORT_TYPE_IEEE_802_11)) { - wpa_printf(MSG_DEBUG, "Could not add NAS-Port-Type"); - goto fail; - } - os_snprintf(buf, sizeof(buf), "CONNECT 11Mbps 802.11b"); if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, (u8 *) buf, os_strlen(buf))) { @@ -215,12 +202,18 @@ static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr, * @vlan_id: Buffer for returning VLAN ID * @psk: Buffer for returning WPA PSK * @has_psk: Buffer for indicating whether psk was filled + * @identity: Buffer for returning identity (from RADIUS) + * @radius_cui: Buffer for returning CUI (from RADIUS) * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING + * + * The caller is responsible for freeing the returned *identity and *radius_cui + * values with os_free(). */ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, const u8 *msg, size_t len, u32 *session_timeout, u32 *acct_interim_interval, int *vlan_id, - u8 *psk, int *has_psk) + u8 *psk, int *has_psk, char **identity, + char **radius_cui) { if (session_timeout) *session_timeout = 0; @@ -232,6 +225,10 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, *has_psk = 0; if (psk) os_memset(psk, 0, PMK_LEN); + if (identity) + *identity = NULL; + if (radius_cui) + *radius_cui = NULL; if (hostapd_maclist_found(hapd->conf->accept_mac, hapd->conf->num_accept_mac, addr, vlan_id)) @@ -256,7 +253,8 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, /* Check whether ACL cache has an entry for this station */ int res = hostapd_acl_cache_get(hapd, addr, session_timeout, acct_interim_interval, - vlan_id, psk, has_psk); + vlan_id, psk, has_psk, + identity, radius_cui); if (res == HOSTAPD_ACL_ACCEPT || res == HOSTAPD_ACL_ACCEPT_TIMEOUT) return res; @@ -268,6 +266,14 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, if (os_memcmp(query->addr, addr, ETH_ALEN) == 0) { /* pending query in RADIUS retransmit queue; * do not generate a new one */ + if (identity) { + os_free(*identity); + *identity = NULL; + } + if (radius_cui) { + os_free(*radius_cui); + *radius_cui = NULL; + } return HOSTAPD_ACL_PENDING; } query = query->next; @@ -333,7 +339,7 @@ static void hostapd_acl_expire_cache(struct hostapd_data *hapd, os_time_t now) hostapd_drv_set_radius_acl_expire(hapd, entry->addr); tmp = entry; entry = entry->next; - os_free(tmp); + hostapd_acl_cache_free_entry(tmp); continue; } @@ -450,6 +456,8 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, if (hdr->code == RADIUS_CODE_ACCESS_ACCEPT) { int passphraselen; char *passphrase; + u8 *buf; + size_t len; if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT, &cache->session_timeout) == 0) @@ -491,6 +499,19 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, } os_free(passphrase); } + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME, + &buf, &len, NULL) == 0) { + cache->identity = os_zalloc(len + 1); + if (cache->identity) + os_memcpy(cache->identity, buf, len); + } + if (radius_msg_get_attr_ptr( + msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, + &buf, &len, NULL) == 0) { + cache->radius_cui = os_zalloc(len + 1); + if (cache->radius_cui) + os_memcpy(cache->radius_cui, buf, len); + } if (hapd->conf->wpa_psk_radius == PSK_RADIUS_REQUIRED && !cache->has_psk) diff --git a/src/ap/ieee802_11_auth.h b/src/ap/ieee802_11_auth.h index b8a4c138..0e8d1cb1 100644 --- a/src/ap/ieee802_11_auth.h +++ b/src/ap/ieee802_11_auth.h @@ -19,7 +19,8 @@ enum { int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, const u8 *msg, size_t len, u32 *session_timeout, u32 *acct_interim_interval, int *vlan_id, - u8 *psk, int *has_psk); + u8 *psk, int *has_psk, char **identity, + char **radius_cui); int hostapd_acl_init(struct hostapd_data *hapd); void hostapd_acl_deinit(struct hostapd_data *hapd); diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c index 3ad33c89..7599ef8d 100644 --- a/src/ap/ieee802_11_vht.c +++ b/src/ap/ieee802_11_vht.c @@ -61,12 +61,48 @@ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid) oper = (struct ieee80211_vht_operation *) pos; os_memset(oper, 0, sizeof(*oper)); + /* + * center freq = 5 GHz + (5 * index) + * So index 42 gives center freq 5.210 GHz + * which is channel 42 in 5G band + */ + oper->vht_op_info_chan_center_freq_seg0_idx = + hapd->iconf->vht_oper_centr_freq_seg0_idx; + oper->vht_op_info_chwidth = hapd->iconf->vht_oper_chwidth; /* VHT Basic MCS set comes from hw */ /* Hard code 1 stream, MCS0-7 is a min Basic VHT MCS rates */ - oper->vht_basic_mcs_set = 0xfffc; + oper->vht_basic_mcs_set = host_to_le16(0xfffc); pos += sizeof(*oper); return pos; } + + +u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *vht_capab, size_t vht_capab_len) +{ + /* 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) { + sta->flags &= ~WLAN_STA_VHT; + os_free(sta->vht_capabilities); + sta->vht_capabilities = NULL; + return WLAN_STATUS_SUCCESS; + } + + if (sta->vht_capabilities == NULL) { + sta->vht_capabilities = + os_zalloc(sizeof(struct ieee80211_vht_capabilities)); + if (sta->vht_capabilities == NULL) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + sta->flags |= WLAN_STA_VHT; + os_memcpy(sta->vht_capabilities, vht_capab, + sizeof(struct ieee80211_vht_capabilities)); + + return WLAN_STATUS_SUCCESS; +} diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c index 9bbd1ff2..c4d3da82 100644 --- a/src/ap/ieee802_1x.c +++ b/src/ap/ieee802_1x.c @@ -126,7 +126,7 @@ static void ieee802_1x_tx_key_one(struct hostapd_data *hapd, hdr = (struct ieee802_1x_hdr *) buf; key = (struct ieee802_1x_eapol_key *) (hdr + 1); key->type = EAPOL_KEY_TYPE_RC4; - key->key_length = htons(key_len); + WPA_PUT_BE16(key->key_length, key_len); wpa_get_ntp_timestamp(key->replay_counter); if (random_get_bytes(key->key_iv, sizeof(key->key_iv))) { @@ -409,97 +409,163 @@ static void ieee802_1x_learn_identity(struct hostapd_data *hapd, } -static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, - struct sta_info *sta, - const u8 *eap, size_t len) +static int add_common_radius_sta_attr(struct hostapd_data *hapd, + struct hostapd_radius_attr *req_attr, + struct sta_info *sta, + struct radius_msg *msg) { - struct radius_msg *msg; char buf[128]; - struct eapol_state_machine *sm = sta->eapol_sm; - struct hostapd_radius_attr *attr; - - if (sm == NULL) - return; - ieee802_1x_learn_identity(hapd, sm, eap, len); + if (!hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_NAS_PORT) && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) { + wpa_printf(MSG_ERROR, "Could not add NAS-Port"); + return -1; + } - wpa_printf(MSG_DEBUG, "Encapsulating EAP message into a RADIUS " - "packet"); + os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, + MAC2STR(sta->addr)); + buf[sizeof(buf) - 1] = '\0'; + if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID, + (u8 *) buf, os_strlen(buf))) { + wpa_printf(MSG_ERROR, "Could not add Calling-Station-Id"); + return -1; + } - sm->radius_identifier = radius_client_get_id(hapd->radius); - msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST, - sm->radius_identifier); - if (msg == NULL) { - printf("Could not create net RADIUS packet\n"); - return; + if (sta->flags & WLAN_STA_PREAUTH) { + os_strlcpy(buf, "IEEE 802.11i Pre-Authentication", + sizeof(buf)); + } else { + os_snprintf(buf, sizeof(buf), "CONNECT %d%sMbps %s", + radius_sta_rate(hapd, sta) / 2, + (radius_sta_rate(hapd, sta) & 1) ? ".5" : "", + radius_mode_txt(hapd)); + buf[sizeof(buf) - 1] = '\0'; + } + if (!hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_CONNECT_INFO) && + !radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, + (u8 *) buf, os_strlen(buf))) { + wpa_printf(MSG_ERROR, "Could not add Connect-Info"); + return -1; } - radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta)); + return 0; +} - if (sm->identity && - !radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, - sm->identity, sm->identity_len)) { - printf("Could not add User-Name\n"); - goto fail; - } - if (!hostapd_config_get_radius_attr(hapd->conf->radius_auth_req_attr, +int add_common_radius_attr(struct hostapd_data *hapd, + struct hostapd_radius_attr *req_attr, + struct sta_info *sta, + struct radius_msg *msg) +{ + char buf[128]; + struct hostapd_radius_attr *attr; + + if (!hostapd_config_get_radius_attr(req_attr, RADIUS_ATTR_NAS_IP_ADDRESS) && hapd->conf->own_ip_addr.af == AF_INET && !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS, (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) { - printf("Could not add NAS-IP-Address\n"); - goto fail; + wpa_printf(MSG_ERROR, "Could not add NAS-IP-Address"); + return -1; } #ifdef CONFIG_IPV6 - if (!hostapd_config_get_radius_attr(hapd->conf->radius_auth_req_attr, + if (!hostapd_config_get_radius_attr(req_attr, RADIUS_ATTR_NAS_IPV6_ADDRESS) && hapd->conf->own_ip_addr.af == AF_INET6 && !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS, (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) { - printf("Could not add NAS-IPv6-Address\n"); - goto fail; + wpa_printf(MSG_ERROR, "Could not add NAS-IPv6-Address"); + return -1; } #endif /* CONFIG_IPV6 */ - if (!hostapd_config_get_radius_attr(hapd->conf->radius_auth_req_attr, + if (!hostapd_config_get_radius_attr(req_attr, RADIUS_ATTR_NAS_IDENTIFIER) && hapd->conf->nas_identifier && !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER, (u8 *) hapd->conf->nas_identifier, os_strlen(hapd->conf->nas_identifier))) { - printf("Could not add NAS-Identifier\n"); - goto fail; - } - - if (!hostapd_config_get_radius_attr(hapd->conf->radius_auth_req_attr, - RADIUS_ATTR_NAS_PORT) && - !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) { - printf("Could not add NAS-Port\n"); - goto fail; + wpa_printf(MSG_ERROR, "Could not add NAS-Identifier"); + return -1; } os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", - MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid); + MAC2STR(hapd->own_addr), + wpa_ssid_txt(hapd->conf->ssid.ssid, + hapd->conf->ssid.ssid_len)); buf[sizeof(buf) - 1] = '\0'; - if (!hostapd_config_get_radius_attr(hapd->conf->radius_auth_req_attr, + if (!hostapd_config_get_radius_attr(req_attr, RADIUS_ATTR_CALLED_STATION_ID) && !radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID, (u8 *) buf, os_strlen(buf))) { - printf("Could not add Called-Station-Id\n"); - goto fail; + wpa_printf(MSG_ERROR, "Could not add Called-Station-Id"); + return -1; } - os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, - MAC2STR(sta->addr)); - buf[sizeof(buf) - 1] = '\0'; - if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID, - (u8 *) buf, os_strlen(buf))) { - printf("Could not add Calling-Station-Id\n"); + if (!hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_NAS_PORT_TYPE) && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE, + RADIUS_NAS_PORT_TYPE_IEEE_802_11)) { + wpa_printf(MSG_ERROR, "Could not add NAS-Port-Type"); + return -1; + } + + if (sta && add_common_radius_sta_attr(hapd, req_attr, sta, msg) < 0) + return -1; + + for (attr = req_attr; attr; attr = attr->next) { + if (!radius_msg_add_attr(msg, attr->type, + wpabuf_head(attr->val), + wpabuf_len(attr->val))) { + wpa_printf(MSG_ERROR, "Could not add RADIUS " + "attribute"); + return -1; + } + } + + return 0; +} + + +static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, + struct sta_info *sta, + const u8 *eap, size_t len) +{ + struct radius_msg *msg; + struct eapol_state_machine *sm = sta->eapol_sm; + + if (sm == NULL) + return; + + ieee802_1x_learn_identity(hapd, sm, eap, len); + + wpa_printf(MSG_DEBUG, "Encapsulating EAP message into a RADIUS " + "packet"); + + sm->radius_identifier = radius_client_get_id(hapd->radius); + msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST, + sm->radius_identifier); + if (msg == NULL) { + printf("Could not create net RADIUS packet\n"); + return; + } + + radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta)); + + if (sm->identity && + !radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, + sm->identity, sm->identity_len)) { + printf("Could not add User-Name\n"); goto fail; } + if (add_common_radius_attr(hapd, hapd->conf->radius_auth_req_attr, sta, + msg) < 0) + goto fail; + /* TODO: should probably check MTU from driver config; 2304 is max for * IEEE 802.11, but use 1400 to avoid problems with too large packets */ @@ -510,32 +576,6 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, goto fail; } - if (!hostapd_config_get_radius_attr(hapd->conf->radius_auth_req_attr, - RADIUS_ATTR_NAS_PORT_TYPE) && - !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE, - RADIUS_NAS_PORT_TYPE_IEEE_802_11)) { - printf("Could not add NAS-Port-Type\n"); - goto fail; - } - - if (sta->flags & WLAN_STA_PREAUTH) { - os_strlcpy(buf, "IEEE 802.11i Pre-Authentication", - sizeof(buf)); - } else { - os_snprintf(buf, sizeof(buf), "CONNECT %d%sMbps %s", - radius_sta_rate(hapd, sta) / 2, - (radius_sta_rate(hapd, sta) & 1) ? ".5" : "", - radius_mode_txt(hapd)); - buf[sizeof(buf) - 1] = '\0'; - } - if (!hostapd_config_get_radius_attr(hapd->conf->radius_auth_req_attr, - RADIUS_ATTR_CONNECT_INFO) && - !radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, - (u8 *) buf, os_strlen(buf))) { - printf("Could not add Connect-Info\n"); - goto fail; - } - if (eap && !radius_msg_add_eap(msg, eap, len)) { printf("Could not add EAP-Message\n"); goto fail; @@ -577,17 +617,6 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, } } - for (attr = hapd->conf->radius_auth_req_attr; attr; attr = attr->next) - { - if (!radius_msg_add_attr(msg, attr->type, - wpabuf_head(attr->val), - wpabuf_len(attr->val))) { - wpa_printf(MSG_ERROR, "Could not add RADIUS " - "attribute"); - goto fail; - } - } - if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, sta->addr) < 0) goto fail; @@ -693,7 +722,8 @@ ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta) flags |= EAPOL_SM_FROM_PMKSA_CACHE; } return eapol_auth_alloc(hapd->eapol_auth, sta->addr, flags, - sta->wps_ie, sta->p2p_ie, sta); + sta->wps_ie, sta->p2p_ie, sta, + sta->identity, sta->radius_cui); } @@ -1037,9 +1067,8 @@ void ieee802_1x_free_station(struct sta_info *sta) static void ieee802_1x_decapsulate_radius(struct hostapd_data *hapd, struct sta_info *sta) { - u8 *eap; - size_t len; - struct eap_hdr *hdr; + struct wpabuf *eap; + const struct eap_hdr *hdr; int eap_type = -1; char buf[64]; struct radius_msg *msg; @@ -1053,7 +1082,7 @@ static void ieee802_1x_decapsulate_radius(struct hostapd_data *hapd, msg = sm->last_recv_radius; - eap = radius_msg_get_eap(msg, &len); + eap = radius_msg_get_eap(msg); if (eap == NULL) { /* RFC 3579, Chap. 2.6.3: * RADIUS server SHOULD NOT send Access-Reject/no EAP-Message @@ -1065,19 +1094,19 @@ static void ieee802_1x_decapsulate_radius(struct hostapd_data *hapd, return; } - if (len < sizeof(*hdr)) { + if (wpabuf_len(eap) < sizeof(*hdr)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_WARNING, "too short EAP packet " "received from authentication server"); - os_free(eap); + wpabuf_free(eap); sm->eap_if->aaaEapNoReq = TRUE; return; } - if (len > sizeof(*hdr)) - eap_type = eap[sizeof(*hdr)]; + if (wpabuf_len(eap) > sizeof(*hdr)) + eap_type = (wpabuf_head_u8(eap))[sizeof(*hdr)]; - hdr = (struct eap_hdr *) eap; + hdr = wpabuf_head(eap); switch (hdr->code) { case EAP_CODE_REQUEST: if (eap_type >= 0) @@ -1112,7 +1141,7 @@ static void ieee802_1x_decapsulate_radius(struct hostapd_data *hapd, sm->eap_if->aaaEapReq = TRUE; wpabuf_free(sm->eap_if->aaaEapReqData); - sm->eap_if->aaaEapReqData = wpabuf_alloc_ext_data(eap, len); + sm->eap_if->aaaEapReqData = eap; } @@ -1177,7 +1206,7 @@ static void ieee802_1x_store_radius_class(struct hostapd_data *hapd, if (count <= 0) return; - nclass = os_zalloc(count * sizeof(struct radius_attr_data)); + nclass = os_calloc(count, sizeof(struct radius_attr_data)); if (nclass == NULL) return; diff --git a/src/ap/ieee802_1x.h b/src/ap/ieee802_1x.h index f9b05caf..e1df9405 100644 --- a/src/ap/ieee802_1x.h +++ b/src/ap/ieee802_1x.h @@ -1,6 +1,6 @@ /* * hostapd / IEEE 802.1X-2004 Authenticator - * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -14,38 +14,8 @@ struct sta_info; struct eapol_state_machine; struct hostapd_config; struct hostapd_bss_config; - -#ifdef _MSC_VER -#pragma pack(push, 1) -#endif /* _MSC_VER */ - -/* RFC 3580, 4. RC4 EAPOL-Key Frame */ - -struct ieee802_1x_eapol_key { - u8 type; - u16 key_length; - u8 replay_counter[8]; /* does not repeat within the life of the keying - * material used to encrypt the Key field; - * 64-bit NTP timestamp MAY be used here */ - u8 key_iv[16]; /* cryptographically random number */ - u8 key_index; /* key flag in the most significant bit: - * 0 = broadcast (default key), - * 1 = unicast (key mapping key); key index is in the - * 7 least significant bits */ - u8 key_signature[16]; /* HMAC-MD5 message integrity check computed with - * MS-MPPE-Send-Key as the key */ - - /* followed by key: if packet body length = 44 + key length, then the - * key field (of key_length bytes) contains the key in encrypted form; - * if packet body length = 44, key field is absent and key_length - * represents the number of least significant octets from - * MS-MPPE-Send-Key attribute to be used as the keying material; - * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */ -} STRUCT_PACKED; - -#ifdef _MSC_VER -#pragma pack(pop) -#endif /* _MSC_VER */ +struct hostapd_radius_attr; +struct radius_msg; void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, @@ -83,4 +53,9 @@ char *eap_type_text(u8 type); const char *radius_mode_txt(struct hostapd_data *hapd); int radius_sta_rate(struct hostapd_data *hapd, struct sta_info *sta); +int add_common_radius_attr(struct hostapd_data *hapd, + struct hostapd_radius_attr *req_attr, + struct sta_info *sta, + struct radius_msg *msg); + #endif /* IEEE802_1X_H */ diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c index 95b701cc..d61177fd 100644 --- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c @@ -235,6 +235,8 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) os_free(sta->ht_capabilities); os_free(sta->psk); + os_free(sta->identity); + os_free(sta->radius_cui); os_free(sta); } @@ -795,8 +797,9 @@ static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx) ap_check_sa_query_timeout(hapd, sta)) return; - nbuf = os_realloc(sta->sa_query_trans_id, - (sta->sa_query_count + 1) * WLAN_SA_QUERY_TR_ID_LEN); + nbuf = os_realloc_array(sta->sa_query_trans_id, + sta->sa_query_count + 1, + WLAN_SA_QUERY_TR_ID_LEN); if (nbuf == NULL) return; if (sta->sa_query_count == 0) { @@ -818,9 +821,7 @@ static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx) HOSTAPD_LEVEL_DEBUG, "association SA Query attempt %d", sta->sa_query_count); -#ifdef NEED_AP_MLME ieee802_11_send_sa_query_req(hapd, sta->addr, trans_id); -#endif /* NEED_AP_MLME */ } diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h index cef428df..b3c57b43 100644 --- a/src/ap/sta_info.h +++ b/src/ap/sta_info.h @@ -28,6 +28,7 @@ #define WLAN_STA_ASSOC_REQ_OK BIT(15) #define WLAN_STA_WPS2 BIT(16) #define WLAN_STA_GAS BIT(17) +#define WLAN_STA_VHT BIT(18) #define WLAN_STA_PENDING_DISASSOC_CB BIT(29) #define WLAN_STA_PENDING_DEAUTH_CB BIT(30) #define WLAN_STA_NONERP BIT(31) @@ -96,7 +97,11 @@ struct sta_info { int vlan_id; u8 *psk; /* PSK from RADIUS authentication server */ + char *identity; /* User-Name from RADIUS */ + char *radius_cui; /* Chargeable-User-Identity from RADIUS */ + struct ieee80211_ht_capabilities *ht_capabilities; + struct ieee80211_vht_capabilities *vht_capabilities; #ifdef CONFIG_IEEE80211W int sa_query_count; /* number of pending SA Query requests; diff --git a/src/ap/utils.c b/src/ap/utils.c index 3e9fc081..931968c8 100644 --- a/src/ap/utils.c +++ b/src/ap/utils.c @@ -23,8 +23,8 @@ int hostapd_register_probereq_cb(struct hostapd_data *hapd, { struct hostapd_probereq_cb *n; - n = os_realloc(hapd->probereq_cb, (hapd->num_probereq_cb + 1) * - sizeof(struct hostapd_probereq_cb)); + n = os_realloc_array(hapd->probereq_cb, hapd->num_probereq_cb + 1, + sizeof(struct hostapd_probereq_cb)); if (n == NULL) return -1; @@ -78,7 +78,8 @@ void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr) struct prune_data data; data.hapd = hapd; data.addr = addr; - if (hapd->iface->for_each_interface) - hapd->iface->for_each_interface(hapd->iface->interfaces, - prune_associations, &data); + if (hapd->iface->interfaces && + hapd->iface->interfaces->for_each_interface) + hapd->iface->interfaces->for_each_interface( + hapd->iface->interfaces, prune_associations, &data); } diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c index f2f766f2..7b1a9e67 100644 --- a/src/ap/vlan_init.c +++ b/src/ap/vlan_init.c @@ -21,6 +21,7 @@ #include "ap_config.h" #include "ap_drv_ops.h" #include "vlan_init.h" +#include "vlan_util.h" #ifdef CONFIG_FULL_DYNAMIC_VLAN @@ -335,7 +336,9 @@ static int br_getnumports(const char *br_name) } -static int vlan_rem(const char *if_name) +#ifndef CONFIG_VLAN_NETLINK + +int vlan_rem(const char *if_name) { int fd; struct vlan_ioctl_args if_request; @@ -378,7 +381,7 @@ static int vlan_rem(const char *if_name) returns 1 if the interface already exists returns 0 otherwise */ -static int vlan_add(const char *if_name, int vid) +int vlan_add(const char *if_name, int vid, const char *vlan_if_name) { int fd; struct vlan_ioctl_args if_request; @@ -474,6 +477,8 @@ static int vlan_set_name_type(unsigned int name_type) return 0; } +#endif /* CONFIG_VLAN_NETLINK */ + static void vlan_newlink(char *ifname, struct hostapd_data *hapd) { @@ -481,6 +486,7 @@ static void vlan_newlink(char *ifname, struct hostapd_data *hapd) char br_name[IFNAMSIZ]; struct hostapd_vlan *vlan = hapd->conf->vlan; char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; + int vlan_naming = hapd->conf->ssid.vlan_naming; wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname); @@ -496,13 +502,22 @@ static void vlan_newlink(char *ifname, struct hostapd_data *hapd) ifconfig_up(br_name); if (tagged_interface) { - - if (!vlan_add(tagged_interface, vlan->vlan_id)) + if (vlan_naming == + DYNAMIC_VLAN_NAMING_WITH_DEVICE) + os_snprintf(vlan_ifname, + sizeof(vlan_ifname), + "%s.%d", tagged_interface, + vlan->vlan_id); + else + os_snprintf(vlan_ifname, + sizeof(vlan_ifname), + "vlan%d", vlan->vlan_id); + + ifconfig_up(tagged_interface); + if (!vlan_add(tagged_interface, vlan->vlan_id, + vlan_ifname)) vlan->clean |= DVLAN_CLEAN_VLAN; - os_snprintf(vlan_ifname, sizeof(vlan_ifname), - "vlan%d", vlan->vlan_id); - if (!br_addif(br_name, vlan_ifname)) vlan->clean |= DVLAN_CLEAN_VLAN_PORT; @@ -527,6 +542,7 @@ static void vlan_dellink(char *ifname, struct hostapd_data *hapd) char br_name[IFNAMSIZ]; struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan; char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; + int vlan_naming = hapd->conf->ssid.vlan_naming; wpa_printf(MSG_DEBUG, "VLAN: vlan_dellink(%s)", ifname); @@ -541,8 +557,16 @@ static void vlan_dellink(char *ifname, struct hostapd_data *hapd) br_delif(br_name, vlan->ifname); if (tagged_interface) { - os_snprintf(vlan_ifname, sizeof(vlan_ifname), - "vlan%d", vlan->vlan_id); + if (vlan_naming == + DYNAMIC_VLAN_NAMING_WITH_DEVICE) + os_snprintf(vlan_ifname, + sizeof(vlan_ifname), + "%s.%d", tagged_interface, + vlan->vlan_id); + else + os_snprintf(vlan_ifname, + sizeof(vlan_ifname), + "vlan%d", vlan->vlan_id); if (vlan->clean & DVLAN_CLEAN_VLAN_PORT) br_delif(br_name, vlan_ifname); ifconfig_down(vlan_ifname); @@ -682,7 +706,12 @@ full_dynamic_vlan_init(struct hostapd_data *hapd) if (priv == NULL) return NULL; - vlan_set_name_type(VLAN_NAME_TYPE_PLUS_VID_NO_PAD); +#ifndef CONFIG_VLAN_NETLINK + vlan_set_name_type(hapd->conf->ssid.vlan_naming == + DYNAMIC_VLAN_NAMING_WITH_DEVICE ? + VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD : + VLAN_NAME_TYPE_PLUS_VID_NO_PAD); +#endif /* CONFIG_VLAN_NETLINK */ priv->s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (priv->s < 0) { diff --git a/src/ap/vlan_util.c b/src/ap/vlan_util.c new file mode 100644 index 00000000..cc54051b --- /dev/null +++ b/src/ap/vlan_util.c @@ -0,0 +1,177 @@ +/* + * hostapd / VLAN netlink api + * Copyright (c) 2012, Michael Braun <michael-dev@fami-braun.de> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#include <sys/ioctl.h> +#include <linux/sockios.h> +#include <linux/if_vlan.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/family.h> +#include <netlink/genl/ctrl.h> +#include <netlink/route/link.h> +#include <netlink/route/link/vlan.h> + +#include "utils/common.h" +#include "utils/eloop.h" +#include "hostapd.h" +#include "vlan_util.h" + +/* + * Add a vlan interface with name 'vlan_if_name', VLAN ID 'vid' and + * tagged interface 'if_name'. + * + * returns -1 on error + * returns 1 if the interface already exists + * returns 0 otherwise +*/ +int vlan_add(const char *if_name, int vid, const char *vlan_if_name) +{ + int ret = -1; + struct nl_sock *handle = NULL; + struct nl_cache *cache = NULL; + struct rtnl_link *rlink = NULL; + int if_idx = 0; + + wpa_printf(MSG_DEBUG, "VLAN: vlan_add(if_name=%s, vid=%d, " + "vlan_if_name=%s)", if_name, vid, vlan_if_name); + + if ((os_strlen(if_name) + 1) > IFNAMSIZ) { + wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'", + if_name); + return -1; + } + + if ((os_strlen(vlan_if_name) + 1) > IFNAMSIZ) { + wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'", + vlan_if_name); + return -1; + } + + handle = nl_socket_alloc(); + if (!handle) { + wpa_printf(MSG_ERROR, "VLAN: failed to open netlink socket"); + goto vlan_add_error; + } + + if (nl_connect(handle, NETLINK_ROUTE) < 0) { + wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink"); + goto vlan_add_error; + } + + if (rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache) < 0) { + cache = NULL; + wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache"); + goto vlan_add_error; + } + + if (!(if_idx = rtnl_link_name2i(cache, if_name))) { + /* link does not exist */ + wpa_printf(MSG_ERROR, "VLAN: interface %s does not exist", + if_name); + goto vlan_add_error; + } + + if ((rlink = rtnl_link_get_by_name(cache, vlan_if_name))) { + /* link does exist */ + rtnl_link_put(rlink); + rlink = NULL; + wpa_printf(MSG_ERROR, "VLAN: interface %s already exists", + vlan_if_name); + ret = 1; + goto vlan_add_error; + } + + rlink = rtnl_link_alloc(); + if (!rlink) { + wpa_printf(MSG_ERROR, "VLAN: failed to allocate new link"); + goto vlan_add_error; + } + + if (rtnl_link_set_type(rlink, "vlan") < 0) { + wpa_printf(MSG_ERROR, "VLAN: failed to set link type"); + goto vlan_add_error; + } + + rtnl_link_set_link(rlink, if_idx); + rtnl_link_set_name(rlink, vlan_if_name); + + if (rtnl_link_vlan_set_id(rlink, vid) < 0) { + wpa_printf(MSG_ERROR, "VLAN: failed to set link vlan id"); + goto vlan_add_error; + } + + if (rtnl_link_add(handle, rlink, NLM_F_CREATE) < 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); + goto vlan_add_error; + } + + ret = 0; + +vlan_add_error: + if (rlink) + rtnl_link_put(rlink); + if (cache) + nl_cache_free(cache); + if (handle) + nl_socket_free(handle); + return ret; +} + + +int vlan_rem(const char *if_name) +{ + int ret = -1; + struct nl_sock *handle = NULL; + struct nl_cache *cache = NULL; + struct rtnl_link *rlink = NULL; + + wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(if_name=%s)", if_name); + + handle = nl_socket_alloc(); + if (!handle) { + wpa_printf(MSG_ERROR, "VLAN: failed to open netlink socket"); + goto vlan_rem_error; + } + + if (nl_connect(handle, NETLINK_ROUTE) < 0) { + wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink"); + goto vlan_rem_error; + } + + if (rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache) < 0) { + cache = NULL; + wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache"); + goto vlan_rem_error; + } + + if (!(rlink = rtnl_link_get_by_name(cache, if_name))) { + /* link does not exist */ + wpa_printf(MSG_ERROR, "VLAN: interface %s does not exists", + if_name); + goto vlan_rem_error; + } + + if (rtnl_link_delete(handle, rlink) < 0) { + wpa_printf(MSG_ERROR, "VLAN: failed to remove link %s", + if_name); + goto vlan_rem_error; + } + + ret = 0; + +vlan_rem_error: + if (rlink) + rtnl_link_put(rlink); + if (cache) + nl_cache_free(cache); + if (handle) + nl_socket_free(handle); + return ret; +} diff --git a/src/ap/vlan_util.h b/src/ap/vlan_util.h new file mode 100644 index 00000000..bef5a16f --- /dev/null +++ b/src/ap/vlan_util.h @@ -0,0 +1,15 @@ +/* + * hostapd / VLAN netlink api + * Copyright (c) 2012, Michael Braun <michael-dev@fami-braun.de> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef VLAN_UTIL_H +#define VLAN_UTIL_H + +int vlan_add(const char *if_name, int vid, const char *vlan_if_name); +int vlan_rem(const char *if_name); + +#endif /* VLAN_UTIL_H */ diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c new file mode 100644 index 00000000..25944045 --- /dev/null +++ b/src/ap/wnm_ap.c @@ -0,0 +1,258 @@ +/* + * hostapd - WNM + * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "ap/hostapd.h" +#include "ap/sta_info.h" +#include "ap/ap_config.h" +#include "ap/ap_drv_ops.h" +#include "ap/wpa_auth.h" +#include "wnm_ap.h" + +#define MAX_TFS_IE_LEN 1024 + +#ifdef CONFIG_IEEE80211V + +/* get the TFS IE from driver */ +static int ieee80211_11_get_tfs_ie(struct hostapd_data *hapd, const u8 *addr, + u8 *buf, u16 *buf_len, enum wnm_oper oper) +{ + wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper); + + return hostapd_drv_wnm_oper(hapd, oper, addr, buf, buf_len); +} + + +/* set the TFS IE to driver */ +static int ieee80211_11_set_tfs_ie(struct hostapd_data *hapd, const u8 *addr, + u8 *buf, u16 *buf_len, enum wnm_oper oper) +{ + wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper); + + return hostapd_drv_wnm_oper(hapd, oper, addr, buf, buf_len); +} + + +/* MLME-SLEEPMODE.response */ +static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, + const u8 *addr, u8 dialog_token, + u8 action_type, u16 intval) +{ + struct ieee80211_mgmt *mgmt; + int res; + size_t len; + size_t gtk_elem_len = 0; + size_t igtk_elem_len = 0; + struct wnm_sleep_element wnmsleep_ie; + u8 *wnmtfs_ie; + u8 wnmsleep_ie_len; + u16 wnmtfs_ie_len; + u8 *pos; + struct sta_info *sta; + enum wnm_oper tfs_oper = action_type == 0 ? WNM_SLEEP_TFS_RESP_IE_ADD : + WNM_SLEEP_TFS_RESP_IE_NONE; + + sta = ap_get_sta(hapd, addr); + if (sta == NULL) { + wpa_printf(MSG_DEBUG, "%s: station not found", __func__); + return -EINVAL; + } + + /* WNM-Sleep Mode IE */ + os_memset(&wnmsleep_ie, 0, sizeof(struct wnm_sleep_element)); + wnmsleep_ie_len = sizeof(struct wnm_sleep_element); + wnmsleep_ie.eid = WLAN_EID_WNMSLEEP; + wnmsleep_ie.len = wnmsleep_ie_len - 2; + wnmsleep_ie.action_type = action_type; + wnmsleep_ie.status = WNM_STATUS_SLEEP_ACCEPT; + wnmsleep_ie.intval = intval; + + /* TFS IE(s) */ + wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN); + if (wnmtfs_ie == NULL) + return -1; + if (ieee80211_11_get_tfs_ie(hapd, addr, wnmtfs_ie, &wnmtfs_ie_len, + tfs_oper)) { + wnmtfs_ie_len = 0; + os_free(wnmtfs_ie); + wnmtfs_ie = NULL; + } + +#define MAX_GTK_SUBELEM_LEN 45 +#define MAX_IGTK_SUBELEM_LEN 26 + mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + + MAX_GTK_SUBELEM_LEN + MAX_IGTK_SUBELEM_LEN); + if (mgmt == NULL) { + wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for " + "WNM-Sleep Response action frame"); + return -1; + } + os_memcpy(mgmt->da, addr, ETH_ALEN); + os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); + os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN); + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + mgmt->u.action.category = WLAN_ACTION_WNM; + mgmt->u.action.u.wnm_sleep_resp.action = WNM_SLEEP_MODE_RESP; + mgmt->u.action.u.wnm_sleep_resp.dialogtoken = dialog_token; + pos = (u8 *)mgmt->u.action.u.wnm_sleep_resp.variable; + /* add key data if MFP is enabled */ + if (wpa_auth_uses_mfp(sta->wpa_sm) || action_type != 1){ + mgmt->u.action.u.wnm_sleep_resp.keydata_len = 0; + } else { + gtk_elem_len = wpa_wnmsleep_gtk_subelem(sta->wpa_sm, pos); + pos += gtk_elem_len; + wpa_printf(MSG_DEBUG, "Pass 4, gtk_len = %d", + (int) gtk_elem_len); +#ifdef CONFIG_IEEE80211W + res = wpa_wnmsleep_igtk_subelem(sta->wpa_sm, pos); + if (res < 0) { + os_free(wnmtfs_ie); + os_free(mgmt); + return -1; + } + igtk_elem_len = res; + pos += igtk_elem_len; + wpa_printf(MSG_DEBUG, "Pass 4 igtk_len = %d", + (int) igtk_elem_len); +#endif /* CONFIG_IEEE80211W */ + + WPA_PUT_LE16((u8 *) + &mgmt->u.action.u.wnm_sleep_resp.keydata_len, + gtk_elem_len + igtk_elem_len); + } + os_memcpy(pos, &wnmsleep_ie, wnmsleep_ie_len); + /* copy TFS IE here */ + pos += wnmsleep_ie_len; + os_memcpy(pos, wnmtfs_ie, wnmtfs_ie_len); + + len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_resp) + gtk_elem_len + + igtk_elem_len + wnmsleep_ie_len + wnmtfs_ie_len; + + /* In driver, response frame should be forced to sent when STA is in + * PS mode */ + res = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, + mgmt->da, &mgmt->u.action.category, len); + + if (!res) { + wpa_printf(MSG_DEBUG, "Successfully send WNM-Sleep Response " + "frame"); + + /* when entering wnmsleep + * 1. pause the node in driver + * 2. mark the node so that AP won't update GTK/IGTK during + * WNM Sleep + */ + if (wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT && + wnmsleep_ie.action_type == 0) { + hostapd_drv_wnm_oper(hapd, WNM_SLEEP_ENTER_CONFIRM, + addr, NULL, NULL); + wpa_set_wnmsleep(sta->wpa_sm, 1); + } + /* when exiting wnmsleep + * 1. unmark the node + * 2. start GTK/IGTK update if MFP is not used + * 3. unpause the node in driver + */ + if (wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT && + wnmsleep_ie.action_type == 1) { + wpa_set_wnmsleep(sta->wpa_sm, 0); + hostapd_drv_wnm_oper(hapd, WNM_SLEEP_EXIT_CONFIRM, + addr, NULL, NULL); + if (wpa_auth_uses_mfp(sta->wpa_sm) && action_type == 1) + wpa_wnmsleep_rekey_gtk(sta->wpa_sm); + } + } else + wpa_printf(MSG_DEBUG, "Fail to send WNM-Sleep Response frame"); + +#undef MAX_GTK_SUBELEM_LEN +#undef MAX_IGTK_SUBELEM_LEN + os_free(wnmtfs_ie); + os_free(mgmt); + return res; +} + + +static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd, + const u8 *addr, const u8 *frm, int len) +{ + /* + * Action [1] | Dialog Token [1] | WNM-Sleep Mode IE | + * TFS Response IE + */ + u8 *pos = (u8 *) frm; /* point to action field */ + u8 dialog_token = pos[1]; + struct wnm_sleep_element *wnmsleep_ie = NULL; + /* multiple TFS Req IE (assuming consecutive) */ + u8 *tfsreq_ie_start = NULL; + u8 *tfsreq_ie_end = NULL; + u16 tfsreq_ie_len = 0; + + pos += 1 + 1; + while (pos - frm < len - 1) { + u8 ie_len = *(pos+1); + if (*pos == WLAN_EID_WNMSLEEP) + wnmsleep_ie = (struct wnm_sleep_element *)pos; + else if (*pos == WLAN_EID_TFS_REQ) { + if (!tfsreq_ie_start) + tfsreq_ie_start = pos; + tfsreq_ie_end = pos; + } else + wpa_printf(MSG_DEBUG, "EID %d not recognized", *pos); + pos += ie_len + 2; + } + + if (!wnmsleep_ie) { + wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found"); + return; + } + + if (wnmsleep_ie->action_type == 0 && tfsreq_ie_start && + tfsreq_ie_end && tfsreq_ie_end - tfsreq_ie_start >= 0) { + tfsreq_ie_len = (tfsreq_ie_end + tfsreq_ie_end[1] + 2) - + tfsreq_ie_start; + wpa_printf(MSG_DEBUG, "TFS Req IE(s) found"); + /* pass the TFS Req IE(s) to driver for processing */ + if (ieee80211_11_set_tfs_ie(hapd, addr, tfsreq_ie_start, + &tfsreq_ie_len, + WNM_SLEEP_TFS_REQ_IE_SET)) + wpa_printf(MSG_DEBUG, "Fail to set TFS Req IE"); + } + + ieee802_11_send_wnmsleep_resp(hapd, addr, dialog_token, + wnmsleep_ie->action_type, + wnmsleep_ie->intval); + + if (wnmsleep_ie->action_type == 1) { + /* clear the tfs after sending the resp frame */ + ieee80211_11_set_tfs_ie(hapd, addr, tfsreq_ie_start, + &tfsreq_ie_len, WNM_SLEEP_TFS_IE_DEL); + } +} + + +void ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd, + struct rx_action *action) +{ + u8 *pos = (u8 *) action->data + 1; /* point to the action field */ + u8 act = *pos; + + switch (act) { + case WNM_SLEEP_MODE_REQ: + ieee802_11_rx_wnmsleep_req(hapd, action->sa, action->data + 1, + action->len); + break; + default: + break; + } +} + +#endif /* CONFIG_IEEE80211V */ diff --git a/src/ap/wnm_ap.h b/src/ap/wnm_ap.h new file mode 100644 index 00000000..ab7c4f1c --- /dev/null +++ b/src/ap/wnm_ap.h @@ -0,0 +1,17 @@ +/* + * IEEE 802.11v WNM related functions and structures + * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WNM_AP_H +#define WNM_AP_H + +struct rx_action; + +void ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd, + struct rx_action *action); + +#endif /* WNM_AP_H */ diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 1d942a44..3203d4fb 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -284,6 +284,9 @@ static void wpa_group_set_key_len(struct wpa_group *group, int cipher) case WPA_CIPHER_CCMP: group->GTK_len = 16; break; + case WPA_CIPHER_GCMP: + group->GTK_len = 16; + break; case WPA_CIPHER_TKIP: group->GTK_len = 32; break; @@ -849,7 +852,8 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, if (msg == REQUEST || msg == PAIRWISE_2 || msg == PAIRWISE_4 || msg == GROUP_2) { u16 ver = key_info & WPA_KEY_INFO_TYPE_MASK; - if (sm->pairwise == WPA_CIPHER_CCMP) { + if (sm->pairwise == WPA_CIPHER_CCMP || + sm->pairwise == WPA_CIPHER_GCMP) { if (wpa_use_aes_cmac(sm) && ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) { wpa_auth_logger(wpa_auth, sm->addr, @@ -865,7 +869,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, "did not use HMAC-SHA1-AES " - "with CCMP"); + "with CCMP/GCMP"); return; } } @@ -1240,7 +1244,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, version = force_version; else if (wpa_use_aes_cmac(sm)) version = WPA_KEY_INFO_TYPE_AES_128_CMAC; - else if (sm->pairwise == WPA_CIPHER_CCMP) + else if (sm->pairwise != WPA_CIPHER_TKIP) version = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; else version = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; @@ -1291,6 +1295,9 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, case WPA_CIPHER_CCMP: WPA_PUT_BE16(key->key_length, 16); break; + case WPA_CIPHER_GCMP: + WPA_PUT_BE16(key->key_length, 16); + break; case WPA_CIPHER_TKIP: WPA_PUT_BE16(key->key_length, 32); break; @@ -1538,6 +1545,8 @@ static enum wpa_alg wpa_alg_enum(int alg) switch (alg) { case WPA_CIPHER_CCMP: return WPA_ALG_CCMP; + case WPA_CIPHER_GCMP: + return WPA_ALG_GCMP; case WPA_CIPHER_TKIP: return WPA_ALG_TKIP; case WPA_CIPHER_WEP104: @@ -1773,7 +1782,7 @@ SM_STATE(WPA_PTK, PTKSTART) static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *pmk, struct wpa_ptk *ptk) { - size_t ptk_len = sm->pairwise == WPA_CIPHER_CCMP ? 48 : 64; + size_t ptk_len = sm->pairwise != WPA_CIPHER_TKIP ? 48 : 64; #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) return wpa_auth_derive_ptk_ft(sm, pmk, ptk, ptk_len); @@ -1898,6 +1907,14 @@ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, igtk.pn) < 0) os_memset(igtk.pn, 0, sizeof(igtk.pn)); os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN); + if (sm->wpa_auth->conf.disable_gtk) { + /* + * Provide unique random IGTK to each STA to prevent use of + * IGTK in the BSS. + */ + if (random_get_bytes(igtk.igtk, WPA_IGTK_LEN) < 0) + return pos; + } pos = wpa_add_kde(pos, RSN_KEY_DATA_IGTK, (const u8 *) &igtk, sizeof(igtk), NULL, 0); @@ -1922,7 +1939,7 @@ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) SM_STATE(WPA_PTK, PTKINITNEGOTIATING) { - u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos; + u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos, dummy_gtk[32]; size_t gtk_len, kde_len; struct wpa_group *gsm = sm->group; u8 *wpa_ie; @@ -1960,6 +1977,15 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) secure = 1; gtk = gsm->GTK[gsm->GN - 1]; gtk_len = gsm->GTK_len; + if (sm->wpa_auth->conf.disable_gtk) { + /* + * Provide unique random GTK to each STA to prevent use + * of GTK in the BSS. + */ + if (random_get_bytes(dummy_gtk, gtk_len) < 0) + return; + gtk = dummy_gtk; + } keyidx = gsm->GN; _rsc = rsc; encr = 1; @@ -2076,6 +2102,9 @@ SM_STATE(WPA_PTK, PTKINITDONE) if (sm->pairwise == WPA_CIPHER_TKIP) { alg = WPA_ALG_TKIP; klen = 32; + } else if (sm->pairwise == WPA_CIPHER_GCMP) { + alg = WPA_ALG_GCMP; + klen = 16; } else { alg = WPA_ALG_CCMP; klen = 16; @@ -2256,6 +2285,7 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) struct wpa_group *gsm = sm->group; u8 *kde, *pos, hdr[2]; size_t kde_len; + u8 *gtk, dummy_gtk[32]; SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group); @@ -2276,6 +2306,16 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "sending 1/2 msg of Group Key Handshake"); + gtk = gsm->GTK[gsm->GN - 1]; + if (sm->wpa_auth->conf.disable_gtk) { + /* + * Provide unique random GTK to each STA to prevent use + * of GTK in the BSS. + */ + if (random_get_bytes(dummy_gtk, gsm->GTK_len) < 0) + return; + gtk = dummy_gtk; + } if (sm->wpa == WPA_VERSION_WPA2) { kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len + ieee80211w_kde_len(sm); @@ -2287,10 +2327,10 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) hdr[0] = gsm->GN & 0x03; hdr[1] = 0; pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2, - gsm->GTK[gsm->GN - 1], gsm->GTK_len); + gtk, gsm->GTK_len); pos = ieee80211w_kde_add(sm, pos); } else { - kde = gsm->GTK[gsm->GN - 1]; + kde = gtk; pos = kde + gsm->GTK_len; } @@ -2416,6 +2456,9 @@ static void wpa_group_gtk_init(struct wpa_authenticator *wpa_auth, static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx) { + if (ctx != NULL && ctx != sm->group) + return 0; + if (sm->wpa_ptk_state != WPA_PTK_PTKINITDONE) { wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "Not in PTKINITDONE; skip Group Key update"); @@ -2433,6 +2476,12 @@ static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx) "marking station for GTK rekeying"); } +#ifdef CONFIG_IEEE80211V + /* Do not rekey GTK/IGTK when STA is in wnmsleep */ + if (sm->is_wnmsleep) + return 0; +#endif /* CONFIG_IEEE80211V */ + sm->group->GKeyDoneStations++; sm->GUpdateStationKeys = TRUE; @@ -2441,6 +2490,132 @@ static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx) } +#ifdef CONFIG_IEEE80211V +/* update GTK when exiting wnmsleep mode */ +void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm) +{ + if (sm->is_wnmsleep) + return; + + wpa_group_update_sta(sm, NULL); +} + + +void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag) +{ + sm->is_wnmsleep = !!flag; +} + + +int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos) +{ + u8 *subelem; + struct wpa_group *gsm = sm->group; + size_t subelem_len, pad_len; + const u8 *key; + size_t key_len; + u8 keybuf[32]; + + /* GTK subslement */ + key_len = gsm->GTK_len; + if (key_len > sizeof(keybuf)) + return 0; + + /* + * Pad key for AES Key Wrap if it is not multiple of 8 bytes or is less + * than 16 bytes. + */ + pad_len = key_len % 8; + if (pad_len) + pad_len = 8 - pad_len; + if (key_len + pad_len < 16) + pad_len += 8; + if (pad_len) { + os_memcpy(keybuf, gsm->GTK[gsm->GN - 1], key_len); + os_memset(keybuf + key_len, 0, pad_len); + keybuf[key_len] = 0xdd; + key_len += pad_len; + key = keybuf; + } else + key = gsm->GTK[gsm->GN - 1]; + + /* + * Sub-elem ID[1] | Length[1] | Key Info[2] | Key Length[1] | RSC[8] | + * Key[5..32] | 8 padding. + */ + subelem_len = 13 + key_len + 8; + subelem = os_zalloc(subelem_len); + if (subelem == NULL) + return 0; + + subelem[0] = WNM_SLEEP_SUBELEM_GTK; + subelem[1] = 11 + key_len + 8; + /* Key ID in B0-B1 of Key Info */ + WPA_PUT_LE16(&subelem[2], gsm->GN & 0x03); + subelem[4] = gsm->GTK_len; + if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, subelem + 5) != 0) + { + os_free(subelem); + return 0; + } + if (aes_wrap(sm->PTK.kek, key_len / 8, key, subelem + 13)) { + os_free(subelem); + return 0; + } + + os_memcpy(pos, subelem, subelem_len); + + wpa_hexdump_key(MSG_DEBUG, "Plaintext GTK", + gsm->GTK[gsm->GN - 1], gsm->GTK_len); + os_free(subelem); + + return subelem_len; +} + + +#ifdef CONFIG_IEEE80211W +int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos) +{ + u8 *subelem, *ptr; + struct wpa_group *gsm = sm->group; + size_t subelem_len; + + /* IGTK subelement + * Sub-elem ID[1] | Length[1] | KeyID[2] | PN[6] | + * Key[16] | 8 padding */ + subelem_len = 1 + 1 + 2 + 6 + WPA_IGTK_LEN + 8; + subelem = os_zalloc(subelem_len); + if (subelem == NULL) + return 0; + + ptr = subelem; + *ptr++ = WNM_SLEEP_SUBELEM_IGTK; + *ptr++ = subelem_len - 2; + WPA_PUT_LE16(ptr, gsm->GN_igtk); + ptr += 2; + if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, ptr) != 0) { + os_free(subelem); + return 0; + } + ptr += 6; + if (aes_wrap(sm->PTK.kek, WPA_IGTK_LEN / 8, + gsm->IGTK[gsm->GN_igtk - 4], ptr)) { + os_free(subelem); + return -1; + } + + os_memcpy(pos, subelem, subelem_len); + + wpa_hexdump_key(MSG_DEBUG, "Plaintext IGTK", + gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN); + os_free(subelem); + + return subelem_len; +} +#endif /* CONFIG_IEEE80211W */ +#endif /* CONFIG_IEEE80211V */ + + static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { @@ -2470,7 +2645,7 @@ static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth, group->GKeyDoneStations); group->GKeyDoneStations = 0; } - wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, NULL); + wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, group); wpa_printf(MSG_DEBUG, "wpa_group_setkeys: GKeyDoneStations=%d", group->GKeyDoneStations); } @@ -2627,6 +2802,8 @@ static int wpa_cipher_bits(int cipher) switch (cipher) { case WPA_CIPHER_CCMP: return 128; + case WPA_CIPHER_GCMP: + return 128; case WPA_CIPHER_TKIP: return 256; case WPA_CIPHER_WEP104: @@ -2758,6 +2935,8 @@ int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen) } else if (sm->wpa == WPA_VERSION_WPA2) { if (sm->pairwise == WPA_CIPHER_CCMP) pairwise = RSN_CIPHER_SUITE_CCMP; + else if (sm->pairwise == WPA_CIPHER_GCMP) + pairwise = RSN_CIPHER_SUITE_GCMP; else if (sm->pairwise == WPA_CIPHER_TKIP) pairwise = RSN_CIPHER_SUITE_TKIP; else if (sm->pairwise == WPA_CIPHER_WEP104) diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h index a07779fa..91ba4991 100644 --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -158,6 +158,8 @@ struct wpa_auth_config { int pmk_r1_push; int ft_over_ds; #endif /* CONFIG_IEEE80211R */ + int disable_gtk; + int ap_mlme; }; typedef enum { @@ -196,6 +198,8 @@ struct wpa_auth_callbacks { struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr); int (*send_ft_action)(void *ctx, const u8 *dst, const u8 *data, size_t data_len); + int (*add_tspec)(void *ctx, const u8 *sta_addr, u8 *tspec_ie, + size_t tspec_ielen); #endif /* CONFIG_IEEE80211R */ }; @@ -278,4 +282,13 @@ int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr); #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211V +void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm); +void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag); +int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos); +#ifdef CONFIG_IEEE80211W +int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos); +#endif /* CONFIG_IEEE80211W */ +#endif /* CONFIG_IEEE80211V */ + #endif /* WPA_AUTH_H */ diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c index d2ec0886..9f7cdae3 100644 --- a/src/ap/wpa_auth_ft.c +++ b/src/ap/wpa_auth_ft.c @@ -52,6 +52,19 @@ wpa_ft_add_sta(struct wpa_authenticator *wpa_auth, const u8 *sta_addr) } +static int wpa_ft_add_tspec(struct wpa_authenticator *wpa_auth, + const u8 *sta_addr, + u8 *tspec_ie, size_t tspec_ielen) +{ + if (wpa_auth->cb.add_tspec == NULL) { + wpa_printf(MSG_DEBUG, "FT: add_tspec is not initialized"); + return -1; + } + return wpa_auth->cb.add_tspec(wpa_auth->cb.ctx, sta_addr, tspec_ie, + tspec_ielen); +} + + int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len) { u8 *pos = buf; @@ -471,7 +484,8 @@ static u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len) #endif /* CONFIG_IEEE80211W */ -static u8 * wpa_ft_process_rdie(u8 *pos, u8 *end, u8 id, u8 descr_count, +static u8 * wpa_ft_process_rdie(struct wpa_state_machine *sm, + u8 *pos, u8 *end, u8 id, u8 descr_count, const u8 *ies, size_t ies_len) { struct ieee802_11_elems parse; @@ -504,7 +518,7 @@ static u8 * wpa_ft_process_rdie(u8 *pos, u8 *end, u8 id, u8 descr_count, } #ifdef NEED_AP_MLME - if (parse.wmm_tspec) { + if (parse.wmm_tspec && sm->wpa_auth->conf.ap_mlme) { struct wmm_tspec_element *tspec; int res; @@ -541,13 +555,35 @@ static u8 * wpa_ft_process_rdie(u8 *pos, u8 *end, u8 id, u8 descr_count, } #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)); + if (res >= 0) { + if (res) + rdie->status_code = host_to_le16(res); + else { + /* TSPEC accepted; include updated TSPEC in + * response */ + rdie->descr_count = 1; + pos += sizeof(*tspec); + } + return pos; + } + } + wpa_printf(MSG_DEBUG, "FT: No supported resource requested"); rdie->status_code = host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE); return pos; } -static u8 * wpa_ft_process_ric(u8 *pos, u8 *end, const u8 *ric, size_t ric_len) +static u8 * wpa_ft_process_ric(struct wpa_state_machine *sm, u8 *pos, u8 *end, + const u8 *ric, size_t ric_len) { const u8 *rpos, *start; const struct rsn_rdie *rdie; @@ -569,7 +605,7 @@ static u8 * wpa_ft_process_ric(u8 *pos, u8 *end, const u8 *ric, size_t ric_len) break; rpos += 2 + rpos[1]; } - pos = wpa_ft_process_rdie(pos, end, rdie->id, + pos = wpa_ft_process_rdie(sm, pos, end, rdie->id, rdie->descr_count, start, rpos - start); } @@ -678,7 +714,8 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, ric_start = pos; if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse) == 0 && parse.ric) { - pos = wpa_ft_process_ric(pos, end, parse.ric, parse.ric_len); + pos = wpa_ft_process_ric(sm, pos, end, parse.ric, + parse.ric_len); if (auth_alg == WLAN_AUTH_FT) _ftie->mic_control[1] += ieee802_11_ie_count(ric_start, @@ -723,6 +760,9 @@ void wpa_ft_install_ptk(struct wpa_state_machine *sm) } else if (sm->pairwise == WPA_CIPHER_CCMP) { alg = WPA_ALG_CCMP; klen = 16; + } else if (sm->pairwise == WPA_CIPHER_GCMP) { + alg = WPA_ALG_GCMP; + klen = 16; } else { wpa_printf(MSG_DEBUG, "FT: Unknown pairwise alg 0x%x - skip " "PTK configuration", sm->pairwise); @@ -845,7 +885,7 @@ static u16 wpa_ft_process_auth_req(struct wpa_state_machine *sm, wpa_hexdump(MSG_DEBUG, "FT: Generated ANonce", sm->ANonce, WPA_NONCE_LEN); - ptk_len = pairwise != WPA_CIPHER_CCMP ? 64 : 48; + ptk_len = pairwise == WPA_CIPHER_TKIP ? 64 : 48; wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr, sm->wpa_auth->addr, pmk_r1_name, (u8 *) &sm->PTK, ptk_len, ptk_name); @@ -1061,8 +1101,16 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, if (os_memcmp(mic, ftie->mic, 16) != 0) { wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE"); + wpa_printf(MSG_DEBUG, "FT: addr=" MACSTR " auth_addr=" MACSTR, + MAC2STR(sm->addr), MAC2STR(sm->wpa_auth->addr)); wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", ftie->mic, 16); wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, 16); + wpa_hexdump(MSG_MSGDUMP, "FT: MDIE", + parse.mdie - 2, parse.mdie_len + 2); + wpa_hexdump(MSG_MSGDUMP, "FT: FTIE", + parse.ftie - 2, parse.ftie_len + 2); + wpa_hexdump(MSG_MSGDUMP, "FT: RSN", + parse.rsn - 2, parse.rsn_len + 2); return WLAN_STATUS_INVALID_FTIE; } diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c index 3e5ac1d8..bdc89e4f 100644 --- a/src/ap/wpa_auth_glue.c +++ b/src/ap/wpa_auth_glue.c @@ -70,6 +70,9 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, wconf->pmk_r1_push = conf->pmk_r1_push; wconf->ft_over_ds = conf->ft_over_ds; #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_HS20 + wconf->disable_gtk = conf->disable_dgaf; +#endif /* CONFIG_HS20 */ } @@ -181,9 +184,15 @@ static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr, { struct hostapd_data *hapd = ctx; struct sta_info *sta = ap_get_sta(hapd, addr); - if (sta && sta->psk) - return sta->psk; - return hostapd_get_psk(hapd->conf, addr, prev_psk); + const u8 *psk = hostapd_get_psk(hapd->conf, addr, prev_psk); + /* + * This is about to iterate over all psks, prev_psk gives the last + * returned psk which should not be returned again. + * logic list (all hostapd_get_psk; sta->psk) + */ + if (sta && sta->psk && !psk && sta->psk != prev_psk) + psk = sta->psk; + return psk; } @@ -294,12 +303,13 @@ static int hostapd_wpa_auth_for_each_auth( { struct hostapd_data *hapd = ctx; struct wpa_auth_iface_iter_data data; - if (hapd->iface->for_each_interface == NULL) + if (hapd->iface->interfaces == NULL || + hapd->iface->interfaces->for_each_interface == NULL) return -1; data.cb = cb; data.cb_ctx = cb_ctx; - return hapd->iface->for_each_interface(hapd->iface->interfaces, - wpa_auth_iface_iter, &data); + return hapd->iface->interfaces->for_each_interface( + hapd->iface->interfaces, wpa_auth_iface_iter, &data); } @@ -351,16 +361,17 @@ static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto, int ret; #ifdef CONFIG_IEEE80211R - if (proto == ETH_P_RRB && hapd->iface->for_each_interface) { + if (proto == ETH_P_RRB && hapd->iface->interfaces && + hapd->iface->interfaces->for_each_interface) { int res; struct wpa_auth_ft_iface_iter_data idata; idata.src_hapd = hapd; idata.dst = dst; idata.data = data; idata.data_len = data_len; - res = hapd->iface->for_each_interface(hapd->iface->interfaces, - hostapd_wpa_auth_ft_iter, - &idata); + res = hapd->iface->interfaces->for_each_interface( + hapd->iface->interfaces, hostapd_wpa_auth_ft_iter, + &idata); if (res == 1) return data_len; } @@ -425,6 +436,9 @@ hostapd_wpa_auth_add_sta(void *ctx, const u8 *sta_addr) struct hostapd_data *hapd = ctx; struct sta_info *sta; + if (hostapd_add_sta_node(hapd, sta_addr, WLAN_AUTH_FT) < 0) + return NULL; + sta = ap_sta_add(hapd, sta_addr); if (sta == NULL) return NULL; @@ -458,6 +472,14 @@ static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf, len - sizeof(*ethhdr)); } + +static int hostapd_wpa_auth_add_tspec(void *ctx, const u8 *sta_addr, + u8 *tspec_ie, size_t tspec_ielen) +{ + struct hostapd_data *hapd = ctx; + return hostapd_add_tspec(hapd, sta_addr, tspec_ie, tspec_ielen); +} + #endif /* CONFIG_IEEE80211R */ @@ -471,6 +493,8 @@ int hostapd_setup_wpa(struct hostapd_data *hapd) hostapd_wpa_auth_conf(hapd->conf, &_conf); if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EAPOL_TX_STATUS) _conf.tx_status = 1; + if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_MLME) + _conf.ap_mlme = 1; os_memset(&cb, 0, sizeof(cb)); cb.ctx = hapd; cb.logger = hostapd_wpa_auth_logger; @@ -489,6 +513,7 @@ int hostapd_setup_wpa(struct hostapd_data *hapd) #ifdef CONFIG_IEEE80211R cb.send_ft_action = hostapd_wpa_auth_send_ft_action; cb.add_sta = hostapd_wpa_auth_add_sta; + cb.add_tspec = hostapd_wpa_auth_add_tspec; #endif /* CONFIG_IEEE80211R */ hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb); if (hapd->wpa_auth == NULL) { diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h index b2235768..d5cf2c50 100644 --- a/src/ap/wpa_auth_i.h +++ b/src/ap/wpa_auth_i.h @@ -87,6 +87,9 @@ struct wpa_state_machine { unsigned int ft_completed:1; unsigned int pmk_r1_name_valid:1; #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211V + unsigned int is_wnmsleep:1; +#endif /* CONFIG_IEEE80211V */ u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN]; int req_replay_counter_used; diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c index f6871823..b88b80ac 100644 --- a/src/ap/wpa_auth_ie.c +++ b/src/ap/wpa_auth_ie.c @@ -123,6 +123,8 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, if (conf->wpa_group == WPA_CIPHER_CCMP) { RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); + } else if (conf->wpa_group == WPA_CIPHER_GCMP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP); } else if (conf->wpa_group == WPA_CIPHER_TKIP) { RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); } else if (conf->wpa_group == WPA_CIPHER_WEP104) { @@ -153,6 +155,11 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, pos += RSN_SELECTOR_LEN; num_suites++; } + if (conf->rsn_pairwise & WPA_CIPHER_GCMP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP); + pos += RSN_SELECTOR_LEN; + num_suites++; + } if (conf->rsn_pairwise & WPA_CIPHER_TKIP) { RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); pos += RSN_SELECTOR_LEN; @@ -453,6 +460,8 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, selector = RSN_CIPHER_SUITE_CCMP; if (data.pairwise_cipher & WPA_CIPHER_CCMP) selector = RSN_CIPHER_SUITE_CCMP; + else if (data.pairwise_cipher & WPA_CIPHER_GCMP) + selector = RSN_CIPHER_SUITE_GCMP; else if (data.pairwise_cipher & WPA_CIPHER_TKIP) selector = RSN_CIPHER_SUITE_TKIP; else if (data.pairwise_cipher & WPA_CIPHER_WEP104) @@ -466,6 +475,8 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, selector = RSN_CIPHER_SUITE_CCMP; if (data.group_cipher & WPA_CIPHER_CCMP) selector = RSN_CIPHER_SUITE_CCMP; + else if (data.group_cipher & WPA_CIPHER_GCMP) + selector = RSN_CIPHER_SUITE_GCMP; else if (data.group_cipher & WPA_CIPHER_TKIP) selector = RSN_CIPHER_SUITE_TKIP; else if (data.group_cipher & WPA_CIPHER_WEP104) @@ -607,6 +618,8 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, if (ciphers & WPA_CIPHER_CCMP) sm->pairwise = WPA_CIPHER_CCMP; + else if (ciphers & WPA_CIPHER_GCMP) + sm->pairwise = WPA_CIPHER_GCMP; else sm->pairwise = WPA_CIPHER_TKIP; diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c index 07ce06c1..5e44c72c 100644 --- a/src/ap/wps_hostapd.c +++ b/src/ap/wps_hostapd.c @@ -13,7 +13,6 @@ #include "utils/uuid.h" #include "crypto/dh_groups.h" #include "crypto/dh_group5.h" -#include "crypto/random.h" #include "common/wpa_ctrl.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" @@ -77,10 +76,11 @@ static int hostapd_wps_for_each(struct hostapd_data *hapd, struct wps_for_each_data data; data.func = func; data.ctx = ctx; - if (iface->for_each_interface == NULL) + if (iface->interfaces == NULL || + iface->interfaces->for_each_interface == NULL) return wps_for_each(iface, &data); - return iface->for_each_interface(iface->interfaces, wps_for_each, - &data); + return iface->interfaces->for_each_interface(iface->interfaces, + wps_for_each, &data); } @@ -257,7 +257,8 @@ static void wps_reload_config(void *eloop_data, void *user_ctx) struct hostapd_iface *iface = eloop_data; wpa_printf(MSG_DEBUG, "WPS: Reload configuration data"); - if (iface->reload_config(iface) < 0) { + if (iface->interfaces == NULL || + iface->interfaces->reload_config(iface) < 0) { wpa_printf(MSG_WARNING, "WPS: Failed to reload the updated " "configuration"); } @@ -344,6 +345,8 @@ static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx) } hapd->wps->wps_state = WPS_STATE_CONFIGURED; + if (hapd->iface->config_fname == NULL) + return 0; len = os_strlen(hapd->iface->config_fname) + 5; tmp_fname = os_malloc(len); if (tmp_fname == NULL) @@ -371,10 +374,17 @@ static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx) fprintf(nconf, "wps_state=2\n"); - fprintf(nconf, "ssid="); - for (i = 0; i < cred->ssid_len; i++) - fputc(cred->ssid[i], nconf); - fprintf(nconf, "\n"); + if (is_hex(cred->ssid, cred->ssid_len)) { + fprintf(nconf, "ssid2="); + for (i = 0; i < cred->ssid_len; i++) + fprintf(nconf, "%02x", cred->ssid[i]); + fprintf(nconf, "\n"); + } else { + fprintf(nconf, "ssid="); + for (i = 0; i < cred->ssid_len; i++) + fputc(cred->ssid[i], nconf); + fprintf(nconf, "\n"); + } if ((cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) && (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK))) @@ -464,6 +474,7 @@ static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx) multi_bss = 1; if (!multi_bss && (str_starts(buf, "ssid=") || + str_starts(buf, "ssid2=") || str_starts(buf, "auth_algs=") || str_starts(buf, "wep_default_key=") || str_starts(buf, "wep_key") || @@ -710,10 +721,12 @@ static int get_uuid_cb(struct hostapd_iface *iface, void *ctx) static const u8 * get_own_uuid(struct hostapd_iface *iface) { const u8 *uuid; - if (iface->for_each_interface == NULL) + if (iface->interfaces == NULL || + iface->interfaces->for_each_interface == NULL) return NULL; uuid = NULL; - iface->for_each_interface(iface->interfaces, get_uuid_cb, &uuid); + iface->interfaces->for_each_interface(iface->interfaces, get_uuid_cb, + &uuid); return uuid; } @@ -729,10 +742,11 @@ static int count_interface_cb(struct hostapd_iface *iface, void *ctx) static int interface_count(struct hostapd_iface *iface) { int count = 0; - if (iface->for_each_interface == NULL) + if (iface->interfaces == NULL || + iface->interfaces->for_each_interface == NULL) return 0; - iface->for_each_interface(iface->interfaces, count_interface_cb, - &count); + iface->interfaces->for_each_interface(iface->interfaces, + count_interface_cb, &count); return count; } diff --git a/src/common/defs.h b/src/common/defs.h index f057ffe3..db29b5df 100644 --- a/src/common/defs.h +++ b/src/common/defs.h @@ -26,6 +26,7 @@ typedef enum { FALSE = 0, TRUE = 1 } Boolean; #ifdef CONFIG_IEEE80211W #define WPA_CIPHER_AES_128_CMAC BIT(5) #endif /* CONFIG_IEEE80211W */ +#define WPA_CIPHER_GCMP BIT(6) #define WPA_KEY_MGMT_IEEE8021X BIT(0) #define WPA_KEY_MGMT_PSK BIT(1) @@ -91,7 +92,8 @@ enum wpa_alg { WPA_ALG_TKIP, WPA_ALG_CCMP, WPA_ALG_IGTK, - WPA_ALG_PMK + WPA_ALG_PMK, + WPA_ALG_GCMP }; /** @@ -102,7 +104,8 @@ enum wpa_cipher { CIPHER_WEP40, CIPHER_TKIP, CIPHER_CCMP, - CIPHER_WEP104 + CIPHER_WEP104, + CIPHER_GCMP }; /** diff --git a/src/common/eapol_common.h b/src/common/eapol_common.h index 4e392e7c..4811f38a 100644 --- a/src/common/eapol_common.h +++ b/src/common/eapol_common.h @@ -38,4 +38,44 @@ enum { IEEE802_1X_TYPE_EAP_PACKET = 0, enum { EAPOL_KEY_TYPE_RC4 = 1, EAPOL_KEY_TYPE_RSN = 2, EAPOL_KEY_TYPE_WPA = 254 }; + +#define IEEE8021X_REPLAY_COUNTER_LEN 8 +#define IEEE8021X_KEY_SIGN_LEN 16 +#define IEEE8021X_KEY_IV_LEN 16 + +#define IEEE8021X_KEY_INDEX_FLAG 0x80 +#define IEEE8021X_KEY_INDEX_MASK 0x03 + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +struct ieee802_1x_eapol_key { + u8 type; + /* Note: key_length is unaligned */ + u8 key_length[2]; + /* does not repeat within the life of the keying material used to + * encrypt the Key field; 64-bit NTP timestamp MAY be used here */ + u8 replay_counter[IEEE8021X_REPLAY_COUNTER_LEN]; + u8 key_iv[IEEE8021X_KEY_IV_LEN]; /* cryptographically random number */ + u8 key_index; /* key flag in the most significant bit: + * 0 = broadcast (default key), + * 1 = unicast (key mapping key); key index is in the + * 7 least significant bits */ + /* HMAC-MD5 message integrity check computed with MS-MPPE-Send-Key as + * the key */ + u8 key_signature[IEEE8021X_KEY_SIGN_LEN]; + + /* followed by key: if packet body length = 44 + key length, then the + * key field (of key_length bytes) contains the key in encrypted form; + * if packet body length = 44, key field is absent and key_length + * represents the number of least significant octets from + * MS-MPPE-Send-Key attribute to be used as the keying material; + * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */ +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + #endif /* EAPOL_COMMON_H */ diff --git a/src/common/gas.c b/src/common/gas.c index a67325c3..cff9254b 100644 --- a/src/common/gas.c +++ b/src/common/gas.c @@ -1,7 +1,7 @@ /* * Generic advertisement service (GAS) (IEEE 802.11u) * Copyright (c) 2009, Atheros Communications - * Copyright (c) 2011, Qualcomm Atheros + * Copyright (c) 2011-2012, Qualcomm Atheros * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -31,7 +31,7 @@ gas_build_req(u8 action, u8 dialog_token, size_t size) } -static struct wpabuf * gas_build_initial_req(u8 dialog_token, size_t size) +struct wpabuf * gas_build_initial_req(u8 dialog_token, size_t size) { return gas_build_req(WLAN_PA_GAS_INITIAL_REQ, dialog_token, size); diff --git a/src/common/gas.h b/src/common/gas.h index 8664a79e..306adc58 100644 --- a/src/common/gas.h +++ b/src/common/gas.h @@ -1,7 +1,7 @@ /* * Generic advertisement service (GAS) (IEEE 802.11u) * Copyright (c) 2009, Atheros Communications - * Copyright (c) 2011, Qualcomm Atheros + * Copyright (c) 2011-2012, Qualcomm Atheros * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -10,6 +10,7 @@ #ifndef GAS_H #define GAS_H +struct wpabuf * gas_build_initial_req(u8 dialog_token, size_t size); struct wpabuf * gas_build_comeback_req(u8 dialog_token); struct wpabuf * gas_build_initial_resp(u8 dialog_token, u16 status_code, u16 comeback_delay, size_t size); diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c index d65675cd..d9d3cd05 100644 --- a/src/common/ieee802_11_common.c +++ b/src/common/ieee802_11_common.c @@ -97,6 +97,11 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen, elems->p2p = pos; elems->p2p_len = elen; break; + case WFD_OUI_TYPE: + /* Wi-Fi Alliance - WFD IE */ + elems->wfd = pos; + elems->wfd_len = elen; + break; case HS20_INDICATION_OUI_TYPE: /* Hotspot 2.0 */ elems->hs20 = pos; @@ -253,6 +258,14 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, elems->ht_operation = pos; elems->ht_operation_len = elen; break; + case WLAN_EID_VHT_CAP: + elems->vht_capabilities = pos; + elems->vht_capabilities_len = elen; + break; + case WLAN_EID_VHT_OPERATION: + elems->vht_operation = pos; + elems->vht_operation_len = elen; + break; case WLAN_EID_LINK_ID: if (elen < 18) break; @@ -397,3 +410,75 @@ const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len) return NULL; } } + + +int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[], + const char *name, const char *val) +{ + int num, v; + const char *pos; + struct hostapd_wmm_ac_params *ac; + + /* skip 'wme_ac_' or 'wmm_ac_' prefix */ + pos = name + 7; + if (os_strncmp(pos, "be_", 3) == 0) { + num = 0; + pos += 3; + } else if (os_strncmp(pos, "bk_", 3) == 0) { + num = 1; + pos += 3; + } else if (os_strncmp(pos, "vi_", 3) == 0) { + num = 2; + pos += 3; + } else if (os_strncmp(pos, "vo_", 3) == 0) { + num = 3; + pos += 3; + } else { + wpa_printf(MSG_ERROR, "Unknown WMM name '%s'", pos); + return -1; + } + + ac = &wmm_ac_params[num]; + + if (os_strcmp(pos, "aifs") == 0) { + v = atoi(val); + if (v < 1 || v > 255) { + wpa_printf(MSG_ERROR, "Invalid AIFS value %d", v); + return -1; + } + ac->aifs = v; + } else if (os_strcmp(pos, "cwmin") == 0) { + v = atoi(val); + if (v < 0 || v > 12) { + wpa_printf(MSG_ERROR, "Invalid cwMin value %d", v); + return -1; + } + ac->cwmin = v; + } else if (os_strcmp(pos, "cwmax") == 0) { + v = atoi(val); + if (v < 0 || v > 12) { + wpa_printf(MSG_ERROR, "Invalid cwMax value %d", v); + return -1; + } + ac->cwmax = v; + } else if (os_strcmp(pos, "txop_limit") == 0) { + v = atoi(val); + if (v < 0 || v > 0xffff) { + wpa_printf(MSG_ERROR, "Invalid txop value %d", v); + return -1; + } + ac->txop_limit = v; + } else if (os_strcmp(pos, "acm") == 0) { + v = atoi(val); + if (v < 0 || v > 1) { + wpa_printf(MSG_ERROR, "Invalid acm value %d", v); + return -1; + } + ac->admission_control_mandatory = v; + } else { + wpa_printf(MSG_ERROR, "Unknown wmm_ac_ field '%s'", pos); + return -1; + } + + return 0; +} diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h index d9b2b6c1..bfc3eb27 100644 --- a/src/common/ieee802_11_common.h +++ b/src/common/ieee802_11_common.h @@ -33,8 +33,11 @@ struct ieee802_11_elems { const u8 *timeout_int; const u8 *ht_capabilities; const u8 *ht_operation; + const u8 *vht_capabilities; + const u8 *vht_operation; const u8 *vendor_ht_cap; const u8 *p2p; + const u8 *wfd; const u8 *link_id; const u8 *interworking; const u8 *hs20; @@ -63,8 +66,11 @@ struct ieee802_11_elems { u8 timeout_int_len; u8 ht_capabilities_len; u8 ht_operation_len; + u8 vht_capabilities_len; + u8 vht_operation_len; u8 vendor_ht_cap_len; u8 p2p_len; + u8 wfd_len; u8 interworking_len; u8 hs20_len; u8 ext_capab_len; @@ -81,4 +87,15 @@ struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len, struct ieee80211_hdr; const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len); +struct hostapd_wmm_ac_params { + int cwmin; + int cwmax; + int aifs; + int txop_limit; /* in units of 32us */ + int admission_control_mandatory; +}; + +int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[], + const char *name, const char *val); + #endif /* IEEE802_11_COMMON_H */ diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index b4f92759..2ab7fbf4 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -701,6 +701,8 @@ struct ieee80211_vht_operation { #define WPS_IE_VENDOR_TYPE 0x0050f204 #define OUI_WFA 0x506f9a #define P2P_IE_VENDOR_TYPE 0x506f9a09 +#define WFD_IE_VENDOR_TYPE 0x506f9a0a +#define WFD_OUI_TYPE 10 #define HS20_IE_VENDOR_TYPE 0x506f9a10 #define WMM_OUI_TYPE 2 @@ -923,6 +925,7 @@ enum p2p_service_protocol_type { P2P_SERV_BONJOUR = 1, P2P_SERV_UPNP = 2, P2P_SERV_WS_DISCOVERY = 3, + P2P_SERV_WIFI_DISPLAY = 4, P2P_SERV_VENDOR_SPECIFIC = 255 }; @@ -934,6 +937,20 @@ enum p2p_sd_status { }; +enum wifi_display_subelem { + WFD_SUBELEM_DEVICE_INFO = 0, + WFD_SUBELEM_ASSOCIATED_BSSID = 1, + WFD_SUBELEM_AUDIO_FORMATS = 2, + WFD_SUBELEM_VIDEO_FORMATS = 3, + WFD_SUBELEM_3D_VIDEO_FORMATS = 4, + WFD_SUBELEM_CONTENT_PROTECTION = 5, + WFD_SUBELEM_COUPLED_SINK = 6, + WFD_SUBELEM_EXT_CAPAB = 7, + WFD_SUBELEM_LOCAL_IP_ADDRESS = 8, + WFD_SUBELEM_SESSION_INFO = 9 +}; + + #define OUI_BROADCOM 0x00904c /* Broadcom (Epigram) */ #define VENDOR_HT_CAPAB_OUI_TYPE 0x33 /* 00-90-4c:0x33 */ @@ -946,6 +963,8 @@ enum p2p_sd_status { #define WLAN_CIPHER_SUITE_CCMP 0x000FAC04 #define WLAN_CIPHER_SUITE_WEP104 0x000FAC05 #define WLAN_CIPHER_SUITE_AES_CMAC 0x000FAC06 +#define WLAN_CIPHER_SUITE_NO_GROUP_ADDR 0x000FAC07 +#define WLAN_CIPHER_SUITE_GCMP 0x000FAC08 /* AKM suite selectors */ #define WLAN_AKM_SUITE_8021X 0x000FAC01 diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c index 7a013a80..36febb3a 100644 --- a/src/common/wpa_common.c +++ b/src/common/wpa_common.c @@ -43,8 +43,10 @@ int wpa_eapol_key_mic(const u8 *key, int ver, const u8 *buf, size_t len, u8 hash[SHA1_MAC_LEN]; switch (ver) { +#ifndef CONFIG_FIPS case WPA_KEY_INFO_TYPE_HMAC_MD5_RC4: return hmac_md5(key, 16, buf, len, mic); +#endif /* CONFIG_FIPS */ case WPA_KEY_INFO_TYPE_HMAC_SHA1_AES: if (hmac_sha1(key, 16, buf, len, hash)) return -1; @@ -350,6 +352,8 @@ static int rsn_selector_to_bitfield(const u8 *s) if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_AES_128_CMAC) return WPA_CIPHER_AES_128_CMAC; #endif /* CONFIG_IEEE80211W */ + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_GCMP) + return WPA_CIPHER_GCMP; return 0; } @@ -906,6 +910,8 @@ const char * wpa_cipher_txt(int cipher) return "CCMP"; case WPA_CIPHER_CCMP | WPA_CIPHER_TKIP: return "CCMP+TKIP"; + case WPA_CIPHER_GCMP: + return "GCMP"; default: return "UNKNOWN"; } diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h index 6b50997c..c871ae1b 100644 --- a/src/common/wpa_common.h +++ b/src/common/wpa_common.h @@ -64,6 +64,7 @@ #define RSN_CIPHER_SUITE_AES_128_CMAC RSN_SELECTOR(0x00, 0x0f, 0xac, 6) #endif /* CONFIG_IEEE80211W */ #define RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED RSN_SELECTOR(0x00, 0x0f, 0xac, 7) +#define RSN_CIPHER_SUITE_GCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 8) /* EAPOL-Key Key Data Encapsulation * GroupKey and PeerKey require encryption, otherwise, encryption is optional. @@ -83,6 +84,9 @@ #ifdef CONFIG_IEEE80211W #define RSN_KEY_DATA_IGTK RSN_SELECTOR(0x00, 0x0f, 0xac, 9) #endif /* CONFIG_IEEE80211W */ +#define RSN_KEY_DATA_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 10) +#define RSN_KEY_DATA_MULTIBAND_GTK RSN_SELECTOR(0x00, 0x0f, 0xac, 11) +#define RSN_KEY_DATA_MULTIBAND_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 12) #define WPA_OUI_TYPE RSN_SELECTOR(0x00, 0x50, 0xf2, 1) diff --git a/src/common/wpa_ctrl.c b/src/common/wpa_ctrl.c index b2b06832..58cbe6a0 100644 --- a/src/common/wpa_ctrl.c +++ b/src/common/wpa_ctrl.c @@ -12,7 +12,12 @@ #ifdef CONFIG_CTRL_IFACE_UNIX #include <sys/un.h> +#include <unistd.h> +#include <fcntl.h> #endif /* CONFIG_CTRL_IFACE_UNIX */ +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE +#include <netdb.h> +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ #ifdef ANDROID #include <dirent.h> @@ -44,6 +49,8 @@ struct wpa_ctrl { struct sockaddr_in local; struct sockaddr_in dest; char *cookie; + char *remote_ifname; + char *remote_ip; #endif /* CONFIG_CTRL_IFACE_UDP */ #ifdef CONFIG_CTRL_IFACE_UNIX int s; @@ -73,6 +80,7 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) int ret; size_t res; int tries = 0; + int flags; ctrl = os_malloc(sizeof(*ctrl)); if (ctrl == NULL) @@ -156,6 +164,19 @@ try_again: return NULL; } + /* + * Make socket non-blocking so that we don't hang forever if + * target dies unexpectedly. + */ + flags = fcntl(ctrl->s, F_GETFL); + if (flags >= 0) { + flags |= O_NONBLOCK; + if (fcntl(ctrl->s, F_SETFL, flags) < 0) { + perror("fcntl(ctrl->s, O_NONBLOCK)"); + /* Not fatal, continue on.*/ + } + } + return ctrl; } @@ -230,6 +251,9 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) struct wpa_ctrl *ctrl; char buf[128]; size_t len; +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + struct hostent *h; +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ ctrl = os_malloc(sizeof(*ctrl)); if (ctrl == NULL) @@ -244,7 +268,11 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) } ctrl->local.sin_family = AF_INET; +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + ctrl->local.sin_addr.s_addr = INADDR_ANY; +#else /* CONFIG_CTRL_IFACE_UDP_REMOTE */ ctrl->local.sin_addr.s_addr = htonl((127 << 24) | 1); +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ if (bind(ctrl->s, (struct sockaddr *) &ctrl->local, sizeof(ctrl->local)) < 0) { close(ctrl->s); @@ -255,10 +283,48 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) ctrl->dest.sin_family = AF_INET; ctrl->dest.sin_addr.s_addr = htonl((127 << 24) | 1); ctrl->dest.sin_port = htons(WPA_CTRL_IFACE_PORT); + +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + if (ctrl_path) { + char *port, *name; + int port_id; + + name = os_strdup(ctrl_path); + if (name == NULL) { + close(ctrl->s); + os_free(ctrl); + return NULL; + } + port = os_strchr(name, ':'); + + if (port) { + port_id = atoi(&port[1]); + port[0] = '\0'; + } else + port_id = WPA_CTRL_IFACE_PORT; + + h = gethostbyname(name); + ctrl->remote_ip = os_strdup(name); + os_free(name); + if (h == NULL) { + perror("gethostbyname"); + close(ctrl->s); + os_free(ctrl->remote_ip); + os_free(ctrl); + return NULL; + } + ctrl->dest.sin_port = htons(port_id); + os_memcpy(h->h_addr, (char *) &ctrl->dest.sin_addr.s_addr, + h->h_length); + } else + ctrl->remote_ip = os_strdup("localhost"); +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ + if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest, sizeof(ctrl->dest)) < 0) { perror("connect"); close(ctrl->s); + os_free(ctrl->remote_ip); os_free(ctrl); return NULL; } @@ -269,14 +335,31 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) ctrl->cookie = os_strdup(buf); } + if (wpa_ctrl_request(ctrl, "IFNAME", 6, buf, &len, NULL) == 0) { + buf[len] = '\0'; + ctrl->remote_ifname = os_strdup(buf); + } + return ctrl; } +char * wpa_ctrl_get_remote_ifname(struct wpa_ctrl *ctrl) +{ +#define WPA_CTRL_MAX_PS_NAME 100 + static char ps[WPA_CTRL_MAX_PS_NAME] = {}; + os_snprintf(ps, WPA_CTRL_MAX_PS_NAME, "%s/%s", + ctrl->remote_ip, ctrl->remote_ifname); + return ps; +} + + void wpa_ctrl_close(struct wpa_ctrl *ctrl) { close(ctrl->s); os_free(ctrl->cookie); + os_free(ctrl->remote_ifname); + os_free(ctrl->remote_ip); os_free(ctrl); } @@ -289,6 +372,7 @@ int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, void (*msg_cb)(char *msg, size_t len)) { struct timeval tv; + struct os_time started_at; int res; fd_set rfds; const char *_cmd; @@ -315,7 +399,30 @@ int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, _cmd_len = cmd_len; } + errno = 0; + started_at.sec = 0; + started_at.usec = 0; +retry_send: if (send(ctrl->s, _cmd, _cmd_len, 0) < 0) { + if (errno == EAGAIN || errno == EBUSY || errno == EWOULDBLOCK) + { + /* + * Must be a non-blocking socket... Try for a bit + * longer before giving up. + */ + if (started_at.sec == 0) + os_get_time(&started_at); + else { + struct os_time n; + os_get_time(&n); + /* Try for a few seconds. */ + if (n.sec > started_at.sec + 5) + goto send_err; + } + os_sleep(1, 0); + goto retry_send; + } + send_err: os_free(cmd_buf); return -1; } diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h index a440b69e..84f11953 100644 --- a/src/common/wpa_ctrl.h +++ b/src/common/wpa_ctrl.h @@ -48,6 +48,10 @@ extern "C" { #define WPA_EVENT_EAP_SUCCESS "CTRL-EVENT-EAP-SUCCESS " /** EAP authentication failed (EAP-Failure received) */ #define WPA_EVENT_EAP_FAILURE "CTRL-EVENT-EAP-FAILURE " +/** Network block temporarily disabled (e.g., due to authentication failure) */ +#define WPA_EVENT_TEMP_DISABLED "CTRL-EVENT-SSID-TEMP-DISABLED " +/** Temporarily disabled network block re-enabled */ +#define WPA_EVENT_REENABLED "CTRL-EVENT-SSID-REENABLED " /** New scan results available */ #define WPA_EVENT_SCAN_RESULTS "CTRL-EVENT-SCAN-RESULTS " /** wpa_supplicant state change */ @@ -133,6 +137,8 @@ extern "C" { #define INTERWORKING_AP "INTERWORKING-AP " #define INTERWORKING_NO_MATCH "INTERWORKING-NO-MATCH " +#define GAS_RESPONSE_INFO "GAS-RESPONSE-INFO " + /* hostapd control interface - fixed message prefixes */ #define WPS_EVENT_PIN_NEEDED "WPS-PIN-NEEDED " #define WPS_EVENT_NEW_AP_SETTINGS "WPS-NEW-AP-SETTINGS " @@ -164,6 +170,7 @@ extern "C" { #define WPA_BSS_MASK_WPS_SCAN BIT(13) #define WPA_BSS_MASK_P2P_SCAN BIT(14) #define WPA_BSS_MASK_INTERNETW BIT(15) +#define WPA_BSS_MASK_WIFI_DISPLAY BIT(16) /* wpa_supplicant/hostapd control interface access */ @@ -290,6 +297,8 @@ int wpa_ctrl_pending(struct wpa_ctrl *ctrl); */ int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl); +char * wpa_ctrl_get_remote_ifname(struct wpa_ctrl *ctrl); + #ifdef ANDROID /** * wpa_ctrl_cleanup() - Delete any local UNIX domain socket files that @@ -302,8 +311,11 @@ void wpa_ctrl_cleanup(void); #endif /* ANDROID */ #ifdef CONFIG_CTRL_IFACE_UDP +/* Port range for multiple wpa_supplicant instances and multiple VIFs */ #define WPA_CTRL_IFACE_PORT 9877 +#define WPA_CTRL_IFACE_PORT_LIMIT 50 /* decremented from start */ #define WPA_GLOBAL_CTRL_IFACE_PORT 9878 +#define WPA_GLOBAL_CTRL_IFACE_PORT_LIMIT 20 /* incremented from start */ #endif /* CONFIG_CTRL_IFACE_UDP */ diff --git a/src/crypto/Makefile b/src/crypto/Makefile index b221dd44..18b50548 100644 --- a/src/crypto/Makefile +++ b/src/crypto/Makefile @@ -31,16 +31,17 @@ LIB_OBJS= \ md4-internal.o \ md5.o \ md5-internal.o \ - md5-non-fips.o \ milenage.o \ ms_funcs.o \ rc4.o \ sha1.o \ sha1-internal.o \ sha1-pbkdf2.o \ + sha1-prf.o \ sha1-tlsprf.o \ sha1-tprf.o \ sha256.o \ + sha256-prf.o \ sha256-tlsprf.o \ sha256-internal.o diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index 7e61cb97..26b9acf2 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -41,21 +41,6 @@ int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); */ int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); -#ifdef CONFIG_FIPS -/** - * md5_vector_non_fips_allow - MD5 hash for data vector (non-FIPS use allowed) - * @num_elem: Number of elements in the data vector - * @addr: Pointers to the data areas - * @len: Lengths of the data blocks - * @mac: Buffer for the hash - * Returns: 0 on success, -1 on failure - */ -int md5_vector_non_fips_allow(size_t num_elem, const u8 *addr[], - const size_t *len, u8 *mac); -#else /* CONFIG_FIPS */ -#define md5_vector_non_fips_allow md5_vector -#endif /* CONFIG_FIPS */ - /** * sha1_vector - SHA-1 hash for data vector @@ -461,4 +446,15 @@ int __must_check crypto_mod_exp(const u8 *base, size_t base_len, int rc4_skip(const u8 *key, size_t keylen, size_t skip, u8 *data, size_t data_len); +/** + * crypto_get_random - Generate cryptographically strong pseudy-random bytes + * @buf: Buffer for data + * @len: Number of bytes to generate + * Returns: 0 on success, -1 on failure + * + * If the PRNG does not have enough entropy to ensure unpredictable byte + * sequence, this functions must return -1. + */ +int crypto_get_random(void *buf, size_t len); + #endif /* CRYPTO_H */ diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c index 8506fff4..711e312d 100644 --- a/src/crypto/crypto_openssl.c +++ b/src/crypto/crypto_openssl.c @@ -15,6 +15,10 @@ #include <openssl/evp.h> #include <openssl/dh.h> #include <openssl/hmac.h> +#include <openssl/rand.h> +#ifdef CONFIG_OPENSSL_CMAC +#include <openssl/cmac.h> +#endif /* CONFIG_OPENSSL_CMAC */ #include "common.h" #include "wpabuf.h" @@ -69,21 +73,14 @@ static BIGNUM * get_group5_prime(void) #define NO_SHA256_WRAPPER #endif -static int openssl_digest_vector(const EVP_MD *type, int non_fips, - size_t num_elem, const u8 *addr[], - const size_t *len, u8 *mac) +static int openssl_digest_vector(const EVP_MD *type, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) { EVP_MD_CTX ctx; size_t i; unsigned int mac_len; EVP_MD_CTX_init(&ctx); -#ifdef CONFIG_FIPS -#ifdef OPENSSL_FIPS - if (non_fips) - EVP_MD_CTX_set_flags(&ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW); -#endif /* OPENSSL_FIPS */ -#endif /* CONFIG_FIPS */ if (!EVP_DigestInit_ex(&ctx, type, NULL)) { wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestInit_ex failed: %s", ERR_error_string(ERR_get_error(), NULL)); @@ -109,7 +106,7 @@ static int openssl_digest_vector(const EVP_MD *type, int non_fips, int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { - return openssl_digest_vector(EVP_md4(), 0, num_elem, addr, len, mac); + return openssl_digest_vector(EVP_md4(), num_elem, addr, len, mac); } @@ -173,22 +170,13 @@ out: int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { - return openssl_digest_vector(EVP_md5(), 0, num_elem, addr, len, mac); + return openssl_digest_vector(EVP_md5(), num_elem, addr, len, mac); } -#ifdef CONFIG_FIPS -int md5_vector_non_fips_allow(size_t num_elem, const u8 *addr[], - const size_t *len, u8 *mac) -{ - return openssl_digest_vector(EVP_md5(), 1, num_elem, addr, len, mac); -} -#endif /* CONFIG_FIPS */ - - int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { - return openssl_digest_vector(EVP_sha1(), 0, num_elem, addr, len, mac); + return openssl_digest_vector(EVP_sha1(), num_elem, addr, len, mac); } @@ -196,60 +184,124 @@ int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { - return openssl_digest_vector(EVP_sha256(), 0, num_elem, addr, len, - mac); + return openssl_digest_vector(EVP_sha256(), num_elem, addr, len, mac); } #endif /* NO_SHA256_WRAPPER */ +static const EVP_CIPHER * aes_get_evp_cipher(size_t keylen) +{ + switch (keylen) { + case 16: + return EVP_aes_128_ecb(); + case 24: + return EVP_aes_192_ecb(); + case 32: + return EVP_aes_256_ecb(); + } + + return NULL; +} + + void * aes_encrypt_init(const u8 *key, size_t len) { - AES_KEY *ak; - ak = os_malloc(sizeof(*ak)); - if (ak == NULL) + EVP_CIPHER_CTX *ctx; + const EVP_CIPHER *type; + + type = aes_get_evp_cipher(len); + if (type == NULL) + return NULL; + + ctx = os_malloc(sizeof(*ctx)); + if (ctx == NULL) return NULL; - if (AES_set_encrypt_key(key, 8 * len, ak) < 0) { - os_free(ak); + EVP_CIPHER_CTX_init(ctx); + if (EVP_EncryptInit_ex(ctx, type, NULL, key, NULL) != 1) { + os_free(ctx); return NULL; } - return ak; + EVP_CIPHER_CTX_set_padding(ctx, 0); + return ctx; } void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) { - AES_encrypt(plain, crypt, ctx); + EVP_CIPHER_CTX *c = ctx; + int clen = 16; + if (EVP_EncryptUpdate(c, crypt, &clen, plain, 16) != 1) { + wpa_printf(MSG_ERROR, "OpenSSL: EVP_EncryptUpdate failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + } } void aes_encrypt_deinit(void *ctx) { - os_free(ctx); + EVP_CIPHER_CTX *c = ctx; + u8 buf[16]; + int len = sizeof(buf); + if (EVP_EncryptFinal_ex(c, buf, &len) != 1) { + wpa_printf(MSG_ERROR, "OpenSSL: EVP_EncryptFinal_ex failed: " + "%s", ERR_error_string(ERR_get_error(), NULL)); + } + if (len != 0) { + wpa_printf(MSG_ERROR, "OpenSSL: Unexpected padding length %d " + "in AES encrypt", len); + } + EVP_CIPHER_CTX_cleanup(c); + os_free(c); } void * aes_decrypt_init(const u8 *key, size_t len) { - AES_KEY *ak; - ak = os_malloc(sizeof(*ak)); - if (ak == NULL) + EVP_CIPHER_CTX *ctx; + const EVP_CIPHER *type; + + type = aes_get_evp_cipher(len); + if (type == NULL) return NULL; - if (AES_set_decrypt_key(key, 8 * len, ak) < 0) { - os_free(ak); + + ctx = os_malloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + EVP_CIPHER_CTX_init(ctx); + if (EVP_DecryptInit_ex(ctx, type, NULL, key, NULL) != 1) { + os_free(ctx); return NULL; } - return ak; + EVP_CIPHER_CTX_set_padding(ctx, 0); + return ctx; } void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) { - AES_decrypt(crypt, plain, ctx); + EVP_CIPHER_CTX *c = ctx; + int plen = 16; + if (EVP_DecryptUpdate(c, plain, &plen, crypt, 16) != 1) { + wpa_printf(MSG_ERROR, "OpenSSL: EVP_DecryptUpdate failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + } } void aes_decrypt_deinit(void *ctx) { + EVP_CIPHER_CTX *c = ctx; + u8 buf[16]; + int len = sizeof(buf); + if (EVP_DecryptFinal_ex(c, buf, &len) != 1) { + wpa_printf(MSG_ERROR, "OpenSSL: EVP_DecryptFinal_ex failed: " + "%s", ERR_error_string(ERR_get_error(), NULL)); + } + if (len != 0) { + wpa_printf(MSG_ERROR, "OpenSSL: Unexpected padding length %d " + "in AES decrypt", len); + } + EVP_CIPHER_CTX_cleanup(c); os_free(ctx); } @@ -571,6 +623,7 @@ struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, ctx = os_zalloc(sizeof(*ctx)); if (ctx == NULL) return NULL; + HMAC_CTX_init(&ctx->ctx); #if OPENSSL_VERSION_NUMBER < 0x00909000 HMAC_Init_ex(&ctx->ctx, key, key_len, md, NULL); @@ -623,3 +676,145 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) return -1; } + + +int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len, + int iterations, u8 *buf, size_t buflen) +{ +#if OPENSSL_VERSION_NUMBER < 0x00908000 + if (PKCS5_PBKDF2_HMAC_SHA1(passphrase, os_strlen(passphrase), + (unsigned char *) ssid, + ssid_len, 4096, buflen, buf) != 1) + return -1; +#else /* openssl < 0.9.8 */ + if (PKCS5_PBKDF2_HMAC_SHA1(passphrase, os_strlen(passphrase), ssid, + ssid_len, 4096, buflen, buf) != 1) + return -1; +#endif /* openssl < 0.9.8 */ + return 0; +} + + +int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + HMAC_CTX ctx; + size_t i; + unsigned int mdlen; + int res; + + HMAC_CTX_init(&ctx); +#if OPENSSL_VERSION_NUMBER < 0x00909000 + HMAC_Init_ex(&ctx, key, key_len, EVP_sha1(), NULL); +#else /* openssl < 0.9.9 */ + if (HMAC_Init_ex(&ctx, key, key_len, EVP_sha1(), NULL) != 1) + return -1; +#endif /* openssl < 0.9.9 */ + + for (i = 0; i < num_elem; i++) + HMAC_Update(&ctx, addr[i], len[i]); + + mdlen = 20; +#if OPENSSL_VERSION_NUMBER < 0x00909000 + HMAC_Final(&ctx, mac, &mdlen); + res = 1; +#else /* openssl < 0.9.9 */ + res = HMAC_Final(&ctx, mac, &mdlen); +#endif /* openssl < 0.9.9 */ + HMAC_CTX_cleanup(&ctx); + + return res == 1 ? 0 : -1; +} + + +int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac); +} + + +#ifdef CONFIG_SHA256 + +int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + HMAC_CTX ctx; + size_t i; + unsigned int mdlen; + int res; + + HMAC_CTX_init(&ctx); +#if OPENSSL_VERSION_NUMBER < 0x00909000 + HMAC_Init_ex(&ctx, key, key_len, EVP_sha256(), NULL); +#else /* openssl < 0.9.9 */ + if (HMAC_Init_ex(&ctx, key, key_len, EVP_sha256(), NULL) != 1) + return -1; +#endif /* openssl < 0.9.9 */ + + for (i = 0; i < num_elem; i++) + HMAC_Update(&ctx, addr[i], len[i]); + + mdlen = 32; +#if OPENSSL_VERSION_NUMBER < 0x00909000 + HMAC_Final(&ctx, mac, &mdlen); + res = 1; +#else /* openssl < 0.9.9 */ + res = HMAC_Final(&ctx, mac, &mdlen); +#endif /* openssl < 0.9.9 */ + HMAC_CTX_cleanup(&ctx); + + return res == 1 ? 0 : -1; +} + + +int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_SHA256 */ + + +int crypto_get_random(void *buf, size_t len) +{ + if (RAND_bytes(buf, len) != 1) + return -1; + return 0; +} + + +#ifdef CONFIG_OPENSSL_CMAC +int omac1_aes_128_vector(const u8 *key, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + CMAC_CTX *ctx; + int ret = -1; + size_t outlen, i; + + ctx = CMAC_CTX_new(); + if (ctx == NULL) + return -1; + + if (!CMAC_Init(ctx, key, 16, EVP_aes_128_cbc(), NULL)) + goto fail; + for (i = 0; i < num_elem; i++) { + if (!CMAC_Update(ctx, addr[i], len[i])) + goto fail; + } + if (!CMAC_Final(ctx, mac, &outlen) || outlen != 16) + goto fail; + + ret = 0; +fail: + CMAC_CTX_free(ctx); + return ret; +} + + +int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac) +{ + return omac1_aes_128_vector(key, 1, &data, &data_len, mac); +} +#endif /* CONFIG_OPENSSL_CMAC */ diff --git a/src/crypto/fips_prf_openssl.c b/src/crypto/fips_prf_openssl.c index 92f0b572..d69eceab 100644 --- a/src/crypto/fips_prf_openssl.c +++ b/src/crypto/fips_prf_openssl.c @@ -31,13 +31,14 @@ int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen) u8 *xpos = x; u32 carry; - if (seed_len > sizeof(xkey)) + if (seed_len < sizeof(xkey)) + os_memset(xkey + seed_len, 0, sizeof(xkey) - seed_len); + else seed_len = sizeof(xkey); /* FIPS 186-2 + change notice 1 */ os_memcpy(xkey, seed, seed_len); - os_memset(xkey + seed_len, 0, 64 - seed_len); t[0] = 0x67452301; t[1] = 0xEFCDAB89; t[2] = 0x98BADCFE; diff --git a/src/crypto/md5-non-fips.c b/src/crypto/md5-non-fips.c deleted file mode 100644 index 418f7828..00000000 --- a/src/crypto/md5-non-fips.c +++ /dev/null @@ -1,107 +0,0 @@ -/* - * MD5 hash implementation and interface functions (non-FIPS allowed cases) - * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi> - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -#include "includes.h" - -#include "common.h" -#include "md5.h" -#include "crypto.h" - - -/** - * hmac_md5_vector_non_fips_allow - HMAC-MD5 over data vector (RFC 2104) - * @key: Key for HMAC operations - * @key_len: Length of the key in bytes - * @num_elem: Number of elements in the data vector - * @addr: Pointers to the data areas - * @len: Lengths of the data blocks - * @mac: Buffer for the hash (16 bytes) - * Returns: 0 on success, -1 on failure - */ -int hmac_md5_vector_non_fips_allow(const u8 *key, size_t key_len, - size_t num_elem, const u8 *addr[], - const size_t *len, u8 *mac) -{ - u8 k_pad[64]; /* padding - key XORd with ipad/opad */ - u8 tk[16]; - const u8 *_addr[6]; - size_t i, _len[6]; - - if (num_elem > 5) { - /* - * Fixed limit on the number of fragments to avoid having to - * allocate memory (which could fail). - */ - return -1; - } - - /* if key is longer than 64 bytes reset it to key = MD5(key) */ - if (key_len > 64) { - if (md5_vector_non_fips_allow(1, &key, &key_len, tk)) - return -1; - key = tk; - key_len = 16; - } - - /* the HMAC_MD5 transform looks like: - * - * MD5(K XOR opad, MD5(K XOR ipad, text)) - * - * where K is an n byte key - * ipad is the byte 0x36 repeated 64 times - * opad is the byte 0x5c repeated 64 times - * and text is the data being protected */ - - /* start out by storing key in ipad */ - os_memset(k_pad, 0, sizeof(k_pad)); - os_memcpy(k_pad, key, key_len); - - /* XOR key with ipad values */ - for (i = 0; i < 64; i++) - k_pad[i] ^= 0x36; - - /* perform inner MD5 */ - _addr[0] = k_pad; - _len[0] = 64; - for (i = 0; i < num_elem; i++) { - _addr[i + 1] = addr[i]; - _len[i + 1] = len[i]; - } - if (md5_vector_non_fips_allow(1 + num_elem, _addr, _len, mac)) - return -1; - - os_memset(k_pad, 0, sizeof(k_pad)); - os_memcpy(k_pad, key, key_len); - /* XOR key with opad values */ - for (i = 0; i < 64; i++) - k_pad[i] ^= 0x5c; - - /* perform outer MD5 */ - _addr[0] = k_pad; - _len[0] = 64; - _addr[1] = mac; - _len[1] = MD5_MAC_LEN; - return md5_vector_non_fips_allow(2, _addr, _len, mac); -} - - -/** - * hmac_md5_non_fips_allow - HMAC-MD5 over data buffer (RFC 2104) - * @key: Key for HMAC operations - * @key_len: Length of the key in bytes - * @data: Pointers to the data area - * @data_len: Length of the data area - * @mac: Buffer for the hash (16 bytes) - * Returns: 0 on success, -1 on failure - */ -int hmac_md5_non_fips_allow(const u8 *key, size_t key_len, const u8 *data, - size_t data_len, u8 *mac) -{ - return hmac_md5_vector_non_fips_allow(key, key_len, 1, &data, - &data_len, mac); -} diff --git a/src/crypto/md5.h b/src/crypto/md5.h index 4d6e9045..33f8426c 100644 --- a/src/crypto/md5.h +++ b/src/crypto/md5.h @@ -15,15 +15,5 @@ int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *mac); -#ifdef CONFIG_FIPS -int hmac_md5_vector_non_fips_allow(const u8 *key, size_t key_len, - size_t num_elem, const u8 *addr[], - const size_t *len, u8 *mac); -int hmac_md5_non_fips_allow(const u8 *key, size_t key_len, const u8 *data, - size_t data_len, u8 *mac); -#else /* CONFIG_FIPS */ -#define hmac_md5_vector_non_fips_allow hmac_md5_vector -#define hmac_md5_non_fips_allow hmac_md5 -#endif /* CONFIG_FIPS */ #endif /* MD5_H */ diff --git a/src/crypto/ms_funcs.c b/src/crypto/ms_funcs.c index 3594d846..b2bbab2b 100644 --- a/src/crypto/ms_funcs.c +++ b/src/crypto/ms_funcs.c @@ -1,6 +1,6 @@ /* * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759 - * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -173,8 +173,9 @@ int generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge, u8 challenge[8]; u8 password_hash[16]; - challenge_hash(peer_challenge, auth_challenge, username, username_len, - challenge); + if (challenge_hash(peer_challenge, auth_challenge, username, + username_len, challenge)) + return -1; if (nt_password_hash(password, password_len, password_hash)) return -1; challenge_response(challenge, password_hash, response); @@ -260,8 +261,9 @@ int generate_authenticator_response_pwhash( if (sha1_vector(3, addr1, len1, response)) return -1; - challenge_hash(peer_challenge, auth_challenge, username, username_len, - challenge); + if (challenge_hash(peer_challenge, auth_challenge, username, + username_len, challenge)) + return -1; return sha1_vector(3, addr2, len2, response); } diff --git a/src/crypto/random.c b/src/crypto/random.c index d85c3e66..053740e9 100644 --- a/src/crypto/random.c +++ b/src/crypto/random.c @@ -29,6 +29,7 @@ #include "utils/common.h" #include "utils/eloop.h" +#include "crypto/crypto.h" #include "sha1.h" #include "random.h" @@ -177,6 +178,27 @@ int random_get_bytes(void *buf, size_t len) *bytes++ ^= tmp[i]; left -= siz; } + +#ifdef CONFIG_FIPS + /* Mix in additional entropy from the crypto module */ + left = len; + while (left) { + size_t siz, i; + u8 tmp[EXTRACT_LEN]; + if (crypto_get_random(tmp, sizeof(tmp)) < 0) { + wpa_printf(MSG_ERROR, "random: No entropy available " + "for generating strong random bytes"); + return -1; + } + wpa_hexdump_key(MSG_EXCESSIVE, "random from crypto module", + tmp, sizeof(tmp)); + siz = left > EXTRACT_LEN ? EXTRACT_LEN : left; + for (i = 0; i < siz; i++) + *bytes++ ^= tmp[i]; + left -= siz; + } +#endif /* CONFIG_FIPS */ + wpa_hexdump_key(MSG_EXCESSIVE, "mixed random", buf, len); if (entropy < len) diff --git a/src/crypto/sha1-pbkdf2.c b/src/crypto/sha1-pbkdf2.c index 450c2a03..8effe2fe 100644 --- a/src/crypto/sha1-pbkdf2.c +++ b/src/crypto/sha1-pbkdf2.c @@ -11,7 +11,7 @@ #include "common.h" #include "sha1.h" -static int pbkdf2_sha1_f(const char *passphrase, const char *ssid, +static int pbkdf2_sha1_f(const char *passphrase, const u8 *ssid, size_t ssid_len, int iterations, unsigned int count, u8 *digest) { @@ -22,7 +22,7 @@ static int pbkdf2_sha1_f(const char *passphrase, const char *ssid, size_t len[2]; size_t passphrase_len = os_strlen(passphrase); - addr[0] = (u8 *) ssid; + addr[0] = ssid; len[0] = ssid_len; addr[1] = count_buf; len[1] = 4; @@ -69,7 +69,7 @@ static int pbkdf2_sha1_f(const char *passphrase, const char *ssid, * iterations is set to 4096 and buflen to 32. This function is described in * IEEE Std 802.11-2004, Clause H.4. The main construction is from PKCS#5 v2.0. */ -int pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len, +int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len, int iterations, u8 *buf, size_t buflen) { unsigned int count = 0; diff --git a/src/crypto/sha1-prf.c b/src/crypto/sha1-prf.c new file mode 100644 index 00000000..90b9e74b --- /dev/null +++ b/src/crypto/sha1-prf.c @@ -0,0 +1,66 @@ +/* + * SHA1-based PRF + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha1.h" +#include "crypto.h" + + +/** + * sha1_prf - SHA1-based Pseudo-Random Function (PRF) (IEEE 802.11i, 8.5.1.1) + * @key: Key for PRF + * @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 + * Returns: 0 on success, -1 of failure + * + * This function is used to derive new, cryptographically separate keys from a + * given key (e.g., PMK in IEEE 802.11i). + */ +int sha1_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len) +{ + u8 counter = 0; + size_t pos, plen; + u8 hash[SHA1_MAC_LEN]; + size_t label_len = os_strlen(label) + 1; + const unsigned char *addr[3]; + size_t len[3]; + + addr[0] = (u8 *) label; + len[0] = label_len; + addr[1] = data; + len[1] = data_len; + addr[2] = &counter; + len[2] = 1; + + pos = 0; + while (pos < buf_len) { + plen = buf_len - pos; + if (plen >= SHA1_MAC_LEN) { + if (hmac_sha1_vector(key, key_len, 3, addr, len, + &buf[pos])) + return -1; + pos += SHA1_MAC_LEN; + } else { + if (hmac_sha1_vector(key, key_len, 3, addr, len, + hash)) + return -1; + os_memcpy(&buf[pos], hash, plen); + break; + } + counter++; + } + + return 0; +} diff --git a/src/crypto/sha1-tlsprf.c b/src/crypto/sha1-tlsprf.c index e79265ba..0effd9b7 100644 --- a/src/crypto/sha1-tlsprf.c +++ b/src/crypto/sha1-tlsprf.c @@ -71,19 +71,16 @@ int tls_prf_sha1_md5(const u8 *secret, size_t secret_len, const char *label, S2--; } - hmac_md5_vector_non_fips_allow(S1, L_S1, 2, &MD5_addr[1], &MD5_len[1], - A_MD5); + hmac_md5_vector(S1, L_S1, 2, &MD5_addr[1], &MD5_len[1], A_MD5); hmac_sha1_vector(S2, L_S2, 2, &SHA1_addr[1], &SHA1_len[1], A_SHA1); MD5_pos = MD5_MAC_LEN; SHA1_pos = SHA1_MAC_LEN; for (i = 0; i < outlen; i++) { if (MD5_pos == MD5_MAC_LEN) { - hmac_md5_vector_non_fips_allow(S1, L_S1, 3, MD5_addr, - MD5_len, P_MD5); + hmac_md5_vector(S1, L_S1, 3, MD5_addr, MD5_len, P_MD5); MD5_pos = 0; - hmac_md5_non_fips_allow(S1, L_S1, A_MD5, MD5_MAC_LEN, - A_MD5); + hmac_md5(S1, L_S1, A_MD5, MD5_MAC_LEN, A_MD5); } if (SHA1_pos == SHA1_MAC_LEN) { hmac_sha1_vector(S2, L_S2, 3, SHA1_addr, SHA1_len, diff --git a/src/crypto/sha1.c b/src/crypto/sha1.c index 274d81fa..d48c77d7 100644 --- a/src/crypto/sha1.c +++ b/src/crypto/sha1.c @@ -102,56 +102,3 @@ int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, { return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac); } - - -/** - * sha1_prf - SHA1-based Pseudo-Random Function (PRF) (IEEE 802.11i, 8.5.1.1) - * @key: Key for PRF - * @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 - * Returns: 0 on success, -1 of failure - * - * This function is used to derive new, cryptographically separate keys from a - * given key (e.g., PMK in IEEE 802.11i). - */ -int sha1_prf(const u8 *key, size_t key_len, const char *label, - const u8 *data, size_t data_len, u8 *buf, size_t buf_len) -{ - u8 counter = 0; - size_t pos, plen; - u8 hash[SHA1_MAC_LEN]; - size_t label_len = os_strlen(label) + 1; - const unsigned char *addr[3]; - size_t len[3]; - - addr[0] = (u8 *) label; - len[0] = label_len; - addr[1] = data; - len[1] = data_len; - addr[2] = &counter; - len[2] = 1; - - pos = 0; - while (pos < buf_len) { - plen = buf_len - pos; - if (plen >= SHA1_MAC_LEN) { - if (hmac_sha1_vector(key, key_len, 3, addr, len, - &buf[pos])) - return -1; - pos += SHA1_MAC_LEN; - } else { - if (hmac_sha1_vector(key, key_len, 3, addr, len, - hash)) - return -1; - os_memcpy(&buf[pos], hash, plen); - break; - } - counter++; - } - - return 0; -} diff --git a/src/crypto/sha1.h b/src/crypto/sha1.h index 08e5a59d..933cd81b 100644 --- a/src/crypto/sha1.h +++ b/src/crypto/sha1.h @@ -22,6 +22,6 @@ int sha1_t_prf(const u8 *key, size_t key_len, const char *label, int __must_check tls_prf_sha1_md5(const u8 *secret, size_t secret_len, const char *label, const u8 *seed, size_t seed_len, u8 *out, size_t outlen); -int pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len, +int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len, int iterations, u8 *buf, size_t buflen); #endif /* SHA1_H */ diff --git a/src/crypto/sha256-prf.c b/src/crypto/sha256-prf.c new file mode 100644 index 00000000..0da6d130 --- /dev/null +++ b/src/crypto/sha256-prf.c @@ -0,0 +1,64 @@ +/* + * SHA256-based PRF (IEEE 802.11r) + * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha256.h" +#include "crypto.h" + + +/** + * sha256_prf - SHA256-based Pseudo-Random Function (IEEE 802.11r, 8.5.1.5.2) + * @key: Key for PRF + * @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 sha256_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len) +{ + u16 counter = 1; + size_t pos, plen; + u8 hash[SHA256_MAC_LEN]; + const u8 *addr[4]; + size_t len[4]; + u8 counter_le[2], length_le[2]; + + 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 * 8); + pos = 0; + while (pos < buf_len) { + plen = buf_len - pos; + WPA_PUT_LE16(counter_le, counter); + if (plen >= SHA256_MAC_LEN) { + hmac_sha256_vector(key, key_len, 4, addr, len, + &buf[pos]); + pos += SHA256_MAC_LEN; + } else { + hmac_sha256_vector(key, key_len, 4, addr, len, hash); + os_memcpy(&buf[pos], hash, plen); + break; + } + counter++; + } +} diff --git a/src/crypto/sha256.c b/src/crypto/sha256.c index 5c09ed19..b55e976f 100644 --- a/src/crypto/sha256.c +++ b/src/crypto/sha256.c @@ -1,6 +1,6 @@ /* * SHA-256 hash implementation and interface functions - * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -21,9 +21,10 @@ * @addr: Pointers to the data areas * @len: Lengths of the data blocks * @mac: Buffer for the hash (32 bytes) + * Returns: 0 on success, -1 on failure */ -void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, - const u8 *addr[], const size_t *len, u8 *mac) +int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) { unsigned char k_pad[64]; /* padding - key XORd with ipad/opad */ unsigned char tk[32]; @@ -35,12 +36,13 @@ void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, * Fixed limit on the number of fragments to avoid having to * allocate memory (which could fail). */ - return; + return -1; } /* if key is longer than 64 bytes reset it to key = SHA256(key) */ if (key_len > 64) { - sha256_vector(1, &key, &key_len, tk); + if (sha256_vector(1, &key, &key_len, tk) < 0) + return -1; key = tk; key_len = 32; } @@ -68,7 +70,8 @@ void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, _addr[i + 1] = addr[i]; _len[i + 1] = len[i]; } - sha256_vector(1 + num_elem, _addr, _len, mac); + if (sha256_vector(1 + num_elem, _addr, _len, mac) < 0) + return -1; os_memset(k_pad, 0, sizeof(k_pad)); os_memcpy(k_pad, key, key_len); @@ -81,7 +84,7 @@ void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, _len[0] = 64; _addr[1] = mac; _len[1] = SHA256_MAC_LEN; - sha256_vector(2, _addr, _len, mac); + return sha256_vector(2, _addr, _len, mac); } @@ -91,61 +94,11 @@ void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, * @key_len: Length of the key in bytes * @data: Pointers to the data area * @data_len: Length of the data area - * @mac: Buffer for the hash (20 bytes) - */ -void hmac_sha256(const u8 *key, size_t key_len, const u8 *data, - size_t data_len, u8 *mac) -{ - hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac); -} - - -/** - * sha256_prf - SHA256-based Pseudo-Random Function (IEEE 802.11r, 8.5.1.5.2) - * @key: Key for PRF - * @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. + * @mac: Buffer for the hash (32 bytes) + * Returns: 0 on success, -1 on failure */ -void sha256_prf(const u8 *key, size_t key_len, const char *label, - const u8 *data, size_t data_len, u8 *buf, size_t buf_len) +int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) { - u16 counter = 1; - size_t pos, plen; - u8 hash[SHA256_MAC_LEN]; - const u8 *addr[4]; - size_t len[4]; - u8 counter_le[2], length_le[2]; - - 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 * 8); - pos = 0; - while (pos < buf_len) { - plen = buf_len - pos; - WPA_PUT_LE16(counter_le, counter); - if (plen >= SHA256_MAC_LEN) { - hmac_sha256_vector(key, key_len, 4, addr, len, - &buf[pos]); - pos += SHA256_MAC_LEN; - } else { - hmac_sha256_vector(key, key_len, 4, addr, len, hash); - os_memcpy(&buf[pos], hash, plen); - break; - } - counter++; - } + return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac); } diff --git a/src/crypto/sha256.h b/src/crypto/sha256.h index 412fb603..fcac8004 100644 --- a/src/crypto/sha256.h +++ b/src/crypto/sha256.h @@ -11,10 +11,10 @@ #define SHA256_MAC_LEN 32 -void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, - const u8 *addr[], const size_t *len, u8 *mac); -void hmac_sha256(const u8 *key, size_t key_len, const u8 *data, - size_t data_len, u8 *mac); +int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac); +int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac); void sha256_prf(const u8 *key, size_t key_len, const char *label, const u8 *data, size_t data_len, u8 *buf, size_t buf_len); void tls_prf_sha256(const u8 *secret, size_t secret_len, diff --git a/src/crypto/tls.h b/src/crypto/tls.h index 990f6e6e..b61e4393 100644 --- a/src/crypto/tls.h +++ b/src/crypto/tls.h @@ -81,6 +81,7 @@ struct tls_config { #define TLS_CONN_ALLOW_SIGN_RSA_MD5 BIT(0) #define TLS_CONN_DISABLE_TIME_CHECKS BIT(1) +#define TLS_CONN_DISABLE_SESSION_TICKET BIT(2) /** * struct tls_connection_params - Parameters for TLS connection diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c index f2b541e0..dfb63ad7 100644 --- a/src/crypto/tls_openssl.c +++ b/src/crypto/tls_openssl.c @@ -711,6 +711,8 @@ void * tls_init(const struct tls_config *conf) "mode"); ERR_load_crypto_strings(); ERR_print_errors_fp(stderr); + os_free(tls_global); + tls_global = NULL; return NULL; } else wpa_printf(MSG_INFO, "Running in FIPS mode"); @@ -719,6 +721,8 @@ void * tls_init(const struct tls_config *conf) if (conf && conf->fips_mode) { wpa_printf(MSG_ERROR, "FIPS mode requested, but not " "supported"); + os_free(tls_global); + tls_global = NULL; return NULL; } #endif /* OPENSSL_FIPS */ @@ -1936,6 +1940,8 @@ static int tls_connection_engine_ca_cert(void *_ssl_ctx, wpa_printf(MSG_DEBUG, "OpenSSL: %s - added CA certificate from engine " "to certificate store", __func__); SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); + conn->ca_cert_verify = 1; + return 0; #else /* OPENSSL_NO_ENGINE */ @@ -2099,7 +2105,7 @@ static int tls_connection_private_key(void *_ssl_ctx, ERR_clear_error(); SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL); os_free(passwd); - + if (!SSL_check_private_key(conn->ssl)) { tls_show_errors(MSG_INFO, __func__, "Private key failed " "verification"); @@ -2145,7 +2151,7 @@ static int tls_global_private_key(SSL_CTX *ssl_ctx, const char *private_key, os_free(passwd); ERR_clear_error(); SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL); - + if (!SSL_CTX_check_private_key(ssl_ctx)) { tls_show_errors(MSG_INFO, __func__, "Private key failed verification"); @@ -2307,6 +2313,11 @@ static int tls_global_dh(SSL_CTX *ssl_ctx, const char *dh_file) int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, struct tls_keys *keys) { +#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) @@ -2324,6 +2335,7 @@ int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, keys->server_random_len = SSL3_RANDOM_SIZE; return 0; +#endif /* CONFIG_FIPS */ } @@ -2331,6 +2343,19 @@ int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, const char *label, int server_random_first, u8 *out, size_t out_len) { +#if OPENSSL_VERSION_NUMBER >= 0x10001000L + SSL *ssl; + if (conn == NULL) + return -1; + if (server_random_first) + return -1; + ssl = conn->ssl; + if (SSL_export_keying_material(ssl, out, out_len, label, + os_strlen(label), NULL, 0, 0) == 1) { + wpa_printf(MSG_DEBUG, "OpenSSL: Using internal PRF"); + return 0; + } +#endif return -1; } @@ -2763,6 +2788,13 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; } +#ifdef SSL_OP_NO_TICKET + if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET) + SSL_set_options(conn->ssl, SSL_OP_NO_TICKET); + else + SSL_clear_options(conn->ssl, SSL_OP_NO_TICKET); +#endif /* SSL_OP_NO_TICKET */ + conn->flags = params->flags; tls_get_errors(tls_ctx); @@ -2798,6 +2830,13 @@ int tls_global_set_params(void *tls_ctx, return -1; } +#ifdef SSL_OP_NO_TICKET + if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET) + SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TICKET); + else + SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TICKET); +#endif /* SSL_OP_NO_TICKET */ + return 0; } @@ -2807,6 +2846,7 @@ int tls_connection_get_keyblock_size(void *tls_ctx, { const EVP_CIPHER *c; const EVP_MD *h; + int md_size; if (conn == NULL || conn->ssl == NULL || conn->ssl->enc_read_ctx == NULL || @@ -2820,9 +2860,20 @@ int tls_connection_get_keyblock_size(void *tls_ctx, #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) + - EVP_MD_size(h) + + md_size + EVP_CIPHER_iv_length(c)); } diff --git a/src/drivers/android_drv.h b/src/drivers/android_drv.h index 6df71602..5906527a 100644 --- a/src/drivers/android_drv.h +++ b/src/drivers/android_drv.h @@ -14,8 +14,6 @@ #define WPA_EVENT_DRIVER_STATE "CTRL-EVENT-DRIVER-STATE " -#define WEXT_CSCAN_AMOUNT 9 - #define MAX_SSID_LEN 32 #define MAX_DRV_CMD_SIZE 248 diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 0aab61ec..e3920722 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -274,6 +274,15 @@ struct wpa_driver_scan_params { size_t num_filter_ssids; /** + * filter_rssi - Filter by RSSI + * + * The driver may filter scan results in firmware to reduce host + * wakeups and thereby save power. Specify the RSSI threshold in s32 + * dBm. + */ + s32 filter_rssi; + + /** * p2p_probe - Used to disable CCK (802.11b) rates for P2P probes * * When set, the driver is expected to remove rates 1, 2, 5.5, and 11 @@ -731,6 +740,11 @@ struct wpa_driver_ap_params { * This is used by driver which advertises this capability. */ int ap_max_inactivity; + + /** + * disable_dgaf - Whether group-addressed frames are disabled + */ + int disable_dgaf; }; /** @@ -751,6 +765,7 @@ struct wpa_driver_capa { #define WPA_DRIVER_CAPA_ENC_TKIP 0x00000004 #define WPA_DRIVER_CAPA_ENC_CCMP 0x00000008 #define WPA_DRIVER_CAPA_ENC_WEP128 0x00000010 +#define WPA_DRIVER_CAPA_ENC_GCMP 0x00000020 unsigned int enc; #define WPA_DRIVER_AUTH_OPEN 0x00000001 @@ -817,6 +832,8 @@ struct wpa_driver_capa { #define WPA_DRIVER_FLAGS_AP_UAPSD 0x00400000 /* Driver supports inactivity timer in AP mode */ #define WPA_DRIVER_FLAGS_INACTIVITY_TIMER 0x00800000 +/* Driver expects user space implementation of MLME in AP mode */ +#define WPA_DRIVER_FLAGS_AP_MLME 0x01000000 unsigned int flags; int max_scan_ssids; @@ -983,6 +1000,23 @@ enum tdls_oper { TDLS_DISABLE }; +enum wnm_oper { + WNM_SLEEP_ENTER_CONFIRM, + WNM_SLEEP_ENTER_FAIL, + WNM_SLEEP_EXIT_CONFIRM, + WNM_SLEEP_EXIT_FAIL, + WNM_SLEEP_TFS_REQ_IE_ADD, /* STA requests driver to add TFS req IE */ + WNM_SLEEP_TFS_REQ_IE_NONE, /* STA requests empty TFS req IE */ + WNM_SLEEP_TFS_REQ_IE_SET, /* AP requests driver to set TFS req IE for + * a STA */ + WNM_SLEEP_TFS_RESP_IE_ADD, /* AP requests driver to add TFS resp IE + * for a STA */ + WNM_SLEEP_TFS_RESP_IE_NONE, /* AP requests empty TFS resp IE */ + WNM_SLEEP_TFS_RESP_IE_SET, /* AP requests driver to set TFS resp IE + * for a STA */ + WNM_SLEEP_TFS_IE_DEL /* AP delete the TFS IE */ +}; + /** * struct wpa_signal_info - Information about channel signal quality */ @@ -1042,7 +1076,8 @@ struct wpa_driver_ops { * @ifname: Interface name (for multi-SSID/VLAN support) * @priv: private driver interface data * @alg: encryption algorithm (%WPA_ALG_NONE, %WPA_ALG_WEP, - * %WPA_ALG_TKIP, %WPA_ALG_CCMP, %WPA_ALG_IGTK, %WPA_ALG_PMK); + * %WPA_ALG_TKIP, %WPA_ALG_CCMP, %WPA_ALG_IGTK, %WPA_ALG_PMK, + * %WPA_ALG_GCMP); * %WPA_ALG_NONE clears the key. * @addr: Address of the peer STA (BSSID of the current AP when setting * pairwise key in station mode), ff:ff:ff:ff:ff:ff for @@ -1059,11 +1094,11 @@ struct wpa_driver_ops { * for Rx keys (in most cases, this is only used with broadcast * keys and set to zero for unicast keys); %NULL if not set * @seq_len: length of the seq, depends on the algorithm: - * TKIP: 6 octets, CCMP: 6 octets, IGTK: 6 octets + * TKIP: 6 octets, CCMP/GCMP: 6 octets, IGTK: 6 octets * @key: key buffer; TKIP: 16-byte temporal key, 8-byte Tx Mic key, * 8-byte Rx Mic Key * @key_len: length of the key buffer in octets (WEP: 5 or 13, - * TKIP: 32, CCMP: 16, IGTK: 16) + * TKIP: 32, CCMP/GCMP: 16, IGTK: 16) * * Returns: 0 on success, -1 on failure * @@ -1568,9 +1603,9 @@ struct wpa_driver_ops { * Returns: 0 on success, -1 on failure * * This function is used to fetch the last used TSC/packet number for - * a TKIP, CCMP, or BIP/IGTK key. It is mainly used with group keys, so - * there is no strict requirement on implementing support for unicast - * keys (i.e., addr != %NULL). + * a TKIP, CCMP, GCMP, or BIP/IGTK key. It is mainly used with group + * keys, so there is no strict requirement on implementing support for + * unicast keys (i.e., addr != %NULL). */ int (*get_seqnum)(const char *ifname, void *priv, const u8 *addr, int idx, u8 *seq); @@ -2399,6 +2434,18 @@ struct wpa_driver_ops { int (*tdls_oper)(void *priv, enum tdls_oper oper, const u8 *peer); /** + * wnm_oper - Notify driver of the WNM frame reception + * @priv: Private driver interface data + * @oper: WNM operation. See %enum wnm_oper + * @peer: Destination (peer) MAC address + * @buf: Buffer for the driver to fill in (for getting IE) + * @buf_len: Return the len of buf + * Returns: 0 on success, negative (<0) on failure + */ + int (*wnm_oper)(void *priv, enum wnm_oper oper, const u8 *peer, + u8 *buf, u16 *buf_len); + + /** * signal_poll - Get current connection information * @priv: Private driver interface data * @signal_info: Connection info structure @@ -3006,7 +3053,14 @@ enum wpa_event_type { * * Described in wpa_event_data.ch_switch * */ - EVENT_CH_SWITCH + EVENT_CH_SWITCH, + + /** + * EVENT_WNM - Request WNM operation + * + * This event can be used to request a WNM operation to be performed. + */ + EVENT_WNM }; @@ -3209,6 +3263,24 @@ union wpa_event_data { } tdls; /** + * struct wnm - Data for EVENT_WNM + */ + struct wnm { + u8 addr[ETH_ALEN]; + enum { + WNM_OPER_SLEEP, + } oper; + enum { + WNM_SLEEP_ENTER, + WNM_SLEEP_EXIT + } sleep_action; + int sleep_intval; + u16 reason_code; + u8 *buf; + u16 buf_len; + } wnm; + + /** * struct ft_ies - FT information elements (EVENT_FT_RESPONSE) * * During FT (IEEE 802.11r) authentication sequence, the driver is diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c index 73898d3f..fefae8c3 100644 --- a/src/drivers/driver_atheros.c +++ b/src/drivers/driver_atheros.c @@ -14,6 +14,12 @@ #include <sys/ioctl.h> #include "common.h" +#include "eloop.h" +#include "common/ieee802_11_defs.h" +#include "l2_packet/l2_packet.h" +#include "p2p/p2p.h" + +#include "common.h" #ifndef _BYTE_ORDER #ifdef WORDS_BIGENDIAN #define _BYTE_ORDER _BIG_ENDIAN @@ -837,7 +843,88 @@ static void atheros_raw_recv_11r(void *ctx, const u8 *src_addr, const u8 *buf, } #endif /* CONFIG_IEEE80211R */ -#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) +#ifdef CONFIG_HS20 +static void atheros_raw_recv_hs20(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) +{ + struct atheros_driver_data *drv = ctx; + const struct ieee80211_mgmt *mgmt; + u16 fc; + union wpa_event_data event; + + /* Send the Action frame for HS20 processing */ + + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.action.category) + + sizeof(mgmt->u.action.u.public_action)) + return; + + mgmt = (const struct ieee80211_mgmt *) buf; + + fc = le_to_host16(mgmt->frame_control); + if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT || + WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_ACTION || + mgmt->u.action.category != WLAN_ACTION_PUBLIC) + return; + + wpa_printf(MSG_DEBUG, "%s:Received Public Action frame", __func__); + + os_memset(&event, 0, sizeof(event)); + event.rx_mgmt.frame = (const u8 *) mgmt; + event.rx_mgmt.frame_len = len; + wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event); +} +#endif /* CONFIG_HS20 */ + +#if defined(CONFIG_IEEE80211V) && !defined(CONFIG_IEEE80211R) +static void atheros_raw_recv_11v(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) +{ + struct atheros_driver_data *drv = ctx; + union wpa_event_data event; + const struct ieee80211_mgmt *mgmt; + u16 fc; + u16 stype; + + /* Do 11R processing for WNM ACTION frames */ + if (len < IEEE80211_HDRLEN) + return; + mgmt = (const struct ieee80211_mgmt *) buf; + + fc = le_to_host16(mgmt->frame_control); + + if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT) + return; + stype = WLAN_FC_GET_STYPE(fc); + + wpa_printf(MSG_DEBUG, "%s: subtype 0x%x len %d", __func__, stype, + (int) len); + + if (os_memcmp(drv->own_addr, mgmt->bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "%s: BSSID does not match - ignore", + __func__); + return; + } + + switch (stype) { + case WLAN_FC_STYPE_ACTION: + if (&mgmt->u.action.category > buf + len) + break; + os_memset(&event, 0, sizeof(event)); + event.rx_action.da = mgmt->da; + event.rx_action.sa = mgmt->sa; + event.rx_action.bssid = mgmt->bssid; + event.rx_action.category = mgmt->u.action.category; + event.rx_action.data = &mgmt->u.action.category; + event.rx_action.len = buf + len - event.rx_action.data; + wpa_supplicant_event(drv->hapd, EVENT_RX_ACTION, &event); + break; + default: + break; + } +} +#endif /* CONFIG_IEEE80211V */ + +#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211V) static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) { @@ -847,6 +934,12 @@ static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, #ifdef CONFIG_IEEE80211R atheros_raw_recv_11r(ctx, src_addr, buf, len); #endif /* CONFIG_IEEE80211R */ +#if defined(CONFIG_IEEE80211V) && !defined(CONFIG_IEEE80211R) + atheros_raw_recv_11v(ctx, src_addr, buf, len); +#endif /* CONFIG_IEEE80211V */ +#ifdef CONFIG_HS20 + atheros_raw_recv_hs20(ctx, src_addr, buf, len); +#endif /* CONFIG_HS20 */ } #endif /* CONFIG_WPS || CONFIG_IEEE80211R */ @@ -862,8 +955,15 @@ static int atheros_receive_pkt(struct atheros_driver_data *drv) #endif /* CONFIG_WPS */ #ifdef CONFIG_IEEE80211R filt.app_filterype |= (IEEE80211_FILTER_TYPE_ASSOC_REQ | - IEEE80211_FILTER_TYPE_AUTH); + IEEE80211_FILTER_TYPE_AUTH | + IEEE80211_FILTER_TYPE_ACTION); #endif +#ifdef CONFIG_IEEE80211V + filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION; +#endif /* CONFIG_IEEE80211V */ +#ifdef CONFIG_HS20 + filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION; +#endif /* CONFIG_HS20 */ if (filt.app_filterype) { ret = set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt, sizeof(struct ieee80211req_set_filter)); @@ -1165,8 +1265,8 @@ atheros_wireless_event_wireless_custom(struct atheros_driver_data *drv, */ wpa_supplicant_event(drv->hapd, EVENT_WPS_BUTTON_PUSHED, NULL); #endif /* CONFIG_WPS */ -#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) -#define WPS_FRAM_TAG_SIZE 30 /* hardcoded in driver */ +#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_HS20) +#define MGMT_FRAM_TAG_SIZE 30 /* hardcoded in driver */ } else if (strncmp(custom, "Manage.prob_req ", 16) == 0) { /* * Atheros driver uses a hack to pass Probe Request frames as a @@ -1175,50 +1275,136 @@ atheros_wireless_event_wireless_custom(struct atheros_driver_data *drv, * Format: "Manage.prob_req <frame len>" | zero padding | frame */ int len = atoi(custom + 16); - if (len < 0 || custom + WPS_FRAM_TAG_SIZE + len > end) { + if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) { wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req event " "length %d", len); return; } atheros_raw_receive(drv, NULL, - (u8 *) custom + WPS_FRAM_TAG_SIZE, len); + (u8 *) custom + MGMT_FRAM_TAG_SIZE, len); } else if (strncmp(custom, "Manage.assoc_req ", 17) == 0) { /* Format: "Manage.assoc_req <frame len>" | zero padding | * frame */ int len = atoi(custom + 17); - if (len < 0 || custom + WPS_FRAM_TAG_SIZE + len > end) { + if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) { wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req/" "assoc_req/auth event length %d", len); return; } atheros_raw_receive(drv, NULL, - (u8 *) custom + WPS_FRAM_TAG_SIZE, len); + (u8 *) custom + MGMT_FRAM_TAG_SIZE, len); } else if (strncmp(custom, "Manage.action ", 14) == 0) { /* Format: "Manage.assoc_req <frame len>" | zero padding | * frame */ int len = atoi(custom + 14); - if (len < 0 || custom + WPS_FRAM_TAG_SIZE + len > end) { + if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) { wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req/" "assoc_req/auth event length %d", len); return; } atheros_raw_receive(drv, NULL, - (u8 *) custom + WPS_FRAM_TAG_SIZE, len); + (u8 *) custom + MGMT_FRAM_TAG_SIZE, len); } else if (strncmp(custom, "Manage.auth ", 12) == 0) { /* Format: "Manage.auth <frame len>" | zero padding | frame */ int len = atoi(custom + 12); - if (len < 0 || custom + WPS_FRAM_TAG_SIZE + len > end) { + if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) { wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req/" "assoc_req/auth event length %d", len); return; } atheros_raw_receive(drv, NULL, - (u8 *) custom + WPS_FRAM_TAG_SIZE, len); + (u8 *) custom + MGMT_FRAM_TAG_SIZE, len); #endif /* CONFIG_WPS or CONFIG_IEEE80211R */ } } +/* +* Handle size of data problem. WEXT only allows data of 256 bytes for custom +* events, and p2p data can be much bigger. So the athr driver sends a small +* event telling me to collect the big data with an ioctl. +* On the first event, send all pending events to supplicant. +*/ +static void fetch_pending_big_events(struct atheros_driver_data *drv) +{ + union wpa_event_data event; + const struct ieee80211_mgmt *mgmt; + u8 tbuf[IW_PRIV_SIZE_MASK]; /* max size is 2047 bytes */ + u16 fc, stype; + struct iwreq iwr; + size_t data_len; + u32 freq, frame_type; + + while (1) { + os_memset(&iwr, 0, sizeof(iwr)); + os_strncpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + + iwr.u.data.pointer = (void *) tbuf; + iwr.u.data.length = sizeof(tbuf); + iwr.u.data.flags = IEEE80211_IOC_P2P_FETCH_FRAME; + + if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_P2P_BIG_PARAM, &iwr) + < 0) { + if (errno == ENOSPC) { + wpa_printf(MSG_DEBUG, "%s:%d exit", + __func__, __LINE__); + return; + } + wpa_printf(MSG_DEBUG, "athr: %s: P2P_BIG_PARAM[" + "P2P_FETCH_FRAME] failed: %s", + __func__, strerror(errno)); + return; + } + data_len = iwr.u.data.length; + wpa_hexdump(MSG_DEBUG, "athr: P2P_FETCH_FRAME data", + (u8 *) tbuf, data_len); + if (data_len < sizeof(freq) + sizeof(frame_type) + 24) { + wpa_printf(MSG_DEBUG, "athr: frame too short"); + continue; + } + os_memcpy(&freq, tbuf, sizeof(freq)); + os_memcpy(&frame_type, &tbuf[sizeof(freq)], + sizeof(frame_type)); + mgmt = (void *) &tbuf[sizeof(freq) + sizeof(frame_type)]; + data_len -= sizeof(freq) + sizeof(frame_type); + + if (frame_type == IEEE80211_EV_RX_MGMT) { + fc = le_to_host16(mgmt->frame_control); + stype = WLAN_FC_GET_STYPE(fc); + + wpa_printf(MSG_DEBUG, "athr: EV_RX_MGMT stype=%u " + "freq=%u len=%u", stype, freq, (int) data_len); + + if (stype == WLAN_FC_STYPE_ACTION) { + os_memset(&event, 0, sizeof(event)); + event.rx_mgmt.frame = (const u8 *) mgmt; + event.rx_mgmt.frame_len = data_len; + wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, + &event); + continue; + } + } else { + wpa_printf(MSG_DEBUG, "athr: %s unknown type %d", + __func__, frame_type); + continue; + } + } +} + +static void +atheros_wireless_event_atheros_custom(struct atheros_driver_data *drv, + int opcode, char *buf, int len) +{ + switch (opcode) { + case IEEE80211_EV_RX_MGMT: + wpa_printf(MSG_DEBUG, "WEXT: EV_RX_MGMT"); + fetch_pending_big_events(drv); + break; + default: + break; + } +} + static void atheros_wireless_event_wireless(struct atheros_driver_data *drv, char *data, int len) @@ -1274,8 +1460,15 @@ atheros_wireless_event_wireless(struct atheros_driver_data *drv, return; /* XXX */ memcpy(buf, custom, iwe->u.data.length); buf[iwe->u.data.length] = '\0'; - atheros_wireless_event_wireless_custom( - drv, buf, buf + iwe->u.data.length); + + if (iwe->u.data.flags != 0) { + atheros_wireless_event_atheros_custom( + drv, (int) iwe->u.data.flags, + buf, len); + } else { + atheros_wireless_event_wireless_custom( + drv, buf, buf + iwe->u.data.length); + } free(buf); break; } @@ -1709,6 +1902,221 @@ static int atheros_add_sta_node(void *priv, const u8 *addr, u16 auth_alg) #endif /* CONFIG_IEEE80211R */ +/* Use only to set a big param, get will not work. */ +static int +set80211big(struct atheros_driver_data *drv, int op, const void *data, int len) +{ + struct iwreq iwr; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + + iwr.u.data.pointer = (void *) data; + iwr.u.data.length = len; + iwr.u.data.flags = op; + wpa_printf(MSG_DEBUG, "%s: op=0x%x=%d (%s) len=0x%x", + __func__, op, op, athr_get_param_name(op), len); + + if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_P2P_BIG_PARAM, &iwr) < 0) { + wpa_printf(MSG_DEBUG, "%s: op=0x%x (%s) subop=0x%x=%d " + "value=0x%x,0x%x failed: %d (%s)", + __func__, op, athr_get_ioctl_name(op), iwr.u.mode, + iwr.u.mode, iwr.u.data.length, + iwr.u.data.flags, errno, strerror(errno)); + return -1; + } + return 0; +} + + +static int atheros_send_action(void *priv, unsigned int freq, + unsigned int wait, + const u8 *dst, const u8 *src, + const u8 *bssid, + const u8 *data, size_t data_len, int no_cck) +{ + struct atheros_driver_data *drv = priv; + struct ieee80211_p2p_send_action *act; + int res; + + act = os_zalloc(sizeof(*act) + data_len); + if (act == NULL) + return -1; + act->freq = freq; + os_memcpy(act->dst_addr, dst, ETH_ALEN); + os_memcpy(act->src_addr, src, ETH_ALEN); + os_memcpy(act->bssid, bssid, ETH_ALEN); + os_memcpy(act + 1, data, data_len); + wpa_printf(MSG_DEBUG, "%s: freq=%d, wait=%u, dst=" MACSTR ", src=" + MACSTR ", bssid=" MACSTR, + __func__, act->freq, wait, MAC2STR(act->dst_addr), + MAC2STR(act->src_addr), MAC2STR(act->bssid)); + wpa_hexdump(MSG_MSGDUMP, "athr: act", (u8 *) act, sizeof(*act)); + wpa_hexdump(MSG_MSGDUMP, "athr: data", data, data_len); + + res = set80211big(drv, IEEE80211_IOC_P2P_SEND_ACTION, + act, sizeof(*act) + data_len); + os_free(act); + return res; +} + + +#ifdef CONFIG_IEEE80211V +static int athr_wnm_tfs(struct atheros_driver_data *drv, const u8* peer, + u8 *ie, u16 *len, enum wnm_oper oper) +{ +#define IEEE80211_APPIE_MAX 1024 /* max appie buffer size */ + u8 buf[IEEE80211_APPIE_MAX]; + struct ieee80211req_getset_appiebuf *tfs_ie; + u16 val; + + wpa_printf(MSG_DEBUG, "atheros: ifname=%s, WNM TFS IE oper=%d " MACSTR, + drv->iface, oper, MAC2STR(peer)); + + switch (oper) { + case WNM_SLEEP_TFS_REQ_IE_SET: + if (*len > IEEE80211_APPIE_MAX - + sizeof(struct ieee80211req_getset_appiebuf)) { + wpa_printf(MSG_DEBUG, "TFS Req IE(s) too large"); + return -1; + } + tfs_ie = (struct ieee80211req_getset_appiebuf *) buf; + tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM; + tfs_ie->app_buflen = ETH_ALEN + 2 + 2 + *len; + + /* Command header for driver */ + os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN); + val = oper; + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2); + val = *len; + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2); + + /* copy the ie */ + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2 + 2, ie, *len); + + if (set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, tfs_ie, + IEEE80211_APPIE_MAX)) { + wpa_printf(MSG_DEBUG, "%s: Failed to set WNM TFS IE: " + "%s", __func__, strerror(errno)); + return -1; + } + break; + case WNM_SLEEP_TFS_RESP_IE_ADD: + tfs_ie = (struct ieee80211req_getset_appiebuf *) buf; + tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM; + tfs_ie->app_buflen = IEEE80211_APPIE_MAX - + sizeof(struct ieee80211req_getset_appiebuf); + /* Command header for driver */ + os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN); + val = oper; + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2); + val = 0; + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2); + + if (set80211priv(drv, IEEE80211_IOCTL_GET_APPIEBUF, tfs_ie, + IEEE80211_APPIE_MAX)) { + wpa_printf(MSG_DEBUG, "%s: Failed to get WNM TFS IE: " + "%s", __func__, strerror(errno)); + return -1; + } + + *len = tfs_ie->app_buflen; + os_memcpy(ie, &(tfs_ie->app_buf[0]), *len); + wpa_printf(MSG_DEBUG, "atheros: %c len=%d", tfs_ie->app_buf[0], + *len); + break; + case WNM_SLEEP_TFS_RESP_IE_NONE: + *len = 0; + break; + case WNM_SLEEP_TFS_IE_DEL: + tfs_ie = (struct ieee80211req_getset_appiebuf *) buf; + tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM; + tfs_ie->app_buflen = IEEE80211_APPIE_MAX - + sizeof(struct ieee80211req_getset_appiebuf); + /* Command header for driver */ + os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN); + val = oper; + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2); + val = 0; + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2); + + if (set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, tfs_ie, + IEEE80211_APPIE_MAX)) { + wpa_printf(MSG_DEBUG, "%s: Failed to set WNM TFS IE: " + "%s", __func__, strerror(errno)); + return -1; + } + break; + default: + wpa_printf(MSG_DEBUG, "Unsupported TFS oper %d", oper); + break; + } + + return 0; +} + + +static int atheros_wnm_sleep(struct atheros_driver_data *drv, + const u8 *peer, enum wnm_oper oper) +{ + u8 *data, *pos; + size_t dlen; + int ret; + u16 val; + + wpa_printf(MSG_DEBUG, "atheros: WNM-Sleep Oper %d, " MACSTR, + oper, MAC2STR(peer)); + + dlen = ETH_ALEN + 2 + 2; + data = os_malloc(dlen); + if (data == NULL) + return -1; + + /* Command header for driver */ + pos = data; + os_memcpy(pos, peer, ETH_ALEN); + pos += ETH_ALEN; + + val = oper; + os_memcpy(pos, &val, 2); + pos += 2; + + val = 0; + os_memcpy(pos, &val, 2); + + ret = atheros_set_wps_ie(drv, data, dlen, IEEE80211_APPIE_FRAME_WNM); + + os_free(data); + + return ret; +} + + +static int atheros_wnm_oper(void *priv, enum wnm_oper oper, const u8 *peer, + u8 *buf, u16 *buf_len) +{ + struct atheros_driver_data *drv = priv; + + switch (oper) { + case WNM_SLEEP_ENTER_CONFIRM: + case WNM_SLEEP_ENTER_FAIL: + case WNM_SLEEP_EXIT_CONFIRM: + case WNM_SLEEP_EXIT_FAIL: + return atheros_wnm_sleep(drv, peer, oper); + case WNM_SLEEP_TFS_REQ_IE_SET: + case WNM_SLEEP_TFS_RESP_IE_ADD: + case WNM_SLEEP_TFS_RESP_IE_NONE: + case WNM_SLEEP_TFS_IE_DEL: + return athr_wnm_tfs(drv, peer, buf, buf_len, oper); + default: + wpa_printf(MSG_DEBUG, "atheros: Unsupported WNM operation %d", + oper); + return -1; + } +} +#endif /* CONFIG_IEEE80211V */ + + const struct wpa_driver_ops wpa_driver_atheros_ops = { .name = "atheros", .hapd_init = atheros_init, @@ -1739,4 +2147,8 @@ const struct wpa_driver_ops wpa_driver_atheros_ops = { .add_tspec = atheros_add_tspec, .add_sta_node = atheros_add_sta_node, #endif /* CONFIG_IEEE80211R */ + .send_action = atheros_send_action, +#ifdef CONFIG_IEEE80211V + .wnm_oper = atheros_wnm_oper, +#endif /* CONFIG_IEEE80211V */ }; diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c index 67abb672..a2b34c12 100644 --- a/src/drivers/driver_bsd.c +++ b/src/drivers/driver_bsd.c @@ -1328,8 +1328,8 @@ wpa_driver_bsd_add_scan_entry(struct wpa_scan_results *res, result->ie_len = pos - (u8 *)(result + 1); - tmp = os_realloc(res->res, - (res->num + 1) * sizeof(struct wpa_scan_res *)); + tmp = os_realloc_array(res->res, res->num + 1, + sizeof(struct wpa_scan_res *)); if (tmp == NULL) { os_free(result); return; diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c index 81856aa9..418cf1a0 100644 --- a/src/drivers/driver_common.c +++ b/src/drivers/driver_common.c @@ -78,6 +78,7 @@ const char * event_to_string(enum wpa_event_type event) E2S(DRIVER_CLIENT_POLL_OK); E2S(EAPOL_TX_STATUS); E2S(CH_SWITCH); + E2S(WNM); } return "UNKNOWN"; diff --git a/src/drivers/driver_ndis.c b/src/drivers/driver_ndis.c index 185ed4f0..b9e096c4 100644 --- a/src/drivers/driver_ndis.c +++ b/src/drivers/driver_ndis.c @@ -858,7 +858,7 @@ static struct wpa_scan_results * wpa_driver_ndis_get_scan_results(void *priv) os_free(b); return NULL; } - results->res = os_zalloc(count * sizeof(struct wpa_scan_res *)); + results->res = os_calloc(count, sizeof(struct wpa_scan_res *)); if (results->res == NULL) { os_free(results); os_free(b); diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index b9503f18..eeec6098 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -2386,7 +2386,7 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct wiphy_info_data *info = arg; int p2p_go_supported = 0, p2p_client_supported = 0; - int p2p_concurrent = 0; + int p2p_concurrent = 0, p2p_multichan_concurrent = 0; int auth_supported = 0, connect_supported = 0; struct wpa_driver_capa *capa = info->capa; static struct nla_policy @@ -2487,6 +2487,8 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) if (combination_has_p2p && combination_has_mgd) { p2p_concurrent = 1; + if (nla_get_u32(tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]) > 1) + p2p_multichan_concurrent = 1; break; } @@ -2554,6 +2556,13 @@ broken_combination: "interface (driver advertised support)"); capa->flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT; capa->flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P; + + if (p2p_multichan_concurrent) { + wpa_printf(MSG_DEBUG, "nl80211: Enable multi-channel " + "concurrent (driver advertised support)"); + capa->flags |= + WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT; + } } if (tb[NL80211_ATTR_TDLS_SUPPORT]) { @@ -2646,9 +2655,16 @@ static int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) drv->capa.flags |= WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE; drv->capa.flags |= WPA_DRIVER_FLAGS_EAPOL_TX_STATUS; - if (!info.device_ap_sme) + if (!info.device_ap_sme) { drv->capa.flags |= WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS; + /* + * No AP SME is currently assumed to also indicate no AP MLME + * in the driver/firmware. + */ + drv->capa.flags |= WPA_DRIVER_FLAGS_AP_MLME; + } + drv->device_ap_sme = info.device_ap_sme; drv->poll_command_supported = info.poll_command_supported; drv->data_tx_status = info.data_tx_status; @@ -3485,73 +3501,110 @@ static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx) } -/** - * wpa_driver_nl80211_scan - Request the driver to initiate scan - * @priv: Pointer to private driver data from wpa_driver_nl80211_init() - * @params: Scan parameters - * Returns: 0 on success, -1 on failure - */ -static int wpa_driver_nl80211_scan(void *priv, - struct wpa_driver_scan_params *params) +static struct nl_msg * +nl80211_scan_common(struct wpa_driver_nl80211_data *drv, u8 cmd, + struct wpa_driver_scan_params *params) { - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - int ret = 0, timeout; - struct nl_msg *msg, *ssids, *freqs, *rates; + struct nl_msg *msg; + int err; size_t i; - drv->scan_for_auth = 0; - msg = nlmsg_alloc(); - ssids = nlmsg_alloc(); - freqs = nlmsg_alloc(); - rates = nlmsg_alloc(); - if (!msg || !ssids || !freqs || !rates) { - nlmsg_free(msg); - nlmsg_free(ssids); - nlmsg_free(freqs); - nlmsg_free(rates); - return -1; - } - - os_free(drv->filter_ssids); - drv->filter_ssids = params->filter_ssids; - params->filter_ssids = NULL; - drv->num_filter_ssids = params->num_filter_ssids; + if (!msg) + return NULL; - nl80211_cmd(drv, msg, 0, NL80211_CMD_TRIGGER_SCAN); + nl80211_cmd(drv, msg, 0, cmd); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, drv->ifindex) < 0) + goto fail; - for (i = 0; i < params->num_ssids; i++) { - wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan SSID", - params->ssids[i].ssid, - params->ssids[i].ssid_len); - NLA_PUT(ssids, i + 1, params->ssids[i].ssid_len, - params->ssids[i].ssid); + if (params->num_ssids) { + struct nl_msg *ssids = nlmsg_alloc(); + if (ssids == NULL) + goto fail; + for (i = 0; i < params->num_ssids; i++) { + wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan SSID", + params->ssids[i].ssid, + params->ssids[i].ssid_len); + if (nla_put(ssids, i + 1, params->ssids[i].ssid_len, + params->ssids[i].ssid) < 0) { + nlmsg_free(ssids); + goto fail; + } + } + err = nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids); + nlmsg_free(ssids); + if (err < 0) + goto fail; } - if (params->num_ssids) - nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids); if (params->extra_ies) { wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan extra IEs", params->extra_ies, params->extra_ies_len); - NLA_PUT(msg, NL80211_ATTR_IE, params->extra_ies_len, - params->extra_ies); + if (nla_put(msg, NL80211_ATTR_IE, params->extra_ies_len, + params->extra_ies) < 0) + goto fail; } if (params->freqs) { + struct nl_msg *freqs = nlmsg_alloc(); + if (freqs == NULL) + goto fail; for (i = 0; params->freqs[i]; i++) { wpa_printf(MSG_MSGDUMP, "nl80211: Scan frequency %u " "MHz", params->freqs[i]); - NLA_PUT_U32(freqs, i + 1, params->freqs[i]); + if (nla_put_u32(freqs, i + 1, params->freqs[i]) < 0) { + nlmsg_free(freqs); + goto fail; + } } - nla_put_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES, freqs); + err = nla_put_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES, + freqs); + nlmsg_free(freqs); + if (err < 0) + goto fail; } + os_free(drv->filter_ssids); + drv->filter_ssids = params->filter_ssids; + params->filter_ssids = NULL; + drv->num_filter_ssids = params->num_filter_ssids; + + return msg; + +fail: + nlmsg_free(msg); + return NULL; +} + + +/** + * wpa_driver_nl80211_scan - Request the driver to initiate scan + * @priv: Pointer to private driver data from wpa_driver_nl80211_init() + * @params: Scan parameters + * Returns: 0 on success, -1 on failure + */ +static int wpa_driver_nl80211_scan(void *priv, + struct wpa_driver_scan_params *params) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int ret = -1, timeout; + struct nl_msg *msg, *rates = NULL; + + drv->scan_for_auth = 0; + + msg = nl80211_scan_common(drv, NL80211_CMD_TRIGGER_SCAN, params); + if (!msg) + return -1; + if (params->p2p_probe) { wpa_printf(MSG_DEBUG, "nl80211: P2P probe - mask SuppRates"); + rates = nlmsg_alloc(); + if (rates == NULL) + goto nla_put_failure; + /* * Remove 2.4 GHz rates 1, 2, 5.5, 11 Mbps from supported rates * by masking out everything else apart from the OFDM rates 6, @@ -3560,7 +3613,9 @@ static int wpa_driver_nl80211_scan(void *priv, */ NLA_PUT(rates, NL80211_BAND_2GHZ, 8, "\x0c\x12\x18\x24\x30\x48\x60\x6c"); - nla_put_nested(msg, NL80211_ATTR_SCAN_SUPP_RATES, rates); + if (nla_put_nested(msg, NL80211_ATTR_SCAN_SUPP_RATES, rates) < + 0) + goto nla_put_failure; NLA_PUT_FLAG(msg, NL80211_ATTR_TX_NO_CCK_RATE); } @@ -3613,9 +3668,7 @@ static int wpa_driver_nl80211_scan(void *priv, drv, drv->ctx); nla_put_failure: - nlmsg_free(ssids); nlmsg_free(msg); - nlmsg_free(freqs); nlmsg_free(rates); return ret; } @@ -3634,8 +3687,10 @@ static int wpa_driver_nl80211_sched_scan(void *priv, { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - int ret = 0; - struct nl_msg *msg, *ssids, *freqs, *match_set_ssid, *match_sets; + int ret = -1; + struct nl_msg *msg; + struct nl_msg *match_set_ssid = NULL, *match_sets = NULL; + struct nl_msg *match_set_rssi = NULL; size_t i; #ifdef ANDROID @@ -3643,30 +3698,18 @@ static int wpa_driver_nl80211_sched_scan(void *priv, return android_pno_start(bss, params); #endif /* ANDROID */ - msg = nlmsg_alloc(); - ssids = nlmsg_alloc(); - freqs = nlmsg_alloc(); - if (!msg || !ssids || !freqs) { - nlmsg_free(msg); - nlmsg_free(ssids); - nlmsg_free(freqs); - return -1; - } - - os_free(drv->filter_ssids); - drv->filter_ssids = params->filter_ssids; - params->filter_ssids = NULL; - drv->num_filter_ssids = params->num_filter_ssids; - - nl80211_cmd(drv, msg, 0, NL80211_CMD_START_SCHED_SCAN); - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + msg = nl80211_scan_common(drv, NL80211_CMD_START_SCHED_SCAN, params); + if (!msg) + goto nla_put_failure; NLA_PUT_U32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, interval); - if (drv->num_filter_ssids && - (int) drv->num_filter_ssids <= drv->capa.max_match_sets) { + if ((drv->num_filter_ssids && + (int) drv->num_filter_ssids <= drv->capa.max_match_sets) || + params->filter_rssi) { match_sets = nlmsg_alloc(); + if (match_sets == NULL) + goto nla_put_failure; for (i = 0; i < drv->num_filter_ssids; i++) { wpa_hexdump_ascii(MSG_MSGDUMP, @@ -3675,45 +3718,35 @@ static int wpa_driver_nl80211_sched_scan(void *priv, drv->filter_ssids[i].ssid_len); match_set_ssid = nlmsg_alloc(); - nla_put(match_set_ssid, + if (match_set_ssid == NULL) + goto nla_put_failure; + NLA_PUT(match_set_ssid, NL80211_ATTR_SCHED_SCAN_MATCH_SSID, drv->filter_ssids[i].ssid_len, drv->filter_ssids[i].ssid); - nla_put_nested(match_sets, i + 1, match_set_ssid); - - nlmsg_free(match_set_ssid); + if (nla_put_nested(match_sets, i + 1, match_set_ssid) < + 0) + goto nla_put_failure; } - nla_put_nested(msg, NL80211_ATTR_SCHED_SCAN_MATCH, - match_sets); - nlmsg_free(match_sets); - } - - for (i = 0; i < params->num_ssids; i++) { - wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Sched scan SSID", - params->ssids[i].ssid, - params->ssids[i].ssid_len); - NLA_PUT(ssids, i + 1, params->ssids[i].ssid_len, - params->ssids[i].ssid); - } - if (params->num_ssids) - nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids); - - if (params->extra_ies) { - wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Sched scan extra IEs", - params->extra_ies, params->extra_ies_len); - NLA_PUT(msg, NL80211_ATTR_IE, params->extra_ies_len, - params->extra_ies); - } - - if (params->freqs) { - for (i = 0; params->freqs[i]; i++) { - wpa_printf(MSG_MSGDUMP, "nl80211: Scan frequency %u " - "MHz", params->freqs[i]); - NLA_PUT_U32(freqs, i + 1, params->freqs[i]); + if (params->filter_rssi) { + match_set_rssi = nlmsg_alloc(); + if (match_set_rssi == NULL) + goto nla_put_failure; + NLA_PUT_U32(match_set_rssi, + NL80211_SCHED_SCAN_MATCH_ATTR_RSSI, + params->filter_rssi); + wpa_printf(MSG_MSGDUMP, + "nl80211: Sched scan RSSI filter %d dBm", + params->filter_rssi); + if (nla_put_nested(match_sets, 0, match_set_rssi) < 0) + goto nla_put_failure; } - nla_put_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES, freqs); + + if (nla_put_nested(msg, NL80211_ATTR_SCHED_SCAN_MATCH, + match_sets) < 0) + goto nla_put_failure; } ret = send_and_recv_msgs(drv, msg, NULL, NULL); @@ -3731,9 +3764,10 @@ static int wpa_driver_nl80211_sched_scan(void *priv, "scan interval %d msec", ret, interval); nla_put_failure: - nlmsg_free(ssids); + nlmsg_free(match_set_ssid); + nlmsg_free(match_sets); + nlmsg_free(match_set_rssi); nlmsg_free(msg); - nlmsg_free(freqs); return ret; } @@ -3981,8 +4015,8 @@ static int bss_info_handler(struct nl_msg *msg, void *arg) return NL_SKIP; } - tmp = os_realloc(res->res, - (res->num + 1) * sizeof(struct wpa_scan_res *)); + tmp = os_realloc_array(res->res, res->num + 1, + sizeof(struct wpa_scan_res *)); if (tmp == NULL) { os_free(r); return NL_SKIP; @@ -4185,6 +4219,10 @@ static int wpa_driver_nl80211_set_key(const char *ifname, void *priv, NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, WLAN_CIPHER_SUITE_CCMP); break; + case WPA_ALG_GCMP: + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, + WLAN_CIPHER_SUITE_GCMP); + break; case WPA_ALG_IGTK: NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, WLAN_CIPHER_SUITE_AES_CMAC); @@ -4325,6 +4363,9 @@ static int nl_add_key(struct nl_msg *msg, enum wpa_alg alg, case WPA_ALG_CCMP: NLA_PUT_U32(msg, NL80211_KEY_CIPHER, WLAN_CIPHER_SUITE_CCMP); break; + case WPA_ALG_GCMP: + NLA_PUT_U32(msg, NL80211_KEY_CIPHER, WLAN_CIPHER_SUITE_GCMP); + break; case WPA_ALG_IGTK: NLA_PUT_U32(msg, NL80211_KEY_CIPHER, WLAN_CIPHER_SUITE_AES_CMAC); @@ -4777,7 +4818,9 @@ static int phy_info_handler(struct nl_msg *msg, void *arg) return NL_SKIP; nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band) { - mode = os_realloc(phy_info->modes, (*phy_info->num_modes + 1) * sizeof(*mode)); + mode = os_realloc_array(phy_info->modes, + *phy_info->num_modes + 1, + sizeof(*mode)); if (!mode) return NL_SKIP; phy_info->modes = mode; @@ -4836,7 +4879,8 @@ static int phy_info_handler(struct nl_msg *msg, void *arg) mode->num_channels++; } - mode->channels = os_zalloc(mode->num_channels * sizeof(struct hostapd_channel_data)); + mode->channels = os_calloc(mode->num_channels, + sizeof(struct hostapd_channel_data)); if (!mode->channels) return NL_SKIP; @@ -4898,7 +4942,7 @@ static int phy_info_handler(struct nl_msg *msg, void *arg) mode->num_rates++; } - mode->rates = os_zalloc(mode->num_rates * sizeof(int)); + mode->rates = os_calloc(mode->num_rates, sizeof(int)); if (!mode->rates) return NL_SKIP; @@ -4943,7 +4987,7 @@ wpa_driver_nl80211_add_11b(struct hostapd_hw_modes *modes, u16 *num_modes) if (mode11g_idx < 0) return modes; /* 2.4 GHz band not supported at all */ - nmodes = os_realloc(modes, (*num_modes + 1) * sizeof(*nmodes)); + nmodes = os_realloc_array(modes, *num_modes + 1, sizeof(*nmodes)); if (nmodes == NULL) return modes; /* Could not add 802.11b mode */ @@ -5457,6 +5501,8 @@ static int wpa_driver_nl80211_set_ap(void *priv, num_suites = 0; if (params->pairwise_ciphers & WPA_CIPHER_CCMP) suites[num_suites++] = WLAN_CIPHER_SUITE_CCMP; + if (params->pairwise_ciphers & WPA_CIPHER_GCMP) + suites[num_suites++] = WLAN_CIPHER_SUITE_GCMP; if (params->pairwise_ciphers & WPA_CIPHER_TKIP) suites[num_suites++] = WLAN_CIPHER_SUITE_TKIP; if (params->pairwise_ciphers & WPA_CIPHER_WEP104) @@ -5473,6 +5519,10 @@ static int wpa_driver_nl80211_set_ap(void *priv, NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, WLAN_CIPHER_SUITE_CCMP); break; + case WPA_CIPHER_GCMP: + NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, + WLAN_CIPHER_SUITE_GCMP); + break; case WPA_CIPHER_TKIP: NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, WLAN_CIPHER_SUITE_TKIP); @@ -5643,7 +5693,8 @@ static int wpa_driver_nl80211_sta_add(void *priv, NLA_PUT_U8(wme, NL80211_STA_WME_MAX_SP, (params->qosinfo > WMM_QOSINFO_STA_SP_SHIFT) & WMM_QOSINFO_STA_SP_MASK); - nla_put_nested(msg, NL80211_ATTR_STA_WME, wme); + if (nla_put_nested(msg, NL80211_ATTR_STA_WME, wme) < 0) + goto nla_put_failure; } ret = send_and_recv_msgs(drv, msg, NULL, NULL); @@ -6746,6 +6797,9 @@ skip_auth_type: case CIPHER_CCMP: cipher = WLAN_CIPHER_SUITE_CCMP; break; + case CIPHER_GCMP: + cipher = WLAN_CIPHER_SUITE_GCMP; + break; case CIPHER_TKIP: default: cipher = WLAN_CIPHER_SUITE_TKIP; @@ -6767,6 +6821,9 @@ skip_auth_type: case CIPHER_CCMP: cipher = WLAN_CIPHER_SUITE_CCMP; break; + case CIPHER_GCMP: + cipher = WLAN_CIPHER_SUITE_GCMP; + break; case CIPHER_TKIP: default: cipher = WLAN_CIPHER_SUITE_TKIP; @@ -6909,6 +6966,9 @@ static int wpa_driver_nl80211_associate( case CIPHER_CCMP: cipher = WLAN_CIPHER_SUITE_CCMP; break; + case CIPHER_GCMP: + cipher = WLAN_CIPHER_SUITE_GCMP; + break; case CIPHER_TKIP: default: cipher = WLAN_CIPHER_SUITE_TKIP; @@ -6931,6 +6991,9 @@ static int wpa_driver_nl80211_associate( case CIPHER_CCMP: cipher = WLAN_CIPHER_SUITE_CCMP; break; + case CIPHER_GCMP: + cipher = WLAN_CIPHER_SUITE_GCMP; + break; case CIPHER_TKIP: default: cipher = WLAN_CIPHER_SUITE_TKIP; @@ -7084,6 +7147,11 @@ done: return ret; } + if (is_p2p_interface(nlmode)) + nl80211_disable_11b_rates(drv, drv->ifindex, 1); + else if (drv->disabled_11b_rates) + nl80211_disable_11b_rates(drv, drv->ifindex, 0); + if (is_ap_interface(nlmode)) { nl80211_mgmt_unsubscribe(bss, "start AP"); /* Setup additional AP mode functionality if needed */ @@ -7336,7 +7404,6 @@ static int get_sta_handler(struct nl_msg *msg, void *arg) [NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 }, [NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 }, [NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 }, - [NL80211_STA_INFO_TX_FAILED] = { .type = NLA_U32 }, }; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), @@ -7372,9 +7439,6 @@ static int get_sta_handler(struct nl_msg *msg, void *arg) if (stats[NL80211_STA_INFO_TX_PACKETS]) data->tx_packets = nla_get_u32(stats[NL80211_STA_INFO_TX_PACKETS]); - if (stats[NL80211_STA_INFO_TX_FAILED]) - data->tx_retry_failed = - nla_get_u32(stats[NL80211_STA_INFO_TX_FAILED]); return NL_SKIP; } @@ -7586,8 +7650,8 @@ static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) else old = NULL; - drv->if_indices = os_realloc(old, - sizeof(int) * (drv->num_if_indices + 1)); + drv->if_indices = os_realloc_array(old, drv->num_if_indices + 1, + sizeof(int)); if (!drv->if_indices) { if (!old) drv->if_indices = drv->default_if_indices; @@ -7651,7 +7715,10 @@ static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val, bridge_ifname, name) < 0) return -1; } - linux_set_iface_flags(drv->global->ioctl_sock, name, 1); + if (linux_set_iface_flags(drv->global->ioctl_sock, name, 1)) { + wpa_printf(MSG_ERROR, "nl80211: Failed to set WDS STA " + "interface %s up", name); + } return i802_set_sta_vlan(priv, addr, name, 0); } else { i802_set_sta_vlan(priv, addr, bss->ifname, 0); @@ -8377,7 +8444,9 @@ static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv, if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Set TX rates failed: ret=%d " "(%s)", ret, strerror(-ret)); - } + } else + drv->disabled_11b_rates = disabled; + return ret; nla_put_failure: @@ -8470,6 +8539,7 @@ static int nl80211_signal_monitor(void *priv, int threshold, int hysteresis) struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg, *cqm = NULL; + int ret = -1; wpa_printf(MSG_DEBUG, "nl80211: Signal monitor threshold=%d " "hysteresis=%d", threshold, hysteresis); @@ -8484,20 +8554,20 @@ static int nl80211_signal_monitor(void *priv, int threshold, int hysteresis) cqm = nlmsg_alloc(); if (cqm == NULL) - return -1; + goto nla_put_failure; NLA_PUT_U32(cqm, NL80211_ATTR_CQM_RSSI_THOLD, threshold); NLA_PUT_U32(cqm, NL80211_ATTR_CQM_RSSI_HYST, hysteresis); - nla_put_nested(msg, NL80211_ATTR_CQM, cqm); + if (nla_put_nested(msg, NL80211_ATTR_CQM, cqm) < 0) + goto nla_put_failure; - if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) - return 0; + ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; nla_put_failure: nlmsg_free(cqm); nlmsg_free(msg); - return -1; + return ret; } diff --git a/src/drivers/driver_privsep.c b/src/drivers/driver_privsep.c index c8916f06..9481cbf8 100644 --- a/src/drivers/driver_privsep.c +++ b/src/drivers/driver_privsep.c @@ -152,7 +152,7 @@ wpa_driver_privsep_get_scan_results2(void *priv) return NULL; } - results->res = os_zalloc(num * sizeof(struct wpa_scan_res *)); + results->res = os_calloc(num, sizeof(struct wpa_scan_res *)); if (results->res == NULL) { os_free(results); os_free(buf); diff --git a/src/drivers/driver_test.c b/src/drivers/driver_test.c index f011651f..e7bf1956 100644 --- a/src/drivers/driver_test.c +++ b/src/drivers/driver_test.c @@ -1478,7 +1478,7 @@ static struct wpa_scan_results * wpa_driver_test_get_scan_results2(void *priv) if (res == NULL) return NULL; - res->res = os_zalloc(drv->num_scanres * sizeof(struct wpa_scan_res *)); + res->res = os_calloc(drv->num_scanres, sizeof(struct wpa_scan_res *)); if (res->res == NULL) { os_free(res); return NULL; @@ -2570,15 +2570,14 @@ wpa_driver_test_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) *num_modes = 3; *flags = 0; - modes = os_zalloc(*num_modes * sizeof(struct hostapd_hw_modes)); + modes = os_calloc(*num_modes, sizeof(struct hostapd_hw_modes)); if (modes == NULL) return NULL; modes[0].mode = HOSTAPD_MODE_IEEE80211G; modes[0].num_channels = 11; modes[0].num_rates = 12; - modes[0].channels = - os_zalloc(11 * sizeof(struct hostapd_channel_data)); - modes[0].rates = os_zalloc(modes[0].num_rates * sizeof(int)); + modes[0].channels = os_calloc(11, sizeof(struct hostapd_channel_data)); + modes[0].rates = os_calloc(modes[0].num_rates, sizeof(int)); if (modes[0].channels == NULL || modes[0].rates == NULL) goto fail; for (i = 0; i < 11; i++) { @@ -2602,9 +2601,8 @@ wpa_driver_test_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) modes[1].mode = HOSTAPD_MODE_IEEE80211B; modes[1].num_channels = 11; modes[1].num_rates = 4; - modes[1].channels = - os_zalloc(11 * sizeof(struct hostapd_channel_data)); - modes[1].rates = os_zalloc(modes[1].num_rates * sizeof(int)); + modes[1].channels = os_calloc(11, sizeof(struct hostapd_channel_data)); + modes[1].rates = os_calloc(modes[1].num_rates, sizeof(int)); if (modes[1].channels == NULL || modes[1].rates == NULL) goto fail; for (i = 0; i < 11; i++) { @@ -2620,8 +2618,8 @@ wpa_driver_test_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) modes[2].mode = HOSTAPD_MODE_IEEE80211A; modes[2].num_channels = 1; modes[2].num_rates = 8; - modes[2].channels = os_zalloc(sizeof(struct hostapd_channel_data)); - modes[2].rates = os_zalloc(modes[2].num_rates * sizeof(int)); + modes[2].channels = os_calloc(1, sizeof(struct hostapd_channel_data)); + modes[2].rates = os_calloc(modes[2].num_rates, sizeof(int)); if (modes[2].channels == NULL || modes[2].rates == NULL) goto fail; modes[2].channels[0].chan = 60; @@ -2822,17 +2820,19 @@ static int wpa_driver_test_probe_req_report(void *priv, int report) static int wpa_driver_test_p2p_find(void *priv, unsigned int timeout, int type) { - struct wpa_driver_test_data *drv = priv; + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; wpa_printf(MSG_DEBUG, "%s(timeout=%u)", __func__, timeout); if (!drv->p2p) return -1; - return p2p_find(drv->p2p, timeout, type, 0, NULL, NULL); + return p2p_find(drv->p2p, timeout, type, 0, NULL, NULL, 0); } static int wpa_driver_test_p2p_stop_find(void *priv) { - struct wpa_driver_test_data *drv = priv; + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; wpa_printf(MSG_DEBUG, "%s", __func__); if (!drv->p2p) return -1; @@ -2843,7 +2843,8 @@ static int wpa_driver_test_p2p_stop_find(void *priv) static int wpa_driver_test_p2p_listen(void *priv, unsigned int timeout) { - struct wpa_driver_test_data *drv = priv; + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; wpa_printf(MSG_DEBUG, "%s(timeout=%u)", __func__, timeout); if (!drv->p2p) return -1; @@ -2857,7 +2858,8 @@ static int wpa_driver_test_p2p_connect(void *priv, const u8 *peer_addr, unsigned int force_freq, int persistent_group) { - struct wpa_driver_test_data *drv = priv; + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; wpa_printf(MSG_DEBUG, "%s(peer_addr=" MACSTR " wps_method=%d " "go_intent=%d " "own_interface_addr=" MACSTR " force_freq=%u " @@ -2874,7 +2876,8 @@ static int wpa_driver_test_p2p_connect(void *priv, const u8 *peer_addr, static int wpa_driver_test_wps_success_cb(void *priv, const u8 *peer_addr) { - struct wpa_driver_test_data *drv = priv; + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; wpa_printf(MSG_DEBUG, "%s(peer_addr=" MACSTR ")", __func__, MAC2STR(peer_addr)); if (!drv->p2p) @@ -2886,7 +2889,8 @@ static int wpa_driver_test_wps_success_cb(void *priv, const u8 *peer_addr) static int wpa_driver_test_p2p_group_formation_failed(void *priv) { - struct wpa_driver_test_data *drv = priv; + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; wpa_printf(MSG_DEBUG, "%s", __func__); if (!drv->p2p) return -1; @@ -2898,7 +2902,8 @@ static int wpa_driver_test_p2p_group_formation_failed(void *priv) static int wpa_driver_test_p2p_set_params(void *priv, const struct p2p_params *params) { - struct wpa_driver_test_data *drv = priv; + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; wpa_printf(MSG_DEBUG, "%s", __func__); if (!drv->p2p) return -1; diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c index 068e4162..bd37ca14 100644 --- a/src/drivers/driver_wext.c +++ b/src/drivers/driver_wext.c @@ -40,10 +40,7 @@ static int wpa_driver_wext_get_range(void *priv); static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv); static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv); static int wpa_driver_wext_set_auth_alg(void *priv, int auth_alg); -#ifdef ANDROID -extern int wpa_driver_wext_combo_scan(void *priv, - struct wpa_driver_scan_params *params); -#endif + int wpa_driver_wext_set_auth_param(struct wpa_driver_wext_data *drv, int idx, u32 value) @@ -481,19 +478,10 @@ static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv, drv->assoc_req_ies = NULL; os_free(drv->assoc_resp_ies); drv->assoc_resp_ies = NULL; -#ifdef ANDROID - if (!drv->skip_disconnect) { - drv->skip_disconnect = 1; -#endif wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); -#ifdef ANDROID - } -#endif + } else { -#ifdef ANDROID - drv->skip_disconnect = 0; -#endif wpa_driver_wext_event_assoc_ies(drv); wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL); @@ -875,7 +863,6 @@ void * wpa_driver_wext_init(void *ctx, const char *ifname) #ifdef ANDROID drv->errors = 0; drv->driver_is_started = TRUE; - drv->skip_disconnect = 0; drv->bgscan_enabled = 0; #endif /* ANDROID */ @@ -1039,13 +1026,6 @@ int wpa_driver_wext_scan(void *priv, struct wpa_driver_scan_params *params) const u8 *ssid = params->ssids[0].ssid; size_t ssid_len = params->ssids[0].ssid_len; -#ifdef ANDROID - if (drv->capa.max_scan_ssids > 1) { - ret = wpa_driver_wext_combo_scan(priv, params); - goto scan_out; - } -#endif - if (ssid_len > IW_ESSID_MAX_SIZE) { wpa_printf(MSG_DEBUG, "%s: too long SSID (%lu)", __FUNCTION__, (unsigned long) ssid_len); @@ -1071,9 +1051,6 @@ int wpa_driver_wext_scan(void *priv, struct wpa_driver_scan_params *params) ret = -1; } -#ifdef ANDROID -scan_out: -#endif /* Not all drivers generate "scan completed" wireless event, so try to * read results after a timeout. */ timeout = 10; @@ -1437,8 +1414,8 @@ static void wpa_driver_wext_add_scan_entry(struct wpa_scan_results *res, if (data->ie) os_memcpy(pos, data->ie, data->ie_len); - tmp = os_realloc(res->res, - (res->num + 1) * sizeof(struct wpa_scan_res *)); + tmp = os_realloc_array(res->res, res->num + 1, + sizeof(struct wpa_scan_res *)); if (tmp == NULL) { os_free(r); return; @@ -1608,11 +1585,7 @@ static int wpa_driver_wext_get_range(void *priv) drv->capa.auth = WPA_DRIVER_AUTH_OPEN | WPA_DRIVER_AUTH_SHARED | WPA_DRIVER_AUTH_LEAP; -#ifdef ANDROID - drv->capa.max_scan_ssids = WEXT_CSCAN_AMOUNT; -#else drv->capa.max_scan_ssids = 1; -#endif wpa_printf(MSG_DEBUG, " capabilities: key_mgmt 0x%x enc 0x%x " "flags 0x%x", @@ -2084,9 +2057,7 @@ int wpa_driver_wext_associate(void *priv, int value; wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); -#ifdef ANDROID - drv->skip_disconnect = 0; -#endif + if (drv->cfg80211) { /* * Stop cfg80211 from trying to associate before we are done diff --git a/src/drivers/driver_wext.h b/src/drivers/driver_wext.h index 7e0b28b6..c4a5bc99 100644 --- a/src/drivers/driver_wext.h +++ b/src/drivers/driver_wext.h @@ -48,7 +48,6 @@ struct wpa_driver_wext_data { #ifdef ANDROID int errors; int driver_is_started; - int skip_disconnect; int bgscan_enabled; #endif /* ANDROID */ }; diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak index 0cc81f9c..c7a98d31 100644 --- a/src/drivers/drivers.mak +++ b/src/drivers/drivers.mak @@ -30,7 +30,7 @@ NEED_RFKILL=y ifdef CONFIG_LIBNL32 DRV_LIBS += -lnl-3 DRV_LIBS += -lnl-genl-3 - DRV_CFLAGS += -DCONFIG_LIBNL20 + DRV_CFLAGS += -DCONFIG_LIBNL20 -I/usr/include/libnl3 else ifdef CONFIG_LIBNL_TINY DRV_LIBS += -lnl-tiny @@ -142,6 +142,28 @@ ifdef NEED_RFKILL DRV_OBJS += ../src/drivers/rfkill.o endif +ifdef CONFIG_VLAN_NETLINK +ifdef CONFIG_FULL_DYNAMIC_VLAN +ifdef CONFIG_LIBNL32 + DRV_LIBS += -lnl-3 + DRV_LIBS += -lnl-genl-3 + DRV_LIBS += -lnl-route-3 + DRV_CFLAGS += -DCONFIG_LIBNL20 +else + ifdef CONFIG_LIBNL_TINY + DRV_LIBS += -lnl-tiny + else + DRV_LIBS += -lnl + endif + + ifdef CONFIG_LIBNL20 + DRV_LIBS += -lnl-genl + DRV_LIBS += -lnl-route + DRV_CFLAGS += -DCONFIG_LIBNL20 + endif +endif +endif +endif ##### COMMON VARS DRV_BOTH_CFLAGS := $(DRV_CFLAGS) $(DRV_WPA_CFLAGS) $(DRV_AP_CFLAGS) diff --git a/src/drivers/drivers.mk b/src/drivers/drivers.mk index 1d7129cd..23fcbb7c 100644 --- a/src/drivers/drivers.mk +++ b/src/drivers/drivers.mk @@ -30,7 +30,7 @@ NEED_RFKILL=y ifdef CONFIG_LIBNL32 DRV_LIBS += -lnl-3 DRV_LIBS += -lnl-genl-3 - DRV_CFLAGS += -DCONFIG_LIBNL20 + DRV_CFLAGS += -DCONFIG_LIBNL20 -I/usr/include/libnl3 else ifdef CONFIG_LIBNL_TINY DRV_LIBS += -lnl-tiny @@ -146,6 +146,29 @@ ifdef CONFIG_DRIVER_CUSTOM DRV_CFLAGS += -DCONFIG_DRIVER_CUSTOM endif +ifdef CONFIG_VLAN_NETLINK +ifdef CONFIG_FULL_DYNAMIC_VLAN +ifdef CONFIG_LIBNL32 + DRV_LIBS += -lnl-3 + DRV_LIBS += -lnl-genl-3 + DRV_LIBS += -lnl-route-3 + DRV_CFLAGS += -DCONFIG_LIBNL20 +else + ifdef CONFIG_LIBNL_TINY + DRV_LIBS += -lnl-tiny + else + DRV_LIBS += -lnl + endif + + ifdef CONFIG_LIBNL20 + DRV_LIBS += -lnl-genl + DRV_LIBS += -lnl-route + DRV_CFLAGS += -DCONFIG_LIBNL20 + endif +endif +endif +endif + ##### COMMON VARS DRV_BOTH_CFLAGS := $(DRV_CFLAGS) $(DRV_WPA_CFLAGS) $(DRV_AP_CFLAGS) DRV_WPA_CFLAGS += $(DRV_CFLAGS) diff --git a/src/drivers/linux_ioctl.c b/src/drivers/linux_ioctl.c index 940b69af..4380428f 100644 --- a/src/drivers/linux_ioctl.c +++ b/src/drivers/linux_ioctl.c @@ -45,8 +45,9 @@ int linux_set_iface_flags(int sock, const char *ifname, int dev_up) if (ioctl(sock, SIOCSIFFLAGS, &ifr) != 0) { ret = errno ? -errno : -999; - wpa_printf(MSG_ERROR, "Could not set interface %s flags: %s", - ifname, strerror(errno)); + wpa_printf(MSG_ERROR, "Could not set interface %s flags (%s): " + "%s", + ifname, dev_up ? "UP" : "DOWN", strerror(errno)); return ret; } diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h index 74cc55c1..2f387880 100644 --- a/src/drivers/nl80211_copy.h +++ b/src/drivers/nl80211_copy.h @@ -771,6 +771,9 @@ enum nl80211_commands { * @NL80211_ATTR_IFNAME: network interface name * @NL80211_ATTR_IFTYPE: type of virtual interface, see &enum nl80211_iftype * + * @NL80211_ATTR_WDEV: wireless device identifier, used for pseudo-devices + * that don't have a netdev (u64) + * * @NL80211_ATTR_MAC: MAC address (various uses) * * @NL80211_ATTR_KEY_DATA: (temporal) key data; for TKIP this consists of @@ -1242,6 +1245,12 @@ enum nl80211_commands { * @NL80211_ATTR_BG_SCAN_PERIOD: Background scan period in seconds * or 0 to disable background scan. * + * @NL80211_ATTR_USER_REG_HINT_TYPE: type of regulatory hint passed from + * userspace. If unset it is assumed the hint comes directly from + * a user. If set code could specify exactly what type of source + * was used to provide the hint. For the different types of + * allowed user regulatory hints see nl80211_user_reg_hint_type. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1493,6 +1502,10 @@ enum nl80211_attrs { NL80211_ATTR_BG_SCAN_PERIOD, + NL80211_ATTR_WDEV, + + NL80211_ATTR_USER_REG_HINT_TYPE, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -1545,6 +1558,8 @@ enum nl80211_attrs { /* default RSSI threshold for scan results if none specified. */ #define NL80211_SCAN_RSSI_THOLD_OFF -300 +#define NL80211_CQM_TXE_MAX_INTVL 1800 + /** * enum nl80211_iftype - (virtual) interface types * @@ -1638,12 +1653,20 @@ struct nl80211_sta_flag_update { * * These attribute types are used with %NL80211_STA_INFO_TXRATE * when getting information about the bitrate of a station. + * There are 2 attributes for bitrate, a legacy one that represents + * a 16-bit value, and new one that represents a 32-bit value. + * If the rate value fits into 16 bit, both attributes are reported + * with the same value. If the rate is too high to fit into 16 bits + * (>6.5535Gbps) only 32-bit attribute is included. + * User space tools encouraged to use the 32-bit attribute and fall + * back to the 16-bit one for compatibility with older kernels. * * @__NL80211_RATE_INFO_INVALID: attribute number 0 is reserved * @NL80211_RATE_INFO_BITRATE: total bitrate (u16, 100kbit/s) * @NL80211_RATE_INFO_MCS: mcs index for 802.11n (u8) * @NL80211_RATE_INFO_40_MHZ_WIDTH: 40 Mhz dualchannel bitrate * @NL80211_RATE_INFO_SHORT_GI: 400ns guard interval + * @NL80211_RATE_INFO_BITRATE32: total bitrate (u32, 100kbit/s) * @NL80211_RATE_INFO_MAX: highest rate_info number currently defined * @__NL80211_RATE_INFO_AFTER_LAST: internal use */ @@ -1653,6 +1676,7 @@ enum nl80211_rate_info { NL80211_RATE_INFO_MCS, NL80211_RATE_INFO_40_MHZ_WIDTH, NL80211_RATE_INFO_SHORT_GI, + NL80211_RATE_INFO_BITRATE32, /* keep last */ __NL80211_RATE_INFO_AFTER_LAST, @@ -2045,6 +2069,26 @@ enum nl80211_dfs_regions { }; /** + * enum nl80211_user_reg_hint_type - type of user regulatory hint + * + * @NL80211_USER_REG_HINT_USER: a user sent the hint. This is always + * assumed if the attribute is not set. + * @NL80211_USER_REG_HINT_CELL_BASE: the hint comes from a cellular + * base station. Device drivers that have been tested to work + * properly to support this type of hint can enable these hints + * by setting the NL80211_FEATURE_CELL_BASE_REG_HINTS feature + * capability on the struct wiphy. The wireless core will + * ignore all cell base station hints until at least one device + * present has been registered with the wireless core that + * has listed NL80211_FEATURE_CELL_BASE_REG_HINTS as a + * supported feature. + */ +enum nl80211_user_reg_hint_type { + NL80211_USER_REG_HINT_USER = 0, + NL80211_USER_REG_HINT_CELL_BASE = 1, +}; + +/** * enum nl80211_survey_info - survey information * * These attribute types are used with %NL80211_ATTR_SURVEY_INFO @@ -2575,6 +2619,17 @@ enum nl80211_ps_state { * @NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT: RSSI threshold event * @NL80211_ATTR_CQM_PKT_LOSS_EVENT: a u32 value indicating that this many * consecutive packets were not acknowledged by the peer + * @NL80211_ATTR_CQM_TXE_RATE: TX error rate in %. Minimum % of TX failures + * during the given %NL80211_ATTR_CQM_TXE_INTVL before an + * %NL80211_CMD_NOTIFY_CQM with reported %NL80211_ATTR_CQM_TXE_RATE and + * %NL80211_ATTR_CQM_TXE_PKTS is generated. + * @NL80211_ATTR_CQM_TXE_PKTS: number of attempted packets in a given + * %NL80211_ATTR_CQM_TXE_INTVL before %NL80211_ATTR_CQM_TXE_RATE is + * checked. + * @NL80211_ATTR_CQM_TXE_INTVL: interval in seconds. Specifies the periodic + * interval in which %NL80211_ATTR_CQM_TXE_PKTS and + * %NL80211_ATTR_CQM_TXE_RATE must be satisfied before generating an + * %NL80211_CMD_NOTIFY_CQM. Set to 0 to turn off TX error reporting. * @__NL80211_ATTR_CQM_AFTER_LAST: internal * @NL80211_ATTR_CQM_MAX: highest key attribute */ @@ -2584,6 +2639,9 @@ enum nl80211_attr_cqm { NL80211_ATTR_CQM_RSSI_HYST, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT, NL80211_ATTR_CQM_PKT_LOSS_EVENT, + NL80211_ATTR_CQM_TXE_RATE, + NL80211_ATTR_CQM_TXE_PKTS, + NL80211_ATTR_CQM_TXE_INTVL, /* keep last */ __NL80211_ATTR_CQM_AFTER_LAST, @@ -2933,11 +2991,15 @@ enum nl80211_ap_sme_features { * @NL80211_FEATURE_HT_IBSS: This driver supports IBSS with HT datarates. * @NL80211_FEATURE_INACTIVITY_TIMER: This driver takes care of freeing up * the connected inactive stations in AP mode. + * @NL80211_FEATURE_CELL_BASE_REG_HINTS: This driver has been tested + * to work properly to suppport receiving regulatory hints from + * cellular base stations. */ enum nl80211_feature_flags { NL80211_FEATURE_SK_TX_STATUS = 1 << 0, NL80211_FEATURE_HT_IBSS = 1 << 1, NL80211_FEATURE_INACTIVITY_TIMER = 1 << 2, + NL80211_FEATURE_CELL_BASE_REG_HINTS = 1 << 3, }; /** diff --git a/src/eap_common/eap_common.c b/src/eap_common/eap_common.c index 0d6ef931..7b077cb9 100644 --- a/src/eap_common/eap_common.c +++ b/src/eap_common/eap_common.c @@ -1,6 +1,6 @@ /* * EAP common peer/server definitions - * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -13,6 +13,41 @@ #include "eap_common.h" /** + * eap_hdr_len_valid - Validate EAP header length field + * @msg: EAP frame (starting with EAP header) + * @min_payload: Minimum payload length needed + * Returns: 1 for valid header, 0 for invalid + * + * This is a helper function that does minimal validation of EAP messages. The + * length field is verified to be large enough to include the header and not + * too large to go beyond the end of the buffer. + */ +int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload) +{ + const struct eap_hdr *hdr; + size_t len; + + if (msg == NULL) + return 0; + + hdr = wpabuf_head(msg); + + if (wpabuf_len(msg) < sizeof(*hdr)) { + wpa_printf(MSG_INFO, "EAP: Too short EAP frame"); + return 0; + } + + len = be_to_host16(hdr->length); + if (len < sizeof(*hdr) + min_payload || len > wpabuf_len(msg)) { + wpa_printf(MSG_INFO, "EAP: Invalid EAP length"); + return 0; + } + + return 1; +} + + +/** * eap_hdr_validate - Validate EAP header * @vendor: Expected EAP Vendor-Id (0 = IETF) * @eap_type: Expected EAP type number @@ -35,19 +70,11 @@ const u8 * eap_hdr_validate(int vendor, EapType eap_type, const u8 *pos; size_t len; - hdr = wpabuf_head(msg); - - if (wpabuf_len(msg) < sizeof(*hdr)) { - wpa_printf(MSG_INFO, "EAP: Too short EAP frame"); + if (!eap_hdr_len_valid(msg, 1)) return NULL; - } + hdr = wpabuf_head(msg); len = be_to_host16(hdr->length); - if (len < sizeof(*hdr) + 1 || len > wpabuf_len(msg)) { - wpa_printf(MSG_INFO, "EAP: Invalid EAP length"); - return NULL; - } - pos = (const u8 *) (hdr + 1); if (*pos == EAP_TYPE_EXPANDED) { diff --git a/src/eap_common/eap_common.h b/src/eap_common/eap_common.h index 73f27972..8850c1fe 100644 --- a/src/eap_common/eap_common.h +++ b/src/eap_common/eap_common.h @@ -1,6 +1,6 @@ /* * EAP common peer/server definitions - * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -11,6 +11,7 @@ #include "wpabuf.h" +int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload); const u8 * eap_hdr_validate(int vendor, EapType eap_type, const struct wpabuf *msg, size_t *plen); struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len, diff --git a/src/eap_common/eap_defs.h b/src/eap_common/eap_defs.h index 360193a9..0d247c4d 100644 --- a/src/eap_common/eap_defs.h +++ b/src/eap_common/eap_defs.h @@ -71,9 +71,13 @@ typedef enum { enum { EAP_VENDOR_IETF = 0, EAP_VENDOR_MICROSOFT = 0x000137 /* Microsoft */, - EAP_VENDOR_WFA = 0x00372A /* Wi-Fi Alliance */ + EAP_VENDOR_WFA = 0x00372A /* Wi-Fi Alliance */, + EAP_VENDOR_HOSTAP = 39068 /* hostapd/wpa_supplicant project */ }; +#define EAP_VENDOR_UNAUTH_TLS EAP_VENDOR_HOSTAP +#define EAP_VENDOR_TYPE_UNAUTH_TLS 1 + #define EAP_MSK_LEN 64 #define EAP_EMSK_LEN 64 diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c index 8b43be43..ba973a52 100644 --- a/src/eap_peer/eap.c +++ b/src/eap_peer/eap.c @@ -20,6 +20,7 @@ #include "common.h" #include "pcsc_funcs.h" #include "state_machine.h" +#include "ext_password.h" #include "crypto/crypto.h" #include "crypto/tls.h" #include "common/wpa_ctrl.h" @@ -93,6 +94,9 @@ static void eap_notify_status(struct eap_sm *sm, const char *status, static void eap_deinit_prev_method(struct eap_sm *sm, const char *txt) { + ext_password_free(sm->ext_pw_buf); + sm->ext_pw_buf = NULL; + if (sm->m == NULL || sm->eap_method_priv == NULL) return; @@ -185,6 +189,12 @@ SM_STATE(EAP, DISABLED) { SM_ENTRY(EAP, DISABLED); sm->num_rounds = 0; + /* + * RFC 4137 does not describe clearing of idleWhile here, but doing so + * allows the timer tick to be stopped more quickly when EAP is not in + * use. + */ + eapol_set_int(sm, EAPOL_idleWhile, 0); } @@ -346,6 +356,8 @@ SM_STATE(EAP, METHOD) } eapReqData = eapol_get_eapReqData(sm); + if (!eap_hdr_len_valid(eapReqData, 1)) + return; /* * Get ignore, methodState, decision, allowNotifications, and @@ -434,6 +446,8 @@ SM_STATE(EAP, IDENTITY) SM_ENTRY(EAP, IDENTITY); eapReqData = eapol_get_eapReqData(sm); + if (!eap_hdr_len_valid(eapReqData, 1)) + return; eap_sm_processIdentity(sm, eapReqData); wpabuf_free(sm->eapRespData); sm->eapRespData = NULL; @@ -450,6 +464,8 @@ SM_STATE(EAP, NOTIFICATION) SM_ENTRY(EAP, NOTIFICATION); eapReqData = eapol_get_eapReqData(sm); + if (!eap_hdr_len_valid(eapReqData, 1)) + return; eap_sm_processNotify(sm, eapReqData); wpabuf_free(sm->eapRespData); sm->eapRespData = NULL; @@ -867,13 +883,17 @@ static struct wpabuf * eap_sm_buildNak(struct eap_sm *sm, int id) static void eap_sm_processIdentity(struct eap_sm *sm, const struct wpabuf *req) { - const struct eap_hdr *hdr = wpabuf_head(req); - const u8 *pos = (const u8 *) (hdr + 1); - pos++; + const u8 *pos; + size_t msg_len; wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_STARTED "EAP authentication started"); + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, req, + &msg_len); + if (pos == NULL) + return; + /* * RFC 3748 - 5.1: Identity * Data field may contain a displayable message in UTF-8. If this @@ -884,7 +904,7 @@ static void eap_sm_processIdentity(struct eap_sm *sm, const struct wpabuf *req) /* TODO: could save displayable message so that it can be shown to the * user in case of interaction is required */ wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Identity data", - pos, be_to_host16(hdr->length) - 5); + pos, msg_len); } @@ -1915,6 +1935,27 @@ const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len) } +static int eap_get_ext_password(struct eap_sm *sm, + struct eap_peer_config *config) +{ + char *name; + + if (config->password == NULL) + return -1; + + name = os_zalloc(config->password_len + 1); + if (name == NULL) + return -1; + os_memcpy(name, config->password, config->password_len); + + ext_password_free(sm->ext_pw_buf); + sm->ext_pw_buf = ext_password_get(sm->ext_pw, name); + os_free(name); + + return sm->ext_pw_buf == NULL ? -1 : 0; +} + + /** * eap_get_config_password - Get password from the network configuration * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() @@ -1926,6 +1967,14 @@ const u8 * eap_get_config_password(struct eap_sm *sm, size_t *len) struct eap_peer_config *config = eap_get_config(sm); if (config == NULL) return NULL; + + if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) { + if (eap_get_ext_password(sm, config) < 0) + return NULL; + *len = wpabuf_len(sm->ext_pw_buf); + return wpabuf_head(sm->ext_pw_buf); + } + *len = config->password_len; return config->password; } @@ -1945,6 +1994,14 @@ const u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash) struct eap_peer_config *config = eap_get_config(sm); if (config == NULL) return NULL; + + if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) { + if (eap_get_ext_password(sm, config) < 0) + return NULL; + *len = wpabuf_len(sm->ext_pw_buf); + return wpabuf_head(sm->ext_pw_buf); + } + *len = config->password_len; if (hash) *hash = !!(config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH); @@ -2256,3 +2313,11 @@ int eap_is_wps_pin_enrollee(struct eap_peer_config *conf) return 1; } + + +void eap_sm_set_ext_pw_ctx(struct eap_sm *sm, struct ext_password_data *ext) +{ + ext_password_free(sm->ext_pw_buf); + sm->ext_pw_buf = NULL; + sm->ext_pw = ext; +} diff --git a/src/eap_peer/eap.h b/src/eap_peer/eap.h index 6e87475d..cf58608c 100644 --- a/src/eap_peer/eap.h +++ b/src/eap_peer/eap.h @@ -306,6 +306,9 @@ void eap_invalidate_cached_session(struct eap_sm *sm); int eap_is_wps_pbc_enrollee(struct eap_peer_config *conf); int eap_is_wps_pin_enrollee(struct eap_peer_config *conf); +struct ext_password_data; +void eap_sm_set_ext_pw_ctx(struct eap_sm *sm, struct ext_password_data *ext); + #endif /* IEEE8021X_EAPOL */ #endif /* EAP_H */ diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h index a6f6f4cf..a08543e1 100644 --- a/src/eap_peer/eap_config.h +++ b/src/eap_peer/eap_config.h @@ -619,6 +619,7 @@ struct eap_peer_config { int fragment_size; #define EAP_CONFIG_FLAGS_PASSWORD_NTHASH BIT(0) +#define EAP_CONFIG_FLAGS_EXT_PASSWORD BIT(1) /** * flags - Network configuration flags (bitfield) * @@ -626,6 +627,8 @@ struct eap_peer_config { * for the network parameters. * bit 0 = password is represented as a 16-byte NtPasswordHash value * instead of plaintext password + * bit 1 = password is stored in external storage; the value in the + * password field is the name of that external entry */ u32 flags; }; diff --git a/src/eap_peer/eap_fast.c b/src/eap_peer/eap_fast.c index 31d9f7cf..7ca5288c 100644 --- a/src/eap_peer/eap_fast.c +++ b/src/eap_peer/eap_fast.c @@ -169,7 +169,7 @@ static void * eap_fast_init(struct eap_sm *sm) data->phase2_type.vendor = EAP_VENDOR_IETF; data->phase2_type.method = EAP_TYPE_NONE; - if (eap_peer_tls_ssl_init(sm, &data->ssl, config)) { + if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_FAST)) { wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize SSL."); eap_fast_deinit(sm, data); return NULL; diff --git a/src/eap_peer/eap_i.h b/src/eap_peer/eap_i.h index 3318b811..dd943174 100644 --- a/src/eap_peer/eap_i.h +++ b/src/eap_peer/eap_i.h @@ -330,6 +330,9 @@ struct eap_sm { struct wps_context *wps; int prev_failure; + + struct ext_password_data *ext_pw; + struct wpabuf *ext_pw_buf; }; const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len); diff --git a/src/eap_peer/eap_md5.c b/src/eap_peer/eap_md5.c index e3484151..d06befae 100644 --- a/src/eap_peer/eap_md5.c +++ b/src/eap_peer/eap_md5.c @@ -1,6 +1,6 @@ /* * EAP peer method: EAP-MD5 (RFC 3748 and RFC 1994) - * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -86,7 +86,13 @@ static struct wpabuf * eap_md5_process(struct eap_sm *sm, void *priv, id = eap_get_id(resp); rpos = wpabuf_put(resp, CHAP_MD5_LEN); - chap_md5(id, password, password_len, challenge, challenge_len, rpos); + if (chap_md5(id, password, password_len, challenge, challenge_len, + rpos)) { + wpa_printf(MSG_INFO, "EAP-MD5: CHAP MD5 operation failed"); + ret->ignore = TRUE; + wpabuf_free(resp); + return NULL; + } wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Response", rpos, CHAP_MD5_LEN); return resp; diff --git a/src/eap_peer/eap_methods.h b/src/eap_peer/eap_methods.h index ff9f50db..4994ff1c 100644 --- a/src/eap_peer/eap_methods.h +++ b/src/eap_peer/eap_methods.h @@ -85,6 +85,7 @@ static inline int eap_peer_method_unload(struct eap_method *method) /* EAP peer method registration calls for statically linked in methods */ int eap_peer_md5_register(void); int eap_peer_tls_register(void); +int eap_peer_unauth_tls_register(void); int eap_peer_mschapv2_register(void); int eap_peer_peap_register(void); int eap_peer_ttls_register(void); diff --git a/src/eap_peer/eap_mschapv2.c b/src/eap_peer/eap_mschapv2.c index 3b0a116b..fb6c282a 100644 --- a/src/eap_peer/eap_mschapv2.c +++ b/src/eap_peer/eap_mschapv2.c @@ -304,7 +304,9 @@ static void eap_mschapv2_password_changed(struct eap_sm *sm, "EAP-MSCHAPV2: Password changed successfully"); data->prev_error = 0; os_free(config->password); - if (config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH) { + if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) { + /* TODO: update external storage */ + } else if (config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH) { config->password = os_malloc(16); config->password_len = 16; if (config->password) { diff --git a/src/eap_peer/eap_peap.c b/src/eap_peer/eap_peap.c index 0caa77e2..7fff1458 100644 --- a/src/eap_peer/eap_peap.c +++ b/src/eap_peer/eap_peap.c @@ -159,7 +159,7 @@ static void * eap_peap_init(struct eap_sm *sm) data->phase2_type.vendor = EAP_VENDOR_IETF; data->phase2_type.method = EAP_TYPE_NONE; - if (eap_peer_tls_ssl_init(sm, &data->ssl, config)) { + if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_PEAP)) { wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL."); eap_peap_deinit(sm, data); return NULL; diff --git a/src/eap_peer/eap_tls.c b/src/eap_peer/eap_tls.c index ed52fb69..061a72b1 100644 --- a/src/eap_peer/eap_tls.c +++ b/src/eap_peer/eap_tls.c @@ -22,6 +22,7 @@ struct eap_tls_data { struct eap_ssl_data ssl; u8 *key_data; void *ssl_ctx; + u8 eap_type; }; @@ -44,7 +45,7 @@ static void * eap_tls_init(struct eap_sm *sm) data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 : sm->ssl_ctx; - if (eap_peer_tls_ssl_init(sm, &data->ssl, config)) { + if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_TLS)) { wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); eap_tls_deinit(sm, data); if (config->engine) { @@ -62,8 +63,37 @@ static void * eap_tls_init(struct eap_sm *sm) return NULL; } + data->eap_type = EAP_TYPE_TLS; + + return data; +} + + +#ifdef EAP_UNAUTH_TLS +static void * eap_unauth_tls_init(struct eap_sm *sm) +{ + struct eap_tls_data *data; + struct eap_peer_config *config = eap_get_config(sm); + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + + data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 : + sm->ssl_ctx; + + if (eap_peer_tls_ssl_init(sm, &data->ssl, config, + EAP_UNAUTH_TLS_TYPE)) { + wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); + eap_tls_deinit(sm, data); + return NULL; + } + + data->eap_type = EAP_UNAUTH_TLS_TYPE; + return data; } +#endif /* EAP_UNAUTH_TLS */ static void eap_tls_deinit(struct eap_sm *sm, void *priv) @@ -109,7 +139,7 @@ static struct wpabuf * eap_tls_failure(struct eap_sm *sm, return resp; } - return eap_peer_tls_build_ack(id, EAP_TYPE_TLS, 0); + return eap_peer_tls_build_ack(id, data->eap_type, 0); } @@ -149,7 +179,7 @@ static struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv, const u8 *pos; struct eap_tls_data *data = priv; - pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_TLS, ret, + pos = eap_peer_tls_process_init(sm, &data->ssl, data->eap_type, ret, reqData, &left, &flags); if (pos == NULL) return NULL; @@ -162,8 +192,8 @@ static struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv, } resp = NULL; - res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_TLS, 0, id, - pos, left, &resp); + res = eap_peer_tls_process_helper(sm, &data->ssl, data->eap_type, 0, + id, pos, left, &resp); if (res < 0) { return eap_tls_failure(sm, data, ret, res, resp, id); @@ -174,7 +204,7 @@ static struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv, if (res == 1) { wpabuf_free(resp); - return eap_peer_tls_build_ack(id, EAP_TYPE_TLS, 0); + return eap_peer_tls_build_ack(id, data->eap_type, 0); } return resp; @@ -285,3 +315,34 @@ int eap_peer_tls_register(void) eap_peer_method_free(eap); return ret; } + + +#ifdef EAP_UNAUTH_TLS +int eap_peer_unauth_tls_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_UNAUTH_TLS, + EAP_VENDOR_TYPE_UNAUTH_TLS, "UNAUTH-TLS"); + if (eap == NULL) + return -1; + + eap->init = eap_unauth_tls_init; + eap->deinit = eap_tls_deinit; + eap->process = eap_tls_process; + eap->isKeyAvailable = eap_tls_isKeyAvailable; + eap->getKey = eap_tls_getKey; + eap->get_status = eap_tls_get_status; + eap->has_reauth_data = eap_tls_has_reauth_data; + eap->deinit_for_reauth = eap_tls_deinit_for_reauth; + eap->init_for_reauth = eap_tls_init_for_reauth; + eap->get_emsk = eap_tls_get_emsk; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} +#endif /* EAP_UNAUTH_TLS */ diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c index 69e83d9b..7eefe8c9 100644 --- a/src/eap_peer/eap_tls_common.c +++ b/src/eap_peer/eap_tls_common.c @@ -16,6 +16,18 @@ #include "eap_config.h" +static struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len, + u8 code, u8 identifier) +{ + if (type == EAP_UNAUTH_TLS_TYPE) + return eap_msg_alloc(EAP_VENDOR_UNAUTH_TLS, + EAP_VENDOR_TYPE_UNAUTH_TLS, payload_len, + code, identifier); + return eap_msg_alloc(EAP_VENDOR_IETF, type, payload_len, code, + identifier); +} + + static int eap_tls_check_blob(struct eap_sm *sm, const char **name, const u8 **data, size_t *data_len) { @@ -48,6 +60,10 @@ static void eap_tls_params_flags(struct tls_connection_params *params, params->flags |= TLS_CONN_ALLOW_SIGN_RSA_MD5; if (os_strstr(txt, "tls_disable_time_checks=1")) params->flags |= TLS_CONN_DISABLE_TIME_CHECKS; + if (os_strstr(txt, "tls_disable_session_ticket=1")) + params->flags |= TLS_CONN_DISABLE_SESSION_TICKET; + if (os_strstr(txt, "tls_disable_session_ticket=0")) + params->flags &= ~TLS_CONN_DISABLE_SESSION_TICKET; } @@ -99,6 +115,18 @@ static int eap_tls_params_from_conf(struct eap_sm *sm, struct eap_peer_config *config, int phase2) { os_memset(params, 0, sizeof(*params)); + if (sm->workaround && data->eap_type != EAP_TYPE_FAST) { + /* + * Some deployed authentication servers seem to be unable to + * handle the TLS Session Ticket extension (they are supposed + * to ignore unrecognized TLS extensions, but end up rejecting + * the ClientHello instead). As a workaround, disable use of + * TLS Sesson Ticket extension for EAP-TLS, EAP-PEAP, and + * EAP-TTLS (EAP-FAST uses session ticket, so any server that + * supports EAP-FAST does not need this workaround). + */ + params->flags |= TLS_CONN_DISABLE_SESSION_TICKET; + } if (phase2) { wpa_printf(MSG_DEBUG, "TLS: using phase2 config options"); eap_tls_params_from_conf2(params, config); @@ -182,13 +210,14 @@ static int eap_tls_init_connection(struct eap_sm *sm, * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @data: Data for TLS processing * @config: Pointer to the network configuration + * @eap_type: EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST) * Returns: 0 on success, -1 on failure * * This function is used to initialize shared TLS functionality for EAP-TLS, * EAP-PEAP, EAP-TTLS, and EAP-FAST. */ int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, - struct eap_peer_config *config) + struct eap_peer_config *config, u8 eap_type) { struct tls_connection_params params; @@ -196,6 +225,7 @@ int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, return -1; data->eap = sm; + data->eap_type = eap_type; data->phase2 = sm->init_phase2; data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 : sm->ssl_ctx; @@ -259,7 +289,9 @@ void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data) u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, const char *label, size_t len) { +#ifndef CONFIG_FIPS struct tls_keys keys; +#endif /* CONFIG_FIPS */ u8 *rnd = NULL, *out; out = os_malloc(len); @@ -271,6 +303,7 @@ u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, == 0) return out; +#ifndef CONFIG_FIPS /* * TLS library did not support key generation, so get the needed TLS * session parameters and use an internal implementation of TLS PRF to @@ -299,6 +332,7 @@ u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, return out; fail: +#endif /* CONFIG_FIPS */ os_free(out); os_free(rnd); return NULL; @@ -516,9 +550,8 @@ static int eap_tls_process_output(struct eap_ssl_data *data, EapType eap_type, length_included = 1; } - *out_data = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, - 1 + length_included * 4 + len, - EAP_CODE_RESPONSE, id); + *out_data = eap_tls_msg_alloc(eap_type, 1 + length_included * 4 + len, + EAP_CODE_RESPONSE, id); if (*out_data == NULL) return -1; @@ -656,8 +689,7 @@ struct wpabuf * eap_peer_tls_build_ack(u8 id, EapType eap_type, { struct wpabuf *resp; - resp = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, 1, EAP_CODE_RESPONSE, - id); + resp = eap_tls_msg_alloc(eap_type, 1, EAP_CODE_RESPONSE, id); if (resp == NULL) return NULL; wpa_printf(MSG_DEBUG, "SSL: Building ACK (type=%d id=%d ver=%d)", @@ -750,7 +782,13 @@ const u8 * eap_peer_tls_process_init(struct eap_sm *sm, return NULL; } - pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, reqData, &left); + if (eap_type == EAP_UNAUTH_TLS_TYPE) + pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS, + EAP_VENDOR_TYPE_UNAUTH_TLS, reqData, + &left); + else + pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, reqData, + &left); if (pos == NULL) { ret->ignore = TRUE; return NULL; @@ -946,8 +984,8 @@ int eap_peer_select_phase2_methods(struct eap_peer_config *config, "method '%s'", start); } else { num_methods++; - _methods = os_realloc(methods, - num_methods * sizeof(*methods)); + _methods = os_realloc_array(methods, num_methods, + sizeof(*methods)); if (_methods == NULL) { os_free(methods); os_free(buf); diff --git a/src/eap_peer/eap_tls_common.h b/src/eap_peer/eap_tls_common.h index 771385bc..91d3a25a 100644 --- a/src/eap_peer/eap_tls_common.h +++ b/src/eap_peer/eap_tls_common.h @@ -68,6 +68,11 @@ struct eap_ssl_data { * ssl_ctx - TLS library context to use for the connection */ void *ssl_ctx; + + /** + * eap_type - EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST) + */ + u8 eap_type; }; @@ -80,9 +85,12 @@ struct eap_ssl_data { /* could be up to 128 bytes, but only the first 64 bytes are used */ #define EAP_TLS_KEY_LEN 64 +/* dummy type used as a flag for UNAUTH-TLS */ +#define EAP_UNAUTH_TLS_TYPE 255 + int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, - struct eap_peer_config *config); + struct eap_peer_config *config, u8 eap_type); void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data); u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, const char *label, size_t len); diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c index e09f5e50..9360a424 100644 --- a/src/eap_peer/eap_ttls.c +++ b/src/eap_peer/eap_ttls.c @@ -110,7 +110,7 @@ static void * eap_ttls_init(struct eap_sm *sm) data->phase2_eap_type.method = EAP_TYPE_NONE; } - if (eap_peer_tls_ssl_init(sm, &data->ssl, config)) { + if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_TTLS)) { wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL."); eap_ttls_deinit(sm, data); return NULL; @@ -401,6 +401,7 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, struct eap_method_ret *ret, struct wpabuf **resp) { +#ifdef EAP_MSCHAPv2 struct wpabuf *msg; u8 *buf, *pos, *challenge, *peer_challenge; const u8 *identity, *password; @@ -488,6 +489,10 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, } return 0; +#else /* EAP_MSCHAPv2 */ + wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build"); + return -1; +#endif /* EAP_MSCHAPv2 */ } @@ -1042,6 +1047,7 @@ static int eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, struct eap_method_ret *ret, struct ttls_parse_avp *parse) { +#ifdef EAP_MSCHAPv2 if (parse->mschapv2_error) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Received " "MS-CHAP-Error - failed"); @@ -1090,6 +1096,10 @@ static int eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, * with EAP-Success after this. */ return 1; +#else /* EAP_MSCHAPv2 */ + wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build"); + return -1; +#endif /* EAP_MSCHAPv2 */ } diff --git a/src/eap_peer/eap_vendor_test.c b/src/eap_peer/eap_vendor_test.c index f23b9d2a..040d1e7f 100644 --- a/src/eap_peer/eap_vendor_test.c +++ b/src/eap_peer/eap_vendor_test.c @@ -19,7 +19,7 @@ #endif /* TEST_PENDING_REQUEST */ -#define EAP_VENDOR_ID 0xfffefd +#define EAP_VENDOR_ID EAP_VENDOR_HOSTAP #define EAP_VENDOR_TYPE 0xfcfbfaf9 diff --git a/src/eap_peer/mschapv2.c b/src/eap_peer/mschapv2.c index 67dbadb7..37e6735e 100644 --- a/src/eap_peer/mschapv2.c +++ b/src/eap_peer/mschapv2.c @@ -63,22 +63,28 @@ int mschapv2_derive_response(const u8 *identity, size_t identity_len, if (pwhash) { wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: password hash", password, password_len); - generate_nt_response_pwhash(auth_challenge, peer_challenge, - username, username_len, - password, nt_response); - generate_authenticator_response_pwhash( - password, peer_challenge, auth_challenge, - username, username_len, nt_response, auth_response); + if (generate_nt_response_pwhash(auth_challenge, peer_challenge, + username, username_len, + password, nt_response) || + generate_authenticator_response_pwhash( + password, peer_challenge, auth_challenge, + username, username_len, nt_response, + auth_response)) + return -1; } else { wpa_hexdump_ascii_key(MSG_DEBUG, "MSCHAPV2: password", password, password_len); - generate_nt_response(auth_challenge, peer_challenge, - username, username_len, - password, password_len, nt_response); - generate_authenticator_response(password, password_len, - peer_challenge, auth_challenge, - username, username_len, - nt_response, auth_response); + if (generate_nt_response(auth_challenge, peer_challenge, + username, username_len, + password, password_len, + nt_response) || + generate_authenticator_response(password, password_len, + peer_challenge, + auth_challenge, + username, username_len, + nt_response, + auth_response)) + return -1; } wpa_hexdump(MSG_DEBUG, "MSCHAPV2: NT Response", nt_response, MSCHAPV2_NT_RESPONSE_LEN); @@ -94,7 +100,8 @@ int mschapv2_derive_response(const u8 *identity, size_t identity_len, hash_nt_password_hash(password_hash, password_hash_hash)) return -1; } - get_master_key(password_hash_hash, nt_response, master_key); + if (get_master_key(password_hash_hash, nt_response, master_key)) + return -1; wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: Master Key", master_key, MSCHAPV2_MASTER_KEY_LEN); diff --git a/src/eap_server/eap_methods.h b/src/eap_server/eap_methods.h index 9667cf47..bc810a9b 100644 --- a/src/eap_server/eap_methods.h +++ b/src/eap_server/eap_methods.h @@ -26,6 +26,7 @@ const char * eap_server_get_name(int vendor, EapType type); int eap_server_identity_register(void); int eap_server_md5_register(void); int eap_server_tls_register(void); +int eap_server_unauth_tls_register(void); int eap_server_mschapv2_register(void); int eap_server_peap_register(void); int eap_server_tlv_register(void); diff --git a/src/eap_server/eap_server.c b/src/eap_server/eap_server.c index 44c089fa..15f7e228 100644 --- a/src/eap_server/eap_server.c +++ b/src/eap_server/eap_server.c @@ -275,6 +275,11 @@ SM_STATE(EAP, INTEGRITY_CHECK) { SM_ENTRY(EAP, INTEGRITY_CHECK); + if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1)) { + sm->ignore = TRUE; + return; + } + if (sm->m->check) { sm->ignore = sm->m->check(sm, sm->eap_method_priv, sm->eap_if.eapRespData); @@ -309,6 +314,9 @@ SM_STATE(EAP, METHOD_RESPONSE) { SM_ENTRY(EAP, METHOD_RESPONSE); + if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1)) + return; + sm->m->process(sm, sm->eap_method_priv, sm->eap_if.eapRespData); if (sm->m->isDone(sm, sm->eap_method_priv)) { eap_sm_Policy_update(sm, NULL, 0); @@ -380,6 +388,9 @@ SM_STATE(EAP, NAK) } sm->m = NULL; + if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1)) + return; + nak = wpabuf_head(sm->eap_if.eapRespData); if (nak && wpabuf_len(sm->eap_if.eapRespData) > sizeof(*nak)) { len = be_to_host16(nak->length); diff --git a/src/eap_server/eap_server_md5.c b/src/eap_server/eap_server_md5.c index f4bb2610..5a5e2907 100644 --- a/src/eap_server/eap_server_md5.c +++ b/src/eap_server/eap_server_md5.c @@ -1,6 +1,6 @@ /* * hostapd / EAP-MD5 server - * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -119,8 +119,12 @@ static void eap_md5_process(struct eap_sm *sm, void *priv, wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Response", pos, CHAP_MD5_LEN); id = eap_get_id(respData); - chap_md5(id, sm->user->password, sm->user->password_len, - data->challenge, CHALLENGE_LEN, hash); + if (chap_md5(id, sm->user->password, sm->user->password_len, + data->challenge, CHALLENGE_LEN, hash)) { + wpa_printf(MSG_INFO, "EAP-MD5: CHAP MD5 operation failed"); + data->state = FAILURE; + return; + } if (os_memcmp(hash, pos, CHAP_MD5_LEN) == 0) { wpa_printf(MSG_DEBUG, "EAP-MD5: Done - Success"); diff --git a/src/eap_server/eap_server_tls.c b/src/eap_server/eap_server_tls.c index d34ef41c..447f47cf 100644 --- a/src/eap_server/eap_server_tls.c +++ b/src/eap_server/eap_server_tls.c @@ -21,6 +21,7 @@ struct eap_tls_data { struct eap_ssl_data ssl; enum { START, CONTINUE, SUCCESS, FAILURE } state; int established; + u8 eap_type; }; @@ -65,8 +66,32 @@ static void * eap_tls_init(struct eap_sm *sm) return NULL; } + data->eap_type = EAP_TYPE_TLS; + + return data; +} + + +#ifdef EAP_SERVER_UNAUTH_TLS +static void * eap_unauth_tls_init(struct eap_sm *sm) +{ + struct eap_tls_data *data; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = START; + + if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) { + wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); + eap_tls_reset(sm, data); + return NULL; + } + + data->eap_type = EAP_UNAUTH_TLS_TYPE; return data; } +#endif /* EAP_SERVER_UNAUTH_TLS */ static void eap_tls_reset(struct eap_sm *sm, void *priv) @@ -84,8 +109,7 @@ static struct wpabuf * eap_tls_build_start(struct eap_sm *sm, { struct wpabuf *req; - req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLS, 1, EAP_CODE_REQUEST, - id); + req = eap_tls_msg_alloc(data->eap_type, 1, EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-TLS: Failed to allocate memory for " "request"); @@ -107,11 +131,11 @@ static struct wpabuf * eap_tls_buildReq(struct eap_sm *sm, void *priv, u8 id) struct wpabuf *res; if (data->ssl.state == FRAG_ACK) { - return eap_server_tls_build_ack(id, EAP_TYPE_TLS, 0); + return eap_server_tls_build_ack(id, data->eap_type, 0); } if (data->ssl.state == WAIT_FRAG_ACK) { - res = eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TLS, 0, + res = eap_server_tls_build_msg(&data->ssl, data->eap_type, 0, id); goto check_established; } @@ -129,7 +153,7 @@ static struct wpabuf * eap_tls_buildReq(struct eap_sm *sm, void *priv, u8 id) return NULL; } - res = eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TLS, 0, id); + res = eap_server_tls_build_msg(&data->ssl, data->eap_type, 0, id); check_established: if (data->established && data->ssl.state != WAIT_FRAG_ACK) { @@ -146,10 +170,17 @@ check_established: static Boolean eap_tls_check(struct eap_sm *sm, void *priv, struct wpabuf *respData) { + struct eap_tls_data *data = priv; const u8 *pos; size_t len; - pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLS, respData, &len); + if (data->eap_type == EAP_UNAUTH_TLS_TYPE) + pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS, + EAP_VENDOR_TYPE_UNAUTH_TLS, respData, + &len); + else + pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_type, + respData, &len); if (pos == NULL || len < 1) { wpa_printf(MSG_INFO, "EAP-TLS: Invalid frame"); return TRUE; @@ -178,7 +209,7 @@ static void eap_tls_process(struct eap_sm *sm, void *priv, { struct eap_tls_data *data = priv; if (eap_server_tls_process(sm, &data->ssl, respData, data, - EAP_TYPE_TLS, NULL, eap_tls_process_msg) < + data->eap_type, NULL, eap_tls_process_msg) < 0) eap_tls_state(data, FAILURE); } @@ -278,3 +309,34 @@ int eap_server_tls_register(void) eap_server_method_free(eap); return ret; } + + +#ifdef EAP_SERVER_UNAUTH_TLS +int eap_server_unauth_tls_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_UNAUTH_TLS, + EAP_VENDOR_TYPE_UNAUTH_TLS, + "UNAUTH-TLS"); + if (eap == NULL) + return -1; + + eap->init = eap_unauth_tls_init; + eap->reset = eap_tls_reset; + eap->buildReq = eap_tls_buildReq; + eap->check = eap_tls_check; + eap->process = eap_tls_process; + eap->isDone = eap_tls_isDone; + eap->getKey = eap_tls_getKey; + eap->isSuccess = eap_tls_isSuccess; + eap->get_emsk = eap_tls_get_emsk; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} +#endif /* EAP_SERVER_UNAUTH_TLS */ diff --git a/src/eap_server/eap_server_tls_common.c b/src/eap_server/eap_server_tls_common.c index 88a410de..31be2ecb 100644 --- a/src/eap_server/eap_server_tls_common.c +++ b/src/eap_server/eap_server_tls_common.c @@ -18,6 +18,18 @@ static void eap_server_tls_free_in_buf(struct eap_ssl_data *data); +struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len, + u8 code, u8 identifier) +{ + if (type == EAP_UNAUTH_TLS_TYPE) + return eap_msg_alloc(EAP_VENDOR_UNAUTH_TLS, + EAP_VENDOR_TYPE_UNAUTH_TLS, payload_len, + code, identifier); + return eap_msg_alloc(EAP_VENDOR_IETF, type, payload_len, code, + identifier); +} + + int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, int verify_peer) { @@ -131,8 +143,7 @@ struct wpabuf * eap_server_tls_build_msg(struct eap_ssl_data *data, if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) plen += 4; - req = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, plen, - EAP_CODE_REQUEST, id); + req = eap_tls_msg_alloc(eap_type, plen, EAP_CODE_REQUEST, id); if (req == NULL) return NULL; @@ -168,8 +179,7 @@ struct wpabuf * eap_server_tls_build_ack(u8 id, int eap_type, int version) { struct wpabuf *req; - req = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, 1, EAP_CODE_REQUEST, - id); + req = eap_tls_msg_alloc(eap_type, 1, EAP_CODE_REQUEST, id); if (req == NULL) return NULL; wpa_printf(MSG_DEBUG, "SSL: Building ACK"); @@ -359,7 +369,13 @@ int eap_server_tls_process(struct eap_sm *sm, struct eap_ssl_data *data, size_t left; int ret, res = 0; - pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, respData, &left); + if (eap_type == EAP_UNAUTH_TLS_TYPE) + pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS, + EAP_VENDOR_TYPE_UNAUTH_TLS, respData, + &left); + else + pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, respData, + &left); if (pos == NULL || left < 1) return 0; /* Should not happen - frame already validated */ flags = *pos++; diff --git a/src/eap_server/eap_server_tnc.c b/src/eap_server/eap_server_tnc.c index 3d2403e1..67a3dfa3 100644 --- a/src/eap_server/eap_server_tnc.c +++ b/src/eap_server/eap_server_tnc.c @@ -9,7 +9,6 @@ #include "includes.h" #include "common.h" -#include "base64.h" #include "eap_i.h" #include "tncs.h" diff --git a/src/eap_server/eap_server_ttls.c b/src/eap_server/eap_server_ttls.c index 45fbf52d..647bd2fa 100644 --- a/src/eap_server/eap_server_ttls.c +++ b/src/eap_server/eap_server_ttls.c @@ -674,6 +674,13 @@ static void eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, return; } + if (sm->identity == NULL) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: No user identity " + "known"); + eap_ttls_state(data, FAILURE); + return; + } + /* MSCHAPv2 does not include optional domain name in the * challenge-response calculation, so remove domain prefix * (if present). */ @@ -979,11 +986,12 @@ static void eap_ttls_process_phase2(struct eap_sm *sm, if (parse.user_name) { os_free(sm->identity); sm->identity = os_malloc(parse.user_name_len); - if (sm->identity) { - os_memcpy(sm->identity, parse.user_name, - parse.user_name_len); - sm->identity_len = parse.user_name_len; + if (sm->identity == NULL) { + eap_ttls_state(data, FAILURE); + goto done; } + os_memcpy(sm->identity, parse.user_name, parse.user_name_len); + sm->identity_len = parse.user_name_len; if (eap_user_get(sm, parse.user_name, parse.user_name_len, 1) != 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase2 Identity not " diff --git a/src/eap_server/eap_server_vendor_test.c b/src/eap_server/eap_server_vendor_test.c index e4c8bc3d..30f600d3 100644 --- a/src/eap_server/eap_server_vendor_test.c +++ b/src/eap_server/eap_server_vendor_test.c @@ -12,7 +12,7 @@ #include "eap_i.h" -#define EAP_VENDOR_ID 0xfffefd +#define EAP_VENDOR_ID EAP_VENDOR_HOSTAP #define EAP_VENDOR_TYPE 0xfcfbfaf9 diff --git a/src/eap_server/eap_sim_db.c b/src/eap_server/eap_sim_db.c index ce3238c9..68fb1f0a 100644 --- a/src/eap_server/eap_sim_db.c +++ b/src/eap_server/eap_sim_db.c @@ -340,6 +340,7 @@ static int eap_sim_db_open_socket(struct eap_sim_db_data *data) addr.sun_family = AF_UNIX; os_snprintf(addr.sun_path, sizeof(addr.sun_path), "/tmp/eap_sim_db_%d-%d", getpid(), counter++); + os_free(data->local_sock); data->local_sock = os_strdup(addr.sun_path); if (bind(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror("bind(eap_sim_db)"); @@ -407,8 +408,11 @@ void * eap_sim_db_init(const char *config, goto fail; if (os_strncmp(data->fname, "unix:", 5) == 0) { - if (eap_sim_db_open_socket(data)) - goto fail; + if (eap_sim_db_open_socket(data)) { + wpa_printf(MSG_DEBUG, "EAP-SIM DB: External database " + "connection not available - will retry " + "later"); + } } return data; diff --git a/src/eap_server/eap_tls_common.h b/src/eap_server/eap_tls_common.h index 31fe503f..11f58275 100644 --- a/src/eap_server/eap_tls_common.h +++ b/src/eap_server/eap_tls_common.h @@ -62,7 +62,12 @@ struct eap_ssl_data { /* could be up to 128 bytes, but only the first 64 bytes are used */ #define EAP_TLS_KEY_LEN 64 +/* dummy type used as a flag for UNAUTH-TLS */ +#define EAP_UNAUTH_TLS_TYPE 255 + +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); void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data); diff --git a/src/eapol_auth/eapol_auth_sm.c b/src/eapol_auth/eapol_auth_sm.c index 8d6d18fc..c3ccb46b 100644 --- a/src/eapol_auth/eapol_auth_sm.c +++ b/src/eapol_auth/eapol_auth_sm.c @@ -757,7 +757,8 @@ SM_STEP(CTRL_DIR) struct eapol_state_machine * eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, int flags, const struct wpabuf *assoc_wps_ie, - const struct wpabuf *assoc_p2p_ie, void *sta_ctx) + const struct wpabuf *assoc_p2p_ie, void *sta_ctx, + const char *identity, const char *radius_cui) { struct eapol_state_machine *sm; struct eap_config eap_conf; @@ -838,6 +839,15 @@ eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, eapol_auth_initialize(sm); + if (identity) { + sm->identity = (u8 *) os_strdup(identity); + if (sm->identity) + sm->identity_len = os_strlen(identity); + } + if (radius_cui) + sm->radius_cui = wpabuf_alloc_copy(radius_cui, + os_strlen(radius_cui)); + return sm; } diff --git a/src/eapol_auth/eapol_auth_sm.h b/src/eapol_auth/eapol_auth_sm.h index 5e8ff43d..b50bbdd0 100644 --- a/src/eapol_auth/eapol_auth_sm.h +++ b/src/eapol_auth/eapol_auth_sm.h @@ -77,7 +77,8 @@ void eapol_auth_deinit(struct eapol_authenticator *eapol); struct eapol_state_machine * eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, int flags, const struct wpabuf *assoc_wps_ie, - const struct wpabuf *assoc_p2p_ie, void *sta_ctx); + const struct wpabuf *assoc_p2p_ie, void *sta_ctx, + const char *identity, const char *radius_cui); void eapol_auth_free(struct eapol_state_machine *sm); void eapol_auth_step(struct eapol_state_machine *sm); void eapol_auth_dump_state(FILE *f, const char *prefix, diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c index f0cae700..e3bfa38a 100644 --- a/src/eapol_supp/eapol_supp_sm.c +++ b/src/eapol_supp/eapol_supp_sm.c @@ -1,6 +1,6 @@ /* * EAPOL supplicant state machines - * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -139,46 +139,6 @@ struct eapol_sm { }; -#define IEEE8021X_REPLAY_COUNTER_LEN 8 -#define IEEE8021X_KEY_SIGN_LEN 16 -#define IEEE8021X_KEY_IV_LEN 16 - -#define IEEE8021X_KEY_INDEX_FLAG 0x80 -#define IEEE8021X_KEY_INDEX_MASK 0x03 - -#ifdef _MSC_VER -#pragma pack(push, 1) -#endif /* _MSC_VER */ - -struct ieee802_1x_eapol_key { - u8 type; - /* Note: key_length is unaligned */ - u8 key_length[2]; - /* does not repeat within the life of the keying material used to - * encrypt the Key field; 64-bit NTP timestamp MAY be used here */ - u8 replay_counter[IEEE8021X_REPLAY_COUNTER_LEN]; - u8 key_iv[IEEE8021X_KEY_IV_LEN]; /* cryptographically random number */ - u8 key_index; /* key flag in the most significant bit: - * 0 = broadcast (default key), - * 1 = unicast (key mapping key); key index is in the - * 7 least significant bits */ - /* HMAC-MD5 message integrity check computed with MS-MPPE-Send-Key as - * the key */ - u8 key_signature[IEEE8021X_KEY_SIGN_LEN]; - - /* followed by key: if packet body length = 44 + key length, then the - * key field (of key_length bytes) contains the key in encrypted form; - * if packet body length = 44, key field is absent and key_length - * represents the number of least significant octets from - * MS-MPPE-Send-Key attribute to be used as the keying material; - * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */ -} STRUCT_PACKED; - -#ifdef _MSC_VER -#pragma pack(pop) -#endif /* _MSC_VER */ - - static void eapol_sm_txLogoff(struct eapol_sm *sm); static void eapol_sm_txStart(struct eapol_sm *sm); static void eapol_sm_processKey(struct eapol_sm *sm); @@ -664,6 +624,7 @@ struct eap_key_data { static void eapol_sm_processKey(struct eapol_sm *sm) { +#ifndef CONFIG_FIPS struct ieee802_1x_hdr *hdr; struct ieee802_1x_eapol_key *key; struct eap_key_data keydata; @@ -671,6 +632,7 @@ static void eapol_sm_processKey(struct eapol_sm *sm) u8 ekey[IEEE8021X_KEY_IV_LEN + IEEE8021X_ENCR_KEY_LEN]; int key_len, res, sign_key_len, encr_key_len; u16 rx_key_length; + size_t plen; wpa_printf(MSG_DEBUG, "EAPOL: processKey"); if (sm->last_rx_key == NULL) @@ -683,9 +645,12 @@ static void eapol_sm_processKey(struct eapol_sm *sm) return; } + if (sm->last_rx_key_len < sizeof(*hdr) + sizeof(*key)) + return; hdr = (struct ieee802_1x_hdr *) sm->last_rx_key; key = (struct ieee802_1x_eapol_key *) (hdr + 1); - if (sizeof(*hdr) + be_to_host16(hdr->length) > sm->last_rx_key_len) { + plen = be_to_host16(hdr->length); + if (sizeof(*hdr) + plen > sm->last_rx_key_len || plen < sizeof(*key)) { wpa_printf(MSG_WARNING, "EAPOL: Too short EAPOL-Key frame"); return; } @@ -751,7 +716,7 @@ static void eapol_sm_processKey(struct eapol_sm *sm) } wpa_printf(MSG_DEBUG, "EAPOL: EAPOL-Key key signature verified"); - key_len = be_to_host16(hdr->length) - sizeof(*key); + key_len = plen - sizeof(*key); if (key_len > 32 || rx_key_length > 32) { wpa_printf(MSG_WARNING, "EAPOL: Too long key data length %d", key_len ? key_len : rx_key_length); @@ -822,6 +787,7 @@ static void eapol_sm_processKey(struct eapol_sm *sm) sm->ctx->eapol_done_cb(sm->ctx->ctx); } } +#endif /* CONFIG_FIPS */ } @@ -1778,7 +1744,8 @@ static void eapol_sm_set_int(void *ctx, enum eapol_int_var variable, switch (variable) { case EAPOL_idleWhile: sm->idleWhile = value; - eapol_enable_timer_tick(sm); + if (sm->idleWhile > 0) + eapol_enable_timer_tick(sm); break; } } @@ -1946,3 +1913,19 @@ void eapol_sm_deinit(struct eapol_sm *sm) os_free(sm->ctx); os_free(sm); } + + +void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm, + struct ext_password_data *ext) +{ + if (sm && sm->eap) + eap_sm_set_ext_pw_ctx(sm->eap, ext); +} + + +int eapol_sm_failed(struct eapol_sm *sm) +{ + if (sm == NULL) + return 0; + return !sm->eapSuccess && sm->eapFail; +} diff --git a/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h index 1a20e4b9..d2a4b94a 100644 --- a/src/eapol_supp/eapol_supp_sm.h +++ b/src/eapol_supp/eapol_supp_sm.h @@ -1,6 +1,6 @@ /* * EAPOL supplicant state machines - * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -243,6 +243,7 @@ struct eapol_ctx { struct eap_peer_config; +struct ext_password_data; #ifdef IEEE8021X_EAPOL struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx); @@ -275,6 +276,9 @@ void eapol_sm_request_reauth(struct eapol_sm *sm); void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm, int in_eapol_sm); void eapol_sm_invalidate_cached_session(struct eapol_sm *sm); const char * eapol_sm_get_method_name(struct eapol_sm *sm); +void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm, + struct ext_password_data *ext); +int eapol_sm_failed(struct eapol_sm *sm); #else /* IEEE8021X_EAPOL */ static inline struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx) { @@ -366,6 +370,14 @@ static inline const char * eapol_sm_get_method_name(struct eapol_sm *sm) { return NULL; } +static inline void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm, + struct ext_password_data *ext) +{ +} +static inline int eapol_sm_failed(struct eapol_sm *sm) +{ + return 0; +} #endif /* IEEE8021X_EAPOL */ #endif /* EAPOL_SUPP_SM_H */ diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c index eb2821ea..03380dc6 100644 --- a/src/p2p/p2p.c +++ b/src/p2p/p2p.c @@ -172,6 +172,8 @@ static const char * p2p_state_txt(int state) return "INVITE_LISTEN"; case P2P_SEARCH_WHEN_READY: return "SEARCH_WHEN_READY"; + case P2P_CONTINUE_SEARCH_WHEN_READY: + return "CONTINUE_SEARCH_WHEN_READY"; default: return "?"; } @@ -725,6 +727,11 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, int level, break; } + if (msg.wfd_subelems) { + wpabuf_free(dev->info.wfd_subelems); + dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); + } + if (scan_res) { p2p_add_group_clients(p2p, p2p_dev_addr, addr, freq, msg.group_info, msg.group_info_len); @@ -782,6 +789,8 @@ static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev) dev->info.wps_vendor_ext[i] = NULL; } + wpabuf_free(dev->info.wfd_subelems); + os_free(dev); } @@ -846,6 +855,7 @@ static void p2p_search(struct p2p_data *p2p) int freq = 0; enum p2p_scan_type type; u16 pw_id = DEV_PW_DEFAULT; + int res; if (p2p->drv_in_listen) { wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Driver is still " @@ -865,12 +875,18 @@ static void p2p_search(struct p2p_data *p2p) wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search"); } - if (p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, type, freq, - p2p->num_req_dev_types, p2p->req_dev_types, - p2p->find_dev_id, pw_id)) { + res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, type, freq, + p2p->num_req_dev_types, p2p->req_dev_types, + p2p->find_dev_id, pw_id); + if (res < 0) { wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Scan request failed"); p2p_continue_find(p2p); + } else if (res == 1) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Could not start " + "p2p_scan at this point - will try again after " + "previous scan completes"); + p2p_set_state(p2p, P2P_CONTINUE_SEARCH_WHEN_READY); } else { wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Running p2p_scan"); p2p->p2p_scan_running = 1; @@ -975,7 +991,7 @@ static void p2p_free_req_dev_types(struct p2p_data *p2p) int p2p_find(struct p2p_data *p2p, unsigned int timeout, enum p2p_discovery_type type, unsigned int num_req_dev_types, const u8 *req_dev_types, - const u8 *dev_id) + const u8 *dev_id, unsigned int search_delay) { int res; @@ -1009,6 +1025,8 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, p2p->find_type = type; p2p_device_clear_reported(p2p); p2p_set_state(p2p, P2P_SEARCH); + p2p->search_delay = search_delay; + p2p->in_search_delay = 0; eloop_cancel_timeout(p2p_find_timeout, p2p, NULL); p2p->last_p2p_find_timeout = timeout; if (timeout) @@ -1070,13 +1088,18 @@ int p2p_search_pending(struct p2p_data *p2p) int p2p_other_scan_completed(struct p2p_data *p2p) { + if (p2p->state == P2P_CONTINUE_SEARCH_WHEN_READY) { + p2p_set_state(p2p, P2P_SEARCH); + p2p_search(p2p); + return 1; + } if (p2p->state != P2P_SEARCH_WHEN_READY) return 0; wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting pending P2P find " "now that previous scan was completed"); if (p2p_find(p2p, p2p->last_p2p_find_timeout, p2p->find_type, p2p->num_req_dev_types, p2p->req_dev_types, - p2p->find_dev_id) < 0) + p2p->find_dev_id, p2p->search_delay) < 0) return 0; return 1; } @@ -1442,6 +1465,11 @@ void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr, } } + if (msg->wfd_subelems) { + wpabuf_free(dev->info.wfd_subelems); + dev->info.wfd_subelems = wpabuf_dup(msg->wfd_subelems); + } + if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) { dev->flags &= ~P2P_DEV_PROBE_REQ_ONLY; wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, @@ -1779,6 +1807,11 @@ static void p2p_add_dev_from_probe_req(struct p2p_data *p2p, const u8 *addr, p2p_copy_wps_info(dev, 1, &msg); + if (msg.wfd_subelems) { + wpabuf_free(dev->info.wfd_subelems); + dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); + } + p2p_parse_free(&msg); wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, @@ -1877,8 +1910,14 @@ struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p) struct wpabuf *buf; u8 *len; int pw_id = -1; + size_t extra = 0; + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_probe_resp) + extra = wpabuf_len(p2p->wfd_ie_probe_resp); +#endif /* CONFIG_WIFI_DISPLAY */ - buf = wpabuf_alloc(1000); + buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; @@ -1889,6 +1928,11 @@ struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p) p2p_build_wps_ie(p2p, buf, pw_id, 1); +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_probe_resp) + wpabuf_put_buf(buf, p2p->wfd_ie_probe_resp); +#endif /* CONFIG_WIFI_DISPLAY */ + /* P2P IE */ len = p2p_buf_add_ie_hdr(buf); p2p_buf_add_capability(buf, p2p->dev_capab & @@ -2173,20 +2217,31 @@ int p2p_assoc_req_ie(struct p2p_data *p2p, const u8 *bssid, u8 *buf, struct p2p_device *peer; size_t tmplen; int res; + size_t extra = 0; if (!p2p_group) return p2p_assoc_req_ie_wlan_ap(p2p, bssid, buf, len, p2p_ie); +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_assoc_req) + extra = wpabuf_len(p2p->wfd_ie_assoc_req); +#endif /* CONFIG_WIFI_DISPLAY */ + /* * (Re)Association Request - P2P IE * P2P Capability attribute (shall be present) * Extended Listen Timing (may be present) * P2P Device Info attribute (shall be present) */ - tmp = wpabuf_alloc(200); + tmp = wpabuf_alloc(200 + extra); if (tmp == NULL) return -1; +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_assoc_req) + wpabuf_put_buf(tmp, p2p->wfd_ie_assoc_req); +#endif /* CONFIG_WIFI_DISPLAY */ + peer = bssid ? p2p_get_device(p2p, bssid) : NULL; lpos = p2p_buf_add_ie_hdr(tmp); @@ -2370,12 +2425,29 @@ struct p2p_data * p2p_init(const struct p2p_config *cfg) eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0, p2p_expiration_timeout, p2p, NULL); + p2p->go_timeout = 100; + p2p->client_timeout = 20; + return p2p; } void p2p_deinit(struct p2p_data *p2p) { +#ifdef CONFIG_WIFI_DISPLAY + wpabuf_free(p2p->wfd_ie_beacon); + wpabuf_free(p2p->wfd_ie_probe_req); + wpabuf_free(p2p->wfd_ie_probe_resp); + wpabuf_free(p2p->wfd_ie_assoc_req); + wpabuf_free(p2p->wfd_ie_invitation); + wpabuf_free(p2p->wfd_ie_prov_disc_req); + wpabuf_free(p2p->wfd_ie_prov_disc_resp); + wpabuf_free(p2p->wfd_ie_go_neg); + wpabuf_free(p2p->wfd_dev_info); + wpabuf_free(p2p->wfd_assoc_bssid); + 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); @@ -2723,7 +2795,13 @@ static void p2p_prov_disc_cb(struct p2p_data *p2p, int success) if (!success) { p2p->pending_action_state = P2P_NO_PENDING_ACTION; - if (p2p->state != P2P_IDLE) + if (p2p->user_initiated_pd && + (p2p->state == P2P_SEARCH || p2p->state == P2P_LISTEN_ONLY)) + { + /* Retry request from timeout to avoid busy loops */ + p2p->pending_action_state = P2P_PENDING_PD; + p2p_set_timeout(p2p, 0, 50000); + } else if (p2p->state != P2P_IDLE) p2p_continue_find(p2p); else if (p2p->user_initiated_pd) { p2p->pending_action_state = P2P_PENDING_PD; @@ -2775,7 +2853,14 @@ void p2p_scan_res_handled(struct p2p_data *p2p) void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id) { - u8 *len = p2p_buf_add_ie_hdr(ies); + u8 *len; + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_probe_req) + wpabuf_put_buf(ies, p2p->wfd_ie_probe_req); +#endif /* CONFIG_WIFI_DISPLAY */ + + len = p2p_buf_add_ie_hdr(ies); p2p_buf_add_capability(ies, p2p->dev_capab & ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0); if (dev_id) @@ -2794,7 +2879,14 @@ void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id) size_t p2p_scan_ie_buf_len(struct p2p_data *p2p) { - return 100; + size_t len = 100; + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p && p2p->wfd_ie_probe_req) + len += wpabuf_len(p2p->wfd_ie_probe_req); +#endif /* CONFIG_WIFI_DISPLAY */ + + return len; } @@ -2845,7 +2937,7 @@ static void p2p_go_neg_req_cb(struct p2p_data *p2p, int success) * channel. */ p2p_set_state(p2p, P2P_CONNECT); - p2p_set_timeout(p2p, 0, 100000); + p2p_set_timeout(p2p, 0, success ? 200000 : 100000); } @@ -2861,7 +2953,7 @@ static void p2p_go_neg_resp_cb(struct p2p_data *p2p, int success) return; } p2p_set_state(p2p, P2P_CONNECT); - p2p_set_timeout(p2p, 0, 100000); + p2p_set_timeout(p2p, 0, 250000); } @@ -3056,6 +3148,14 @@ int p2p_listen_end(struct p2p_data *p2p, unsigned int freq) p2p_set_timeout(p2p, 0, 100000); return 1; } + if (p2p->search_delay) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Delay " + "search operation by %u ms", + p2p->search_delay); + p2p_set_timeout(p2p, p2p->search_delay / 1000, + (p2p->search_delay % 1000) * 1000); + return 1; + } p2p_search(p2p); return 1; } @@ -3250,6 +3350,16 @@ static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx) /* Check if we timed out waiting for PD req */ if (p2p->pending_action_state == P2P_PENDING_PD) p2p_timeout_prov_disc_req(p2p); + if (p2p->search_delay && !p2p->in_search_delay) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Delay " + "search operation by %u ms", + p2p->search_delay); + p2p->in_search_delay = 1; + p2p_set_timeout(p2p, p2p->search_delay / 1000, + (p2p->search_delay % 1000) * 1000); + break; + } + p2p->in_search_delay = 0; p2p_search(p2p); break; case P2P_CONNECT: @@ -3295,6 +3405,8 @@ static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx) break; case P2P_SEARCH_WHEN_READY: break; + case P2P_CONTINUE_SEARCH_WHEN_READY: + break; } } @@ -3478,6 +3590,24 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info, pos += res; } +#ifdef CONFIG_WIFI_DISPLAY + if (dev->info.wfd_subelems) { + res = os_snprintf(pos, end - pos, "wfd_subelems="); + if (res < 0 || res >= end - pos) + return pos - buf; + pos += res; + + pos += wpa_snprintf_hex(pos, end - pos, + wpabuf_head(dev->info.wfd_subelems), + wpabuf_len(dev->info.wfd_subelems)); + + res = os_snprintf(pos, end - pos, "\n"); + if (res < 0 || res >= end - pos) + return pos - buf; + pos += res; + } +#endif /* CONFIG_WIFI_DISPLAY */ + return pos - buf; } @@ -4085,5 +4215,150 @@ int p2p_in_progress(struct p2p_data *p2p) { if (p2p == NULL) return 0; + if (p2p->state == P2P_SEARCH || p2p->state == P2P_SEARCH_WHEN_READY || + p2p->state == P2P_CONTINUE_SEARCH_WHEN_READY) + return 2; return p2p->state != P2P_IDLE && p2p->state != P2P_PROVISIONING; } + + +void p2p_set_config_timeout(struct p2p_data *p2p, u8 go_timeout, + u8 client_timeout) +{ + if (p2p) { + p2p->go_timeout = go_timeout; + p2p->client_timeout = client_timeout; + } +} + + +void p2p_increase_search_delay(struct p2p_data *p2p, unsigned int delay) +{ + if (p2p && p2p->search_delay < delay) + p2p->search_delay = delay; +} + + +#ifdef CONFIG_WIFI_DISPLAY + +static void p2p_update_wfd_ie_groups(struct p2p_data *p2p) +{ + size_t g; + struct p2p_group *group; + + for (g = 0; g < p2p->num_groups; g++) { + group = p2p->groups[g]; + p2p_group_update_ies(group); + } +} + + +int p2p_set_wfd_ie_beacon(struct p2p_data *p2p, struct wpabuf *ie) +{ + wpabuf_free(p2p->wfd_ie_beacon); + p2p->wfd_ie_beacon = ie; + p2p_update_wfd_ie_groups(p2p); + return 0; +} + + +int p2p_set_wfd_ie_probe_req(struct p2p_data *p2p, struct wpabuf *ie) +{ + wpabuf_free(p2p->wfd_ie_probe_req); + p2p->wfd_ie_probe_req = ie; + return 0; +} + + +int p2p_set_wfd_ie_probe_resp(struct p2p_data *p2p, struct wpabuf *ie) +{ + wpabuf_free(p2p->wfd_ie_probe_resp); + p2p->wfd_ie_probe_resp = ie; + p2p_update_wfd_ie_groups(p2p); + return 0; +} + + +int p2p_set_wfd_ie_assoc_req(struct p2p_data *p2p, struct wpabuf *ie) +{ + wpabuf_free(p2p->wfd_ie_assoc_req); + p2p->wfd_ie_assoc_req = ie; + return 0; +} + + +int p2p_set_wfd_ie_invitation(struct p2p_data *p2p, struct wpabuf *ie) +{ + wpabuf_free(p2p->wfd_ie_invitation); + p2p->wfd_ie_invitation = ie; + return 0; +} + + +int p2p_set_wfd_ie_prov_disc_req(struct p2p_data *p2p, struct wpabuf *ie) +{ + wpabuf_free(p2p->wfd_ie_prov_disc_req); + p2p->wfd_ie_prov_disc_req = ie; + return 0; +} + + +int p2p_set_wfd_ie_prov_disc_resp(struct p2p_data *p2p, struct wpabuf *ie) +{ + wpabuf_free(p2p->wfd_ie_prov_disc_resp); + p2p->wfd_ie_prov_disc_resp = ie; + return 0; +} + + +int p2p_set_wfd_ie_go_neg(struct p2p_data *p2p, struct wpabuf *ie) +{ + wpabuf_free(p2p->wfd_ie_go_neg); + p2p->wfd_ie_go_neg = ie; + return 0; +} + + +int p2p_set_wfd_dev_info(struct p2p_data *p2p, const struct wpabuf *elem) +{ + wpabuf_free(p2p->wfd_dev_info); + if (elem) { + p2p->wfd_dev_info = wpabuf_dup(elem); + if (p2p->wfd_dev_info == NULL) + return -1; + } else + p2p->wfd_dev_info = NULL; + + return 0; +} + + +int p2p_set_wfd_assoc_bssid(struct p2p_data *p2p, const struct wpabuf *elem) +{ + wpabuf_free(p2p->wfd_assoc_bssid); + if (elem) { + p2p->wfd_assoc_bssid = wpabuf_dup(elem); + if (p2p->wfd_assoc_bssid == NULL) + return -1; + } else + p2p->wfd_assoc_bssid = NULL; + + return 0; +} + + +int p2p_set_wfd_coupled_sink_info(struct p2p_data *p2p, + const struct wpabuf *elem) +{ + wpabuf_free(p2p->wfd_coupled_sink_info); + if (elem) { + p2p->wfd_coupled_sink_info = wpabuf_dup(elem); + if (p2p->wfd_coupled_sink_info == NULL) + return -1; + } else + p2p->wfd_coupled_sink_info = NULL; + + return 0; +} + +#endif /* CONFIG_WIFI_DISPLAY */ diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h index 59da4527..b80f8984 100644 --- a/src/p2p/p2p.h +++ b/src/p2p/p2p.h @@ -79,6 +79,8 @@ struct p2p_go_neg_results { */ int freq; + int ht40; + /** * ssid - SSID of the group */ @@ -214,6 +216,11 @@ struct p2p_peer_info { size_t wps_sec_dev_type_list_len; struct wpabuf *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT]; + + /** + * wfd_subelems - Wi-Fi Display subelements from WFD IE(s) + */ + struct wpabuf *wfd_subelems; }; enum p2p_prov_disc_status { @@ -841,12 +848,13 @@ enum p2p_discovery_type { * containing num_req_dev_types * WPS_DEV_TYPE_LEN bytes; %NULL if no * requested device types. * @dev_id: Device ID to search for or %NULL to find all devices + * @search_delay: Extra delay in milliseconds between search iterations * Returns: 0 on success, -1 on failure */ int p2p_find(struct p2p_data *p2p, unsigned int timeout, enum p2p_discovery_type type, unsigned int num_req_dev_types, const u8 *req_dev_types, - const u8 *dev_id); + const u8 *dev_id, unsigned int search_delay); /** * p2p_stop_find - Stop P2P Find (Device Discovery) @@ -969,6 +977,11 @@ int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr, void * p2p_sd_request(struct p2p_data *p2p, const u8 *dst, const struct wpabuf *tlvs); +#ifdef CONFIG_WIFI_DISPLAY +void * p2p_sd_request_wfd(struct p2p_data *p2p, const u8 *dst, + const struct wpabuf *tlvs); +#endif /* CONFIG_WIFI_DISPLAY */ + /** * p2p_sd_cancel_request - Cancel a pending service discovery query * @p2p: P2P module context from p2p_init() @@ -1729,4 +1742,29 @@ int p2p_other_scan_completed(struct p2p_data *p2p); const char * p2p_wps_method_text(enum p2p_wps_method method); +/** + * p2p_set_config_timeout - Set local config timeouts + * @p2p: P2P module context from p2p_init() + * @go_timeout: Time in 10 ms units it takes to start the GO mode + * @client_timeout: Time in 10 ms units it takes to start the client mode + */ +void p2p_set_config_timeout(struct p2p_data *p2p, u8 go_timeout, + u8 client_timeout); + +void p2p_increase_search_delay(struct p2p_data *p2p, unsigned int delay); + +int p2p_set_wfd_ie_beacon(struct p2p_data *p2p, struct wpabuf *ie); +int p2p_set_wfd_ie_probe_req(struct p2p_data *p2p, struct wpabuf *ie); +int p2p_set_wfd_ie_probe_resp(struct p2p_data *p2p, struct wpabuf *ie); +int p2p_set_wfd_ie_assoc_req(struct p2p_data *p2p, struct wpabuf *ie); +int p2p_set_wfd_ie_invitation(struct p2p_data *p2p, struct wpabuf *ie); +int p2p_set_wfd_ie_prov_disc_req(struct p2p_data *p2p, struct wpabuf *ie); +int p2p_set_wfd_ie_prov_disc_resp(struct p2p_data *p2p, struct wpabuf *ie); +int p2p_set_wfd_ie_go_neg(struct p2p_data *p2p, struct wpabuf *ie); +int p2p_set_wfd_dev_info(struct p2p_data *p2p, const struct wpabuf *elem); +int p2p_set_wfd_assoc_bssid(struct p2p_data *p2p, const struct wpabuf *elem); +int p2p_set_wfd_coupled_sink_info(struct p2p_data *p2p, + const struct wpabuf *elem); +struct wpabuf * wifi_display_encaps(struct wpabuf *subelems); + #endif /* P2P_H */ diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c index 248b2a06..031b3a1f 100644 --- a/src/p2p/p2p_go_neg.c +++ b/src/p2p/p2p_go_neg.c @@ -134,8 +134,14 @@ static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p, struct wpabuf *buf; u8 *len; u8 group_capab; + size_t extra = 0; - buf = wpabuf_alloc(1000); +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_go_neg) + extra = wpabuf_len(p2p->wfd_ie_go_neg); +#endif /* CONFIG_WIFI_DISPLAY */ + + buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; @@ -161,7 +167,7 @@ static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p, p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | p2p->next_tie_breaker); p2p->next_tie_breaker = !p2p->next_tie_breaker; - p2p_buf_add_config_timeout(buf, 100, 20); + p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout); p2p_buf_add_listen_channel(buf, p2p->cfg->country, p2p->cfg->reg_class, p2p->cfg->channel); if (p2p->ext_listen_interval) @@ -177,6 +183,11 @@ static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p, /* WPS IE with Device Password ID attribute */ p2p_build_wps_ie(p2p, buf, p2p_wps_method_pw_id(peer->wps_method), 0); +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_go_neg) + wpabuf_put_buf(buf, p2p->wfd_ie_go_neg); +#endif /* CONFIG_WIFI_DISPLAY */ + return buf; } @@ -246,10 +257,17 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p, struct wpabuf *buf; u8 *len; u8 group_capab; + size_t extra = 0; wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Building GO Negotiation Response"); - buf = wpabuf_alloc(1000); + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_go_neg) + extra = wpabuf_len(p2p->wfd_ie_go_neg); +#endif /* CONFIG_WIFI_DISPLAY */ + + buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; @@ -274,7 +292,7 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p, ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, group_capab); p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | tie_breaker); - p2p_buf_add_config_timeout(buf, 100, 20); + p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout); if (peer && peer->go_state == REMOTE_GO) { wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Omit Operating " "Channel attribute"); @@ -308,6 +326,12 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p, p2p_wps_method_pw_id(peer ? peer->wps_method : WPS_NOT_READY), 0); +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_go_neg) + wpabuf_put_buf(buf, p2p->wfd_ie_go_neg); +#endif /* CONFIG_WIFI_DISPLAY */ + + return buf; } @@ -692,7 +716,7 @@ fail: P2P_PENDING_GO_NEG_RESPONSE_FAILURE; if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, p2p->cfg->dev_addr, - wpabuf_head(resp), wpabuf_len(resp), 200) < 0) { + wpabuf_head(resp), wpabuf_len(resp), 250) < 0) { wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Failed to send Action frame"); } @@ -710,10 +734,17 @@ static struct wpabuf * p2p_build_go_neg_conf(struct p2p_data *p2p, u8 *len; struct p2p_channels res; u8 group_capab; + size_t extra = 0; wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Building GO Negotiation Confirm"); - buf = wpabuf_alloc(1000); + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_go_neg) + extra = wpabuf_len(p2p->wfd_ie_go_neg); +#endif /* CONFIG_WIFI_DISPLAY */ + + buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; @@ -752,6 +783,11 @@ static struct wpabuf * p2p_build_go_neg_conf(struct p2p_data *p2p, } p2p_buf_update_ie_hdr(buf, len); +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_go_neg) + wpabuf_put_buf(buf, p2p->wfd_ie_go_neg); +#endif /* CONFIG_WIFI_DISPLAY */ + return buf; } diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c index 8d4a3cbe..86873205 100644 --- a/src/p2p/p2p_group.c +++ b/src/p2p/p2p_group.c @@ -22,6 +22,7 @@ struct p2p_group_member { u8 addr[ETH_ALEN]; /* P2P Interface Address */ u8 dev_addr[ETH_ALEN]; /* P2P Device Address */ struct wpabuf *p2p_ie; + struct wpabuf *wfd_ie; struct wpabuf *client_info; u8 dev_capab; }; @@ -37,12 +38,10 @@ struct p2p_group { int group_formation; int beacon_update; struct wpabuf *noa; + struct wpabuf *wfd_ie; }; -static void p2p_group_update_ies(struct p2p_group *group); - - struct p2p_group * p2p_group_init(struct p2p_data *p2p, struct p2p_group_config *config) { @@ -52,8 +51,8 @@ struct p2p_group * p2p_group_init(struct p2p_data *p2p, if (group == NULL) return NULL; - groups = os_realloc(p2p->groups, (p2p->num_groups + 1) * - sizeof(struct p2p_group *)); + groups = os_realloc_array(p2p->groups, p2p->num_groups + 1, + sizeof(struct p2p_group *)); if (groups == NULL) { os_free(group); return NULL; @@ -74,6 +73,7 @@ struct p2p_group * p2p_group_init(struct p2p_data *p2p, static void p2p_group_free_member(struct p2p_group_member *m) { + wpabuf_free(m->wfd_ie); wpabuf_free(m->p2p_ie); wpabuf_free(m->client_info); os_free(m); @@ -118,6 +118,7 @@ void p2p_group_deinit(struct p2p_group *group) p2p_group_free_members(group); os_free(group->cfg); wpabuf_free(group->noa); + wpabuf_free(group->wfd_ie); os_free(group); } @@ -172,11 +173,22 @@ static struct wpabuf * p2p_group_build_beacon_ie(struct p2p_group *group) { struct wpabuf *ie; u8 *len; + size_t extra = 0; + +#ifdef CONFIG_WIFI_DISPLAY + if (group->p2p->wfd_ie_beacon) + extra = wpabuf_len(group->p2p->wfd_ie_beacon); +#endif /* CONFIG_WIFI_DISPLAY */ - ie = wpabuf_alloc(257); + ie = wpabuf_alloc(257 + extra); if (ie == NULL) return NULL; +#ifdef CONFIG_WIFI_DISPLAY + if (group->p2p->wfd_ie_beacon) + wpabuf_put_buf(ie, group->p2p->wfd_ie_beacon); +#endif /* CONFIG_WIFI_DISPLAY */ + len = p2p_buf_add_ie_hdr(ie); p2p_group_add_common_ies(group, ie); p2p_buf_add_device_id(ie, group->p2p->cfg->dev_addr); @@ -187,17 +199,193 @@ static struct wpabuf * p2p_group_build_beacon_ie(struct p2p_group *group) } +#ifdef CONFIG_WIFI_DISPLAY + +struct wpabuf * p2p_group_get_wfd_ie(struct p2p_group *g) +{ + return g->wfd_ie; +} + + +struct wpabuf * wifi_display_encaps(struct wpabuf *subelems) +{ + struct wpabuf *ie; + const u8 *pos, *end; + + if (subelems == NULL) + return NULL; + + ie = wpabuf_alloc(wpabuf_len(subelems) + 100); + if (ie == NULL) + return NULL; + + pos = wpabuf_head(subelems); + end = pos + wpabuf_len(subelems); + + while (end > pos) { + size_t frag_len = end - pos; + if (frag_len > 251) + frag_len = 251; + wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(ie, 4 + frag_len); + wpabuf_put_be32(ie, WFD_IE_VENDOR_TYPE); + wpabuf_put_data(ie, pos, frag_len); + pos += frag_len; + } + + return ie; +} + + +static int wifi_display_add_dev_info_descr(struct wpabuf *buf, + struct p2p_group_member *m) +{ + const u8 *pos, *end; + const u8 *dev_info = NULL; + const u8 *assoc_bssid = NULL; + const u8 *coupled_sink = NULL; + u8 zero_addr[ETH_ALEN]; + + if (m->wfd_ie == NULL) + return 0; + + os_memset(zero_addr, 0, ETH_ALEN); + pos = wpabuf_head_u8(m->wfd_ie); + end = pos + wpabuf_len(m->wfd_ie); + while (pos + 1 < end) { + u8 id; + u16 len; + + id = *pos++; + len = WPA_GET_BE16(pos); + pos += 2; + if (pos + len > end) + break; + + switch (id) { + case WFD_SUBELEM_DEVICE_INFO: + if (len < 6) + break; + dev_info = pos; + break; + case WFD_SUBELEM_ASSOCIATED_BSSID: + if (len < ETH_ALEN) + break; + assoc_bssid = pos; + break; + case WFD_SUBELEM_COUPLED_SINK: + if (len < 1 + ETH_ALEN) + break; + coupled_sink = pos; + break; + } + + pos += len; + } + + if (dev_info == NULL) + return 0; + + wpabuf_put_u8(buf, 23); + wpabuf_put_data(buf, m->dev_addr, ETH_ALEN); + if (assoc_bssid) + wpabuf_put_data(buf, assoc_bssid, ETH_ALEN); + else + wpabuf_put_data(buf, zero_addr, ETH_ALEN); + wpabuf_put_data(buf, dev_info, 2); /* WFD Device Info */ + wpabuf_put_data(buf, dev_info + 4, 2); /* WFD Device Max Throughput */ + if (coupled_sink) { + wpabuf_put_data(buf, coupled_sink, 1 + ETH_ALEN); + } else { + wpabuf_put_u8(buf, 0); + wpabuf_put_data(buf, zero_addr, ETH_ALEN); + } + + return 1; +} + + +static struct wpabuf * +wifi_display_build_go_ie(struct p2p_group *group) +{ + struct wpabuf *wfd_subelems, *wfd_ie; + struct p2p_group_member *m; + u8 *len; + unsigned int count = 0; + + if (!group->p2p->wfd_ie_probe_resp) + return NULL; + + wfd_subelems = wpabuf_alloc(wpabuf_len(group->p2p->wfd_ie_probe_resp) + + group->num_members * 24 + 100); + if (wfd_subelems == NULL) + return NULL; + if (group->p2p->wfd_dev_info) + wpabuf_put_buf(wfd_subelems, group->p2p->wfd_dev_info); + if (group->p2p->wfd_assoc_bssid) + wpabuf_put_buf(wfd_subelems, + group->p2p->wfd_assoc_bssid); + if (group->p2p->wfd_coupled_sink_info) + wpabuf_put_buf(wfd_subelems, + group->p2p->wfd_coupled_sink_info); + + /* Build WFD Session Info */ + wpabuf_put_u8(wfd_subelems, WFD_SUBELEM_SESSION_INFO); + len = wpabuf_put(wfd_subelems, 2); + m = group->members; + while (m) { + if (wifi_display_add_dev_info_descr(wfd_subelems, m)) + count++; + m = m->next; + } + + if (count == 0) { + /* No Wi-Fi Display clients - do not include subelement */ + wfd_subelems->used -= 3; + } else { + WPA_PUT_BE16(len, (u8 *) wpabuf_put(wfd_subelems, 0) - len - + 2); + wpa_printf(MSG_DEBUG, "WFD: WFD Session Info: %u descriptors", + count); + } + + wfd_ie = wifi_display_encaps(wfd_subelems); + wpabuf_free(wfd_subelems); + + return wfd_ie; +} + +static void wifi_display_group_update(struct p2p_group *group) +{ + wpabuf_free(group->wfd_ie); + group->wfd_ie = wifi_display_build_go_ie(group); +} + +#endif /* CONFIG_WIFI_DISPLAY */ + + static struct wpabuf * p2p_group_build_probe_resp_ie(struct p2p_group *group) { u8 *group_info; struct wpabuf *ie; struct p2p_group_member *m; u8 *len; + size_t extra = 0; + +#ifdef CONFIG_WIFI_DISPLAY + if (group->wfd_ie) + extra += wpabuf_len(group->wfd_ie); +#endif /* CONFIG_WIFI_DISPLAY */ - ie = wpabuf_alloc(257); + ie = wpabuf_alloc(257 + extra); if (ie == NULL) return NULL; +#ifdef CONFIG_WIFI_DISPLAY + if (group->wfd_ie) + wpabuf_put_buf(ie, group->wfd_ie); +#endif /* CONFIG_WIFI_DISPLAY */ + len = p2p_buf_add_ie_hdr(ie); p2p_group_add_common_ies(group, ie); @@ -216,15 +404,20 @@ static struct wpabuf * p2p_group_build_probe_resp_ie(struct p2p_group *group) (u8 *) wpabuf_put(ie, 0) - group_info - 3); p2p_buf_update_ie_hdr(ie, len); + return ie; } -static void p2p_group_update_ies(struct p2p_group *group) +void p2p_group_update_ies(struct p2p_group *group) { struct wpabuf *beacon_ie; struct wpabuf *probe_resp_ie; +#ifdef CONFIG_WIFI_DISPLAY + wifi_display_group_update(group); +#endif /* CONFIG_WIFI_DISPLAY */ + probe_resp_ie = p2p_group_build_probe_resp_ie(group); if (probe_resp_ie == NULL) return; @@ -354,6 +547,9 @@ int p2p_group_notif_assoc(struct p2p_group *group, const u8 *addr, &m->dev_capab, m->dev_addr); } +#ifdef CONFIG_WIFI_DISPLAY + m->wfd_ie = ieee802_11_vendor_ie_concat(ie, len, WFD_IE_VENDOR_TYPE); +#endif /* CONFIG_WIFI_DISPLAY */ p2p_group_remove_member(group, addr); @@ -361,8 +557,9 @@ int p2p_group_notif_assoc(struct p2p_group *group, const u8 *addr, group->members = m; group->num_members++; wpa_msg(group->p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Add client " MACSTR - " to group (p2p=%d client_info=%d); num_members=%u/%u", - MAC2STR(addr), m->p2p_ie ? 1 : 0, m->client_info ? 1 : 0, + " to group (p2p=%d wfd=%d client_info=%d); num_members=%u/%u", + MAC2STR(addr), m->p2p_ie ? 1 : 0, m->wfd_ie ? 1 : 0, + m->client_info ? 1 : 0, group->num_members, group->cfg->max_clients); if (group->num_members == group->cfg->max_clients) group->beacon_update = 1; @@ -378,6 +575,12 @@ struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status) { struct wpabuf *resp; u8 *rlen; + size_t extra = 0; + +#ifdef CONFIG_WIFI_DISPLAY + if (group->wfd_ie) + extra = wpabuf_len(group->wfd_ie); +#endif /* CONFIG_WIFI_DISPLAY */ /* * (Re)Association Response - P2P IE @@ -385,9 +588,15 @@ struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status) * denied) * Extended Listen Timing (may be present) */ - resp = wpabuf_alloc(20); + resp = wpabuf_alloc(20 + extra); if (resp == NULL) return NULL; + +#ifdef CONFIG_WIFI_DISPLAY + if (group->wfd_ie) + wpabuf_put_buf(resp, group->wfd_ie); +#endif /* CONFIG_WIFI_DISPLAY */ + rlen = p2p_buf_add_ie_hdr(resp); if (status != P2P_SC_SUCCESS) p2p_buf_add_status(resp, status); diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h index b0abff0b..bf550158 100644 --- a/src/p2p/p2p_i.h +++ b/src/p2p/p2p_i.h @@ -109,6 +109,7 @@ struct p2p_sd_query { struct p2p_sd_query *next; u8 peer[ETH_ALEN]; int for_all_peers; + int wsd; /* Wi-Fi Display Service Discovery Request */ struct wpabuf *tlvs; }; @@ -207,6 +208,11 @@ struct p2p_data { * P2P_SEARCH_WHEN_READY - Waiting to start Search */ P2P_SEARCH_WHEN_READY, + + /** + * P2P_CONTINUE_SEARCH_WHEN_READY - Waiting to continue Search + */ + P2P_CONTINUE_SEARCH_WHEN_READY, } state; /** @@ -437,6 +443,27 @@ struct p2p_data { * in IDLE state. */ int pd_retries; + + u8 go_timeout; + u8 client_timeout; + + /* Extra delay in milliseconds between search iterations */ + unsigned int search_delay; + int in_search_delay; + +#ifdef CONFIG_WIFI_DISPLAY + struct wpabuf *wfd_ie_beacon; + struct wpabuf *wfd_ie_probe_req; + struct wpabuf *wfd_ie_probe_resp; + struct wpabuf *wfd_ie_assoc_req; + struct wpabuf *wfd_ie_invitation; + struct wpabuf *wfd_ie_prov_disc_req; + struct wpabuf *wfd_ie_prov_disc_resp; + struct wpabuf *wfd_ie_go_neg; + struct wpabuf *wfd_dev_info; + struct wpabuf *wfd_assoc_bssid; + struct wpabuf *wfd_coupled_sink_info; +#endif /* CONFIG_WIFI_DISPLAY */ }; /** @@ -445,6 +472,7 @@ struct p2p_data { struct p2p_message { struct wpabuf *p2p_attributes; struct wpabuf *wps_attributes; + struct wpabuf *wfd_subelems; u8 dialog_token; @@ -565,6 +593,8 @@ u8 p2p_group_presence_req(struct p2p_group *group, const u8 *noa, size_t noa_len); int p2p_group_is_group_id_match(struct p2p_group *group, const u8 *group_id, size_t group_id_len); +void p2p_group_update_ies(struct p2p_group *group); +struct wpabuf * p2p_group_get_wfd_ie(struct p2p_group *g); void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token); diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c index 59255495..df24c645 100644 --- a/src/p2p/p2p_invitation.c +++ b/src/p2p/p2p_invitation.c @@ -21,8 +21,27 @@ static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p, struct wpabuf *buf; u8 *len; const u8 *dev_addr; + size_t extra = 0; + +#ifdef CONFIG_WIFI_DISPLAY + struct wpabuf *wfd_ie = p2p->wfd_ie_invitation; + if (wfd_ie && p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO) { + size_t i; + for (i = 0; i < p2p->num_groups; i++) { + struct p2p_group *g = p2p->groups[i]; + struct wpabuf *ie; + ie = p2p_group_get_wfd_ie(g); + if (ie) { + wfd_ie = ie; + break; + } + } + } + if (wfd_ie) + extra = wpabuf_len(wfd_ie); +#endif /* CONFIG_WIFI_DISPLAY */ - buf = wpabuf_alloc(1000); + buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; @@ -36,7 +55,8 @@ static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p, if (p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO || !p2p->inv_persistent) p2p_buf_add_config_timeout(buf, 0, 0); else - p2p_buf_add_config_timeout(buf, 100, 20); + p2p_buf_add_config_timeout(buf, p2p->go_timeout, + p2p->client_timeout); p2p_buf_add_invitation_flags(buf, p2p->inv_persistent ? P2P_INVITATION_FLAGS_TYPE : 0); p2p_buf_add_operating_channel(buf, p2p->cfg->country, @@ -54,6 +74,11 @@ static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p, p2p_buf_add_device_info(buf, p2p, peer); p2p_buf_update_ie_hdr(buf, len); +#ifdef CONFIG_WIFI_DISPLAY + if (wfd_ie) + wpabuf_put_buf(buf, wfd_ie); +#endif /* CONFIG_WIFI_DISPLAY */ + return buf; } @@ -67,8 +92,30 @@ static struct wpabuf * p2p_build_invitation_resp(struct p2p_data *p2p, { struct wpabuf *buf; u8 *len; + size_t extra = 0; + +#ifdef CONFIG_WIFI_DISPLAY + struct wpabuf *wfd_ie = p2p->wfd_ie_invitation; + if (wfd_ie && group_bssid) { + size_t i; + for (i = 0; i < p2p->num_groups; i++) { + struct p2p_group *g = p2p->groups[i]; + struct wpabuf *ie; + if (!p2p_group_is_group_id_match(g, group_bssid, + ETH_ALEN)) + continue; + ie = p2p_group_get_wfd_ie(g); + if (ie) { + wfd_ie = ie; + break; + } + } + } + if (wfd_ie) + extra = wpabuf_len(wfd_ie); +#endif /* CONFIG_WIFI_DISPLAY */ - buf = wpabuf_alloc(1000); + buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; @@ -87,6 +134,11 @@ static struct wpabuf * p2p_build_invitation_resp(struct p2p_data *p2p, p2p_buf_add_channel_list(buf, p2p->cfg->country, channels); p2p_buf_update_ie_hdr(buf, len); +#ifdef CONFIG_WIFI_DISPLAY + if (wfd_ie) + wpabuf_put_buf(buf, wfd_ie); +#endif /* CONFIG_WIFI_DISPLAY */ + return buf; } diff --git a/src/p2p/p2p_parse.c b/src/p2p/p2p_parse.c index a3ec57df..097a31de 100644 --- a/src/p2p/p2p_parse.c +++ b/src/p2p/p2p_parse.c @@ -414,6 +414,13 @@ int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg) return -1; } +#ifdef CONFIG_WIFI_DISPLAY + if (elems.wfd) { + msg->wfd_subelems = ieee802_11_vendor_ie_concat( + data, len, WFD_IE_VENDOR_TYPE); + } +#endif /* CONFIG_WIFI_DISPLAY */ + return 0; } @@ -453,6 +460,10 @@ void p2p_parse_free(struct p2p_message *msg) msg->p2p_attributes = NULL; wpabuf_free(msg->wps_attributes); msg->wps_attributes = NULL; +#ifdef CONFIG_WIFI_DISPLAY + wpabuf_free(msg->wfd_subelems); + msg->wfd_subelems = NULL; +#endif /* CONFIG_WIFI_DISPLAY */ } diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c index 38a9dd81..a2d5aeeb 100644 --- a/src/p2p/p2p_pd.c +++ b/src/p2p/p2p_pd.c @@ -16,8 +16,8 @@ /* - * Number of retries to attempt for provision discovery requests during IDLE - * state in case the peer is not listening. + * Number of retries to attempt for provision discovery requests + * in case the peer is not listening. */ #define MAX_PROV_DISC_REQ_RETRIES 10 @@ -46,8 +46,14 @@ static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p, { struct wpabuf *buf; u8 *len; + size_t extra = 0; - buf = wpabuf_alloc(1000); +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_prov_disc_req) + extra = wpabuf_len(p2p->wfd_ie_prov_disc_req); +#endif /* CONFIG_WIFI_DISPLAY */ + + buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; @@ -66,17 +72,46 @@ static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p, /* WPS IE with Config Methods attribute */ p2p_build_wps_ie_config_methods(buf, config_methods); +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_prov_disc_req) + wpabuf_put_buf(buf, p2p->wfd_ie_prov_disc_req); +#endif /* CONFIG_WIFI_DISPLAY */ + return buf; } static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, u8 dialog_token, - u16 config_methods) + u16 config_methods, + const u8 *group_id, + size_t group_id_len) { struct wpabuf *buf; + size_t extra = 0; + +#ifdef CONFIG_WIFI_DISPLAY + struct wpabuf *wfd_ie = p2p->wfd_ie_prov_disc_resp; + if (wfd_ie && group_id) { + size_t i; + for (i = 0; i < p2p->num_groups; i++) { + struct p2p_group *g = p2p->groups[i]; + struct wpabuf *ie; + if (!p2p_group_is_group_id_match(g, group_id, + group_id_len)) + continue; + ie = p2p_group_get_wfd_ie(g); + if (ie) { + wfd_ie = ie; + break; + } + } + } + if (wfd_ie) + extra = wpabuf_len(wfd_ie); +#endif /* CONFIG_WIFI_DISPLAY */ - buf = wpabuf_alloc(100); + buf = wpabuf_alloc(100 + extra); if (buf == NULL) return NULL; @@ -85,6 +120,11 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, /* WPS IE with Config Methods attribute */ p2p_build_wps_ie_config_methods(buf, config_methods); +#ifdef CONFIG_WIFI_DISPLAY + if (wfd_ie) + wpabuf_put_buf(buf, wfd_ie); +#endif /* CONFIG_WIFI_DISPLAY */ + return buf; } @@ -117,6 +157,9 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, "P2P: Provision Discovery Request add device " "failed " MACSTR, MAC2STR(sa)); } + } else if (msg.wfd_subelems) { + wpabuf_free(dev->info.wfd_subelems); + dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); } if (!(msg.wps_config_methods & @@ -162,7 +205,8 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, out: resp = p2p_build_prov_disc_resp(p2p, msg.dialog_token, - reject ? 0 : msg.wps_config_methods); + reject ? 0 : msg.wps_config_methods, + msg.group_id, msg.group_id_len); if (resp == NULL) { p2p_parse_free(&msg); return; @@ -408,8 +452,7 @@ int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr, */ p2p->user_initiated_pd = !join; - /* Also set some retries to attempt in case of IDLE state */ - if (p2p->user_initiated_pd && p2p->state == P2P_IDLE) + if (p2p->user_initiated_pd) p2p->pd_retries = MAX_PROV_DISC_REQ_RETRIES; return p2p_send_prov_disc_req(p2p, dev, join, force_freq); diff --git a/src/p2p/p2p_sd.c b/src/p2p/p2p_sd.c index 7a59f107..bf756050 100644 --- a/src/p2p/p2p_sd.c +++ b/src/p2p/p2p_sd.c @@ -15,15 +15,55 @@ #include "p2p.h" +#ifdef CONFIG_WIFI_DISPLAY +static int wfd_wsd_supported(struct wpabuf *wfd) +{ + const u8 *pos, *end; + u8 subelem; + u16 len; + + if (wfd == NULL) + return 0; + + pos = wpabuf_head(wfd); + end = pos + wpabuf_len(wfd); + + while (pos + 3 <= end) { + subelem = *pos++; + len = WPA_GET_BE16(pos); + pos += 2; + if (pos + len > end) + break; + + if (subelem == WFD_SUBELEM_DEVICE_INFO && len >= 6) { + u16 info = WPA_GET_BE16(pos); + return !!(info & 0x0040); + } + + pos += len; + } + + return 0; +} +#endif /* CONFIG_WIFI_DISPLAY */ + struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p, struct p2p_device *dev) { struct p2p_sd_query *q; + int wsd = 0; if (!(dev->info.dev_capab & P2P_DEV_CAPAB_SERVICE_DISCOVERY)) return NULL; /* peer does not support SD */ +#ifdef CONFIG_WIFI_DISPLAY + if (wfd_wsd_supported(dev->info.wfd_subelems)) + wsd = 1; +#endif /* CONFIG_WIFI_DISPLAY */ for (q = p2p->sd_queries; q; q = q->next) { + /* Use WSD only if the peer indicates support or it */ + if (q->wsd && !wsd) + continue; if (q->for_all_peers && !(dev->flags & P2P_DEV_SD_INFO)) return q; if (!q->for_all_peers && @@ -420,7 +460,7 @@ void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa, if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL || #endif os_memcmp(sa, p2p->sd_peer->info.p2p_device_addr, ETH_ALEN) != 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Ignore unexpected GAS Initial Response from " MACSTR, MAC2STR(sa)); return; @@ -670,7 +710,7 @@ void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa, if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL || #endif os_memcmp(sa, p2p->sd_peer->info.p2p_device_addr, ETH_ALEN) != 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Ignore unexpected GAS Comeback Response from " MACSTR, MAC2STR(sa)); return; @@ -907,6 +947,19 @@ void * p2p_sd_request(struct p2p_data *p2p, const u8 *dst, } +#ifdef CONFIG_WIFI_DISPLAY +void * p2p_sd_request_wfd(struct p2p_data *p2p, const u8 *dst, + const struct wpabuf *tlvs) +{ + struct p2p_sd_query *q; + q = p2p_sd_request(p2p, dst, tlvs); + if (q) + q->wsd = 1; + return q; +} +#endif /* CONFIG_WIFI_DISPLAY */ + + #ifdef ANDROID_P2P void p2p_sd_service_update(struct p2p_data *p2p, int action) #else diff --git a/src/radius/radius.c b/src/radius/radius.c index 2b9cbcaa..d5edfd8c 100644 --- a/src/radius/radius.c +++ b/src/radius/radius.c @@ -78,8 +78,8 @@ static void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier) static int radius_msg_initialize(struct radius_msg *msg) { - msg->attr_pos = - os_zalloc(RADIUS_DEFAULT_ATTR_COUNT * sizeof(*msg->attr_pos)); + msg->attr_pos = os_calloc(RADIUS_DEFAULT_ATTR_COUNT, + sizeof(*msg->attr_pos)); if (msg->attr_pos == NULL) return -1; @@ -269,7 +269,7 @@ static void radius_msg_dump_attr(struct radius_attr_hdr *hdr) printf(" Attribute %d (%s) length=%d\n", hdr->type, attr ? attr->name : "?Unknown?", hdr->length); - if (attr == NULL) + if (attr == NULL || hdr->length < sizeof(struct radius_attr_hdr)) return; len = hdr->length - sizeof(struct radius_attr_hdr); @@ -332,7 +332,7 @@ void radius_msg_dump(struct radius_msg *msg) printf("RADIUS message: code=%d (%s) identifier=%d length=%d\n", msg->hdr->code, radius_code_string(msg->hdr->code), - msg->hdr->identifier, ntohs(msg->hdr->length)); + msg->hdr->identifier, be_to_host16(msg->hdr->length)); for (i = 0; i < msg->attr_used; i++) { struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i); @@ -357,11 +357,11 @@ int radius_msg_finish(struct radius_msg *msg, const u8 *secret, "Message-Authenticator"); return -1; } - msg->hdr->length = htons(wpabuf_len(msg->buf)); + msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); hmac_md5(secret, secret_len, wpabuf_head(msg->buf), wpabuf_len(msg->buf), (u8 *) (attr + 1)); } else - msg->hdr->length = htons(wpabuf_len(msg->buf)); + msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); if (wpabuf_len(msg->buf) > 0xffff) { wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)", @@ -387,7 +387,7 @@ int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret, printf("WARNING: Could not add Message-Authenticator\n"); return -1; } - msg->hdr->length = htons(wpabuf_len(msg->buf)); + msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); os_memcpy(msg->hdr->authenticator, req_authenticator, sizeof(msg->hdr->authenticator)); hmac_md5(secret, secret_len, wpabuf_head(msg->buf), @@ -430,7 +430,7 @@ int radius_msg_finish_das_resp(struct radius_msg *msg, const u8 *secret, return -1; } - msg->hdr->length = htons(wpabuf_len(msg->buf)); + msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); os_memcpy(msg->hdr->authenticator, req_hdr->authenticator, 16); hmac_md5(secret, secret_len, wpabuf_head(msg->buf), wpabuf_len(msg->buf), (u8 *) (attr + 1)); @@ -458,7 +458,7 @@ void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret, const u8 *addr[2]; size_t len[2]; - msg->hdr->length = htons(wpabuf_len(msg->buf)); + msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); os_memset(msg->hdr->authenticator, 0, MD5_MAC_LEN); addr[0] = wpabuf_head(msg->buf); len[0] = wpabuf_len(msg->buf); @@ -562,8 +562,8 @@ static int radius_msg_add_attr_to_array(struct radius_msg *msg, size_t *nattr_pos; int nlen = msg->attr_size * 2; - nattr_pos = os_realloc(msg->attr_pos, - nlen * sizeof(*msg->attr_pos)); + nattr_pos = os_realloc_array(msg->attr_pos, nlen, + sizeof(*msg->attr_pos)); if (nattr_pos == NULL) return -1; @@ -633,7 +633,7 @@ struct radius_msg * radius_msg_parse(const u8 *data, size_t len) hdr = (struct radius_hdr *) data; - msg_len = ntohs(hdr->length); + msg_len = be_to_host16(hdr->length); if (msg_len < sizeof(*hdr) || msg_len > len) { wpa_printf(MSG_INFO, "RADIUS: Invalid message length"); return NULL; @@ -707,9 +707,9 @@ int radius_msg_add_eap(struct radius_msg *msg, const u8 *data, size_t data_len) } -u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *eap_len) +struct wpabuf * radius_msg_get_eap(struct radius_msg *msg) { - u8 *eap, *pos; + struct wpabuf *eap; size_t len, i; struct radius_attr_hdr *attr; @@ -719,30 +719,27 @@ u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *eap_len) len = 0; for (i = 0; i < msg->attr_used; i++) { attr = radius_get_attr_hdr(msg, i); - if (attr->type == RADIUS_ATTR_EAP_MESSAGE) + if (attr->type == RADIUS_ATTR_EAP_MESSAGE && + attr->length > sizeof(struct radius_attr_hdr)) len += attr->length - sizeof(struct radius_attr_hdr); } if (len == 0) return NULL; - eap = os_malloc(len); + eap = wpabuf_alloc(len); if (eap == NULL) return NULL; - pos = eap; for (i = 0; i < msg->attr_used; i++) { attr = radius_get_attr_hdr(msg, i); - if (attr->type == RADIUS_ATTR_EAP_MESSAGE) { + if (attr->type == RADIUS_ATTR_EAP_MESSAGE && + attr->length > sizeof(struct radius_attr_hdr)) { int flen = attr->length - sizeof(*attr); - os_memcpy(pos, attr + 1, flen); - pos += flen; + wpabuf_put_data(eap, attr + 1, flen); } } - if (eap_len) - *eap_len = len; - return eap; } @@ -843,7 +840,7 @@ int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src, for (i = 0; i < src->attr_used; i++) { attr = radius_get_attr_hdr(src, i); - if (attr->type == type) { + if (attr->type == type && attr->length >= sizeof(*attr)) { if (!radius_msg_add_attr(dst, type, (u8 *) (attr + 1), attr->length - sizeof(*attr))) return -1; @@ -900,7 +897,8 @@ static u8 *radius_msg_get_vendor_attr(struct radius_msg *msg, u32 vendor, u32 vendor_id; struct radius_attr_vendor *vhdr; - if (attr->type != RADIUS_ATTR_VENDOR_SPECIFIC) + if (attr->type != RADIUS_ATTR_VENDOR_SPECIFIC || + attr->length < sizeof(*attr)) continue; left = attr->length - sizeof(*attr); @@ -1273,7 +1271,7 @@ int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len) } } - if (!attr) + if (!attr || attr->length < sizeof(*attr)) return -1; dlen = attr->length - sizeof(*attr); @@ -1298,7 +1296,7 @@ int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf, } } - if (!attr) + if (!attr || attr->length < sizeof(*attr)) return -1; *buf = (u8 *) (attr + 1); @@ -1349,6 +1347,8 @@ int radius_msg_get_vlanid(struct radius_msg *msg) for (i = 0; i < msg->attr_used; i++) { attr = radius_get_attr_hdr(msg, i); + if (attr->length < sizeof(*attr)) + return -1; data = (const u8 *) (attr + 1); dlen = attr->length - sizeof(*attr); if (attr->length < 3) @@ -1534,7 +1534,7 @@ int radius_copy_class(struct radius_class_data *dst, if (src->attr == NULL) return 0; - dst->attr = os_zalloc(src->count * sizeof(struct radius_attr_data)); + dst->attr = os_calloc(src->count, sizeof(struct radius_attr_data)); if (dst->attr == NULL) return -1; diff --git a/src/radius/radius.h b/src/radius/radius.h index 2d059df1..727640b2 100644 --- a/src/radius/radius.h +++ b/src/radius/radius.h @@ -18,7 +18,7 @@ struct radius_hdr { u8 code; u8 identifier; - u16 length; /* including this header */ + be16 length; /* including this header */ u8 authenticator[16]; /* followed by length-20 octets of attributes */ } STRUCT_PACKED; @@ -213,7 +213,7 @@ struct radius_attr_hdr * radius_msg_add_attr(struct radius_msg *msg, u8 type, struct radius_msg * radius_msg_parse(const u8 *data, size_t len); int radius_msg_add_eap(struct radius_msg *msg, const u8 *data, size_t data_len); -u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *len); +struct wpabuf * radius_msg_get_eap(struct radius_msg *msg); int radius_msg_verify(struct radius_msg *msg, const u8 *secret, size_t secret_len, struct radius_msg *sent_msg, int auth); diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c index 1ee71316..425ad935 100644 --- a/src/radius/radius_client.c +++ b/src/radius/radius_client.c @@ -281,8 +281,8 @@ int radius_client_register(struct radius_client_data *radius, num = &radius->num_auth_handlers; } - newh = os_realloc(*handlers, - (*num + 1) * sizeof(struct radius_rx_handler)); + newh = os_realloc_array(*handlers, *num + 1, + sizeof(struct radius_rx_handler)); if (newh == NULL) return -1; diff --git a/src/radius/radius_das.c b/src/radius/radius_das.c index 8ecfffc9..bded9651 100644 --- a/src/radius/radius_das.c +++ b/src/radius/radius_das.c @@ -127,8 +127,11 @@ fail: return NULL; if (error) { - radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE, - error); + if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE, + error)) { + radius_msg_free(reply); + return NULL; + } } return reply; @@ -225,7 +228,12 @@ static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx) break; /* Unsupported Service */ - radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE, 405); + if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE, + 405)) { + radius_msg_free(reply); + reply = NULL; + break; + } break; default: wpa_printf(MSG_DEBUG, "DAS: Unexpected RADIUS code %u in " diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c index 123c59b7..5b2d7111 100644 --- a/src/radius/radius_server.c +++ b/src/radius/radius_server.c @@ -689,8 +689,7 @@ static int radius_server_request(struct radius_server_data *data, const char *from_addr, int from_port, struct radius_session *force_sess) { - u8 *eap = NULL; - size_t eap_len; + struct wpabuf *eap = NULL; int res, state_included = 0; u8 statebuf[4]; unsigned int state; @@ -754,7 +753,7 @@ static int radius_server_request(struct radius_server_data *data, return -1; } - eap = radius_msg_get_eap(msg, &eap_len); + eap = radius_msg_get_eap(msg); if (eap == NULL) { RADIUS_DEBUG("No EAP-Message in RADIUS packet from %s", from_addr); @@ -763,7 +762,7 @@ static int radius_server_request(struct radius_server_data *data, return -1; } - RADIUS_DUMP("Received EAP data", eap, eap_len); + RADIUS_DUMP("Received EAP data", wpabuf_head(eap), wpabuf_len(eap)); /* FIX: if Code is Request, Success, or Failure, send Access-Reject; * RFC3579 Sect. 2.6.2. @@ -773,10 +772,7 @@ static int radius_server_request(struct radius_server_data *data, * Or is this already done by the EAP state machine? */ wpabuf_free(sess->eap_if->eapRespData); - sess->eap_if->eapRespData = wpabuf_alloc_ext_data(eap, eap_len); - if (sess->eap_if->eapRespData == NULL) - os_free(eap); - eap = NULL; + sess->eap_if->eapRespData = eap; sess->eap_if->eapResp = TRUE; eap_server_sm_step(sess->eap); diff --git a/src/rsn_supp/peerkey.c b/src/rsn_supp/peerkey.c index 3d5aac6b..5e4872e9 100644 --- a/src/rsn_supp/peerkey.c +++ b/src/rsn_supp/peerkey.c @@ -221,6 +221,9 @@ static int wpa_supplicant_process_smk_m2( if (cipher & WPA_CIPHER_CCMP) { wpa_printf(MSG_DEBUG, "RSN: Using CCMP for PeerKey"); cipher = WPA_CIPHER_CCMP; + } else if (cipher & WPA_CIPHER_GCMP) { + wpa_printf(MSG_DEBUG, "RSN: Using GCMP for PeerKey"); + cipher = WPA_CIPHER_GCMP; } else if (cipher & WPA_CIPHER_TKIP) { wpa_printf(MSG_DEBUG, "RSN: Using TKIP for PeerKey"); cipher = WPA_CIPHER_TKIP; @@ -269,6 +272,8 @@ static int wpa_supplicant_process_smk_m2( pos += 2; if (cipher == WPA_CIPHER_CCMP) RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); + else if (cipher == WPA_CIPHER_GCMP) + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP); else if (cipher == WPA_CIPHER_TKIP) RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); pos += RSN_SELECTOR_LEN; @@ -344,7 +349,7 @@ static void wpa_supplicant_send_stk_1_of_4(struct wpa_sm *sm, msg->type = EAPOL_KEY_TYPE_RSN; - if (peerkey->cipher == WPA_CIPHER_CCMP) + if (peerkey->cipher != WPA_CIPHER_TKIP) ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; else ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; @@ -352,7 +357,7 @@ static void wpa_supplicant_send_stk_1_of_4(struct wpa_sm *sm, key_info = ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_ACK; WPA_PUT_BE16(msg->key_info, key_info); - if (peerkey->cipher == WPA_CIPHER_CCMP) + if (peerkey->cipher != WPA_CIPHER_TKIP) WPA_PUT_BE16(msg->key_length, 16); else WPA_PUT_BE16(msg->key_length, 32); @@ -403,7 +408,7 @@ static void wpa_supplicant_send_stk_3_of_4(struct wpa_sm *sm, msg->type = EAPOL_KEY_TYPE_RSN; - if (peerkey->cipher == WPA_CIPHER_CCMP) + if (peerkey->cipher != WPA_CIPHER_TKIP) ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; else ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; @@ -412,7 +417,7 @@ static void wpa_supplicant_send_stk_3_of_4(struct wpa_sm *sm, WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE; WPA_PUT_BE16(msg->key_info, key_info); - if (peerkey->cipher == WPA_CIPHER_CCMP) + if (peerkey->cipher != WPA_CIPHER_TKIP) WPA_PUT_BE16(msg->key_length, 16); else WPA_PUT_BE16(msg->key_length, 32); @@ -500,6 +505,9 @@ static int wpa_supplicant_process_smk_m5(struct wpa_sm *sm, if (cipher & WPA_CIPHER_CCMP) { wpa_printf(MSG_DEBUG, "RSN: Using CCMP for PeerKey"); peerkey->cipher = WPA_CIPHER_CCMP; + } else if (cipher & WPA_CIPHER_GCMP) { + wpa_printf(MSG_DEBUG, "RSN: Using GCMP for PeerKey"); + peerkey->cipher = WPA_CIPHER_GCMP; } else if (cipher & WPA_CIPHER_TKIP) { wpa_printf(MSG_DEBUG, "RSN: Using TKIP for PeerKey"); peerkey->cipher = WPA_CIPHER_TKIP; @@ -1016,7 +1024,7 @@ int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer) return -1; } - if (sm->pairwise_cipher == WPA_CIPHER_CCMP) + if (sm->pairwise_cipher != WPA_CIPHER_TKIP) ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; else ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; @@ -1061,6 +1069,11 @@ int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer) pos += RSN_SELECTOR_LEN; count++; } + if (sm->allowed_pairwise_cipher & WPA_CIPHER_GCMP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP); + pos += RSN_SELECTOR_LEN; + count++; + } if (sm->allowed_pairwise_cipher & WPA_CIPHER_TKIP) { RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); pos += RSN_SELECTOR_LEN; diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c index 2d265d08..9783e7c4 100644 --- a/src/rsn_supp/pmksa_cache.c +++ b/src/rsn_supp/pmksa_cache.c @@ -197,11 +197,25 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) { /* Remove the oldest entry to make room for the new entry */ pos = pmksa->pmksa; - pmksa->pmksa = pos->next; - wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache " - "entry (for " MACSTR ") to make room for new one", - MAC2STR(pos->aa)); - pmksa_cache_free_entry(pmksa, pos, 0); + + if (pos == pmksa->sm->cur_pmksa) { + /* + * Never remove the current PMKSA cache entry, since + * it's in use, and removing it triggers a needless + * deauthentication. + */ + pos = pos->next; + pmksa->pmksa->next = pos ? pos->next : NULL; + } else + pmksa->pmksa = pos->next; + + if (pos) { + wpa_printf(MSG_DEBUG, "RSN: removed the oldest idle " + "PMKSA cache entry (for " MACSTR ") to " + "make room for new one", + MAC2STR(pos->aa)); + pmksa_cache_free_entry(pmksa, pos, 0); + } } /* Add the new entry; order by expiration time */ diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index 7159c3ae..bcd59510 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -91,7 +91,7 @@ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) if (wpa_key_mgmt_ft(sm->key_mgmt) || wpa_key_mgmt_sha256(sm->key_mgmt)) ver = WPA_KEY_INFO_TYPE_AES_128_CMAC; - else if (sm->pairwise_cipher == WPA_CIPHER_CCMP) + else if (sm->pairwise_cipher != WPA_CIPHER_TKIP) ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; else ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; @@ -190,14 +190,17 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, #endif /* CONFIG_IEEE80211R */ } if (res == 0) { + struct rsn_pmksa_cache_entry *sa = NULL; wpa_hexdump_key(MSG_DEBUG, "WPA: PMK from EAPOL state " "machines", sm->pmk, pmk_len); sm->pmk_len = pmk_len; if (sm->proto == WPA_PROTO_RSN && !wpa_key_mgmt_ft(sm->key_mgmt)) { - pmksa_cache_add(sm->pmksa, sm->pmk, pmk_len, - src_addr, sm->own_addr, - sm->network_ctx, sm->key_mgmt); + sa = pmksa_cache_add(sm->pmksa, + sm->pmk, pmk_len, + src_addr, sm->own_addr, + sm->network_ctx, + sm->key_mgmt); } if (!sm->cur_pmksa && pmkid && pmksa_cache_get(sm->pmksa, src_addr, pmkid, NULL)) @@ -207,6 +210,9 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, "PMKID"); abort_cached = 0; } + + if (!sm->cur_pmksa) + sm->cur_pmksa = sa; } else { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to get master session key from " @@ -350,7 +356,7 @@ static int wpa_derive_ptk(struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, struct wpa_ptk *ptk) { - size_t ptk_len = sm->pairwise_cipher == WPA_CIPHER_CCMP ? 48 : 64; + size_t ptk_len = sm->pairwise_cipher != WPA_CIPHER_TKIP ? 48 : 64; #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->key_mgmt)) return wpa_derive_ptk_ft(sm, src_addr, key, ptk, ptk_len); @@ -520,6 +526,11 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm, keylen = 16; rsclen = 6; break; + case WPA_CIPHER_GCMP: + alg = WPA_ALG_GCMP; + keylen = 16; + rsclen = 6; + break; case WPA_CIPHER_TKIP: alg = WPA_ALG_TKIP; keylen = 32; @@ -579,6 +590,14 @@ static int wpa_supplicant_check_group_cipher(struct wpa_sm *sm, *key_rsc_len = 6; *alg = WPA_ALG_CCMP; break; + case WPA_CIPHER_GCMP: + if (keylen != 16 || maxkeylen < 16) { + ret = -1; + break; + } + *key_rsc_len = 6; + *alg = WPA_ALG_GCMP; + break; case WPA_CIPHER_TKIP: if (keylen != 32 || maxkeylen < 32) { ret = -1; @@ -1125,6 +1144,14 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, goto failed; } break; + case WPA_CIPHER_GCMP: + if (keylen != 16) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Invalid GCMP key length %d (src=" MACSTR + ")", keylen, MAC2STR(sm->bssid)); + goto failed; + } + break; case WPA_CIPHER_TKIP: if (keylen != 32) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, @@ -1712,6 +1739,13 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, } else goto out; } + if (sm->pairwise_cipher == WPA_CIPHER_GCMP && + ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: GCMP is used, but EAPOL-Key " + "descriptor version (%d) is not 2", ver); + goto out; + } #ifdef CONFIG_PEERKEY for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) { @@ -1851,6 +1885,8 @@ static int wpa_cipher_bits(int cipher) switch (cipher) { case WPA_CIPHER_CCMP: return 128; + case WPA_CIPHER_GCMP: + return 128; case WPA_CIPHER_TKIP: return 256; case WPA_CIPHER_WEP104: @@ -1900,6 +1936,8 @@ static u32 wpa_cipher_suite(struct wpa_sm *sm, int cipher) case WPA_CIPHER_CCMP: return (sm->proto == WPA_PROTO_RSN ? RSN_CIPHER_SUITE_CCMP : WPA_CIPHER_SUITE_CCMP); + case WPA_CIPHER_GCMP: + return RSN_CIPHER_SUITE_GCMP; case WPA_CIPHER_TKIP: return (sm->proto == WPA_PROTO_RSN ? RSN_CIPHER_SUITE_TKIP : WPA_CIPHER_SUITE_TKIP); @@ -2659,3 +2697,125 @@ void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx) pmksa_cache_flush(sm->pmksa, network_ctx); #endif /* CONFIG_NO_WPA2 */ } + + +#ifdef CONFIG_IEEE80211V +int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf) +{ + struct wpa_gtk_data gd; +#ifdef CONFIG_IEEE80211W + struct wpa_igtk_kde igd; + u16 keyidx; +#endif /* CONFIG_IEEE80211W */ + u16 keyinfo; + u8 keylen; /* plaintext key len */ + u8 keydatalen; + u8 *key_rsc; + + os_memset(&gd, 0, sizeof(gd)); +#ifdef CONFIG_IEEE80211W + os_memset(&igd, 0, sizeof(igd)); +#endif /* CONFIG_IEEE80211W */ + + switch (sm->group_cipher) { + case WPA_CIPHER_CCMP: + keylen = 16; + gd.key_rsc_len = 6; + gd.alg = WPA_ALG_CCMP; + break; + case WPA_CIPHER_GCMP: + keylen = 16; + gd.key_rsc_len = 6; + gd.alg = WPA_ALG_GCMP; + break; + case WPA_CIPHER_TKIP: + keylen = 32; + gd.key_rsc_len = 6; + gd.alg = WPA_ALG_TKIP; + break; + case WPA_CIPHER_WEP104: + keylen = 13; + gd.key_rsc_len = 0; + gd.alg = WPA_ALG_WEP; + break; + case WPA_CIPHER_WEP40: + keylen = 5; + gd.key_rsc_len = 0; + gd.alg = WPA_ALG_WEP; + break; + default: + wpa_printf(MSG_DEBUG, "Unsupported group cipher suite"); + return -1; + } + + if (subelem_id == WNM_SLEEP_SUBELEM_GTK) { + key_rsc = buf + 5; + keyinfo = WPA_GET_LE16(buf+2); + keydatalen = buf[1] - 11 - 8; + gd.gtk_len = keylen; + if (gd.gtk_len != buf[4]) { + wpa_printf(MSG_DEBUG, "GTK len mismatch len %d vs %d", + gd.gtk_len, buf[4]); + return -1; + } + gd.keyidx = keyinfo & 0x03; /* B0 - B1 */ + gd.tx = wpa_supplicant_gtk_tx_bit_workaround( + sm, !!(keyinfo & WPA_KEY_INFO_TXRX)); + + if (keydatalen % 8) { + wpa_printf(MSG_DEBUG, "WPA: Unsupported AES-WRAP len " + "%d", keydatalen); + return -1; + } + + if (aes_unwrap(sm->ptk.kek, keydatalen / 8, buf + 13, gd.gtk)) + { + wpa_printf(MSG_WARNING, "WNM: AES unwrap failed - " + "could not decrypt GTK"); + return -1; + } + + wpa_hexdump_key(MSG_DEBUG, "Install GTK (WNM SLEEP)", + gd.gtk, gd.gtk_len); + if (wpa_supplicant_install_gtk(sm, &gd, key_rsc)) { + wpa_printf(MSG_DEBUG, "Failed to install the GTK in " + "WNM mode"); + return -1; + } +#ifdef CONFIG_IEEE80211W + } else if (subelem_id == WNM_SLEEP_SUBELEM_IGTK) { + if (buf[1] != 2 + 6 + WPA_IGTK_LEN + 8) { + wpa_printf(MSG_DEBUG, "WPA: Unsupported AES-WRAP len " + "%d", buf[1] - 2 - 6 - 8); + return -1; + } + os_memcpy(igd.keyid, buf + 2, 2); + os_memcpy(igd.pn, buf + 4, 6); + + keyidx = WPA_GET_LE16(igd.keyid); + + if (aes_unwrap(sm->ptk.kek, WPA_IGTK_LEN / 8, buf + 10, + igd.igtk)) { + wpa_printf(MSG_WARNING, "WNM: AES unwrap failed - " + "could not decrypr IGTK"); + return -1; + } + + wpa_hexdump_key(MSG_DEBUG, "Install IGTK (WNM SLEEP)", + igd.igtk, WPA_IGTK_LEN); + if (wpa_sm_set_key(sm, WPA_ALG_IGTK, broadcast_ether_addr, + keyidx, 0, igd.pn, sizeof(igd.pn), + igd.igtk, WPA_IGTK_LEN) < 0) { + wpa_printf(MSG_DEBUG, "Failed to install the IGTK in " + "WNM mode"); + return -1; + } +#endif /* CONFIG_IEEE80211W */ + } else { + wpa_printf(MSG_DEBUG, "Unknown element id"); + return -1; + } + + return 0; +} +#endif /* CONFIG_IEEE80211V */ diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h index a70b57b8..1077b5a0 100644 --- a/src/rsn_supp/wpa.h +++ b/src/rsn_supp/wpa.h @@ -366,4 +366,8 @@ void wpa_tdls_enable(struct wpa_sm *sm, int enabled); void wpa_tdls_disable_link(struct wpa_sm *sm, const u8 *addr); int wpa_tdls_is_external_setup(struct wpa_sm *sm); +#ifdef CONFIG_IEEE80211V +int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf); +#endif /* CONFIG_IEEE80211V */ + #endif /* WPA_H */ diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c index fec95449..bdf389b1 100644 --- a/src/rsn_supp/wpa_ft.c +++ b/src/rsn_supp/wpa_ft.c @@ -173,6 +173,8 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, /* Group Suite Selector */ if (sm->group_cipher == WPA_CIPHER_CCMP) RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); + else if (sm->group_cipher == WPA_CIPHER_GCMP) + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP); else if (sm->group_cipher == WPA_CIPHER_TKIP) RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); else { @@ -190,6 +192,8 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, /* Pairwise Suite List */ if (sm->pairwise_cipher == WPA_CIPHER_CCMP) RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); + else if (sm->pairwise_cipher == WPA_CIPHER_GCMP) + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP); else if (sm->pairwise_cipher == WPA_CIPHER_TKIP) RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); else { @@ -328,6 +332,10 @@ static int wpa_ft_install_ptk(struct wpa_sm *sm, const u8 *bssid) alg = WPA_ALG_CCMP; keylen = 16; break; + case WPA_CIPHER_GCMP: + alg = WPA_ALG_GCMP; + keylen = 16; + break; case WPA_CIPHER_TKIP: alg = WPA_ALG_TKIP; keylen = 32; @@ -483,7 +491,7 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, sm->pmk_r1_name, WPA_PMK_NAME_LEN); bssid = target_ap; - ptk_len = sm->pairwise_cipher == WPA_CIPHER_CCMP ? 48 : 64; + ptk_len = sm->pairwise_cipher != WPA_CIPHER_TKIP ? 48 : 64; wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, ftie->anonce, sm->own_addr, bssid, sm->pmk_r1_name, (u8 *) &sm->ptk, ptk_len, ptk_name); @@ -577,6 +585,11 @@ static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem, rsc_len = 6; alg = WPA_ALG_CCMP; break; + case WPA_CIPHER_GCMP: + keylen = 16; + rsc_len = 6; + alg = WPA_ALG_GCMP; + break; case WPA_CIPHER_TKIP: keylen = 32; rsc_len = 6; diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c index 56d564cd..16268d56 100644 --- a/src/rsn_supp/wpa_ie.c +++ b/src/rsn_supp/wpa_ie.c @@ -132,6 +132,8 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, if (group_cipher == WPA_CIPHER_CCMP) { RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); + } else if (group_cipher == WPA_CIPHER_GCMP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP); } else if (group_cipher == WPA_CIPHER_TKIP) { RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); } else if (group_cipher == WPA_CIPHER_WEP104) { @@ -149,6 +151,8 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, *pos++ = 0; if (pairwise_cipher == WPA_CIPHER_CCMP) { RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); + } else if (pairwise_cipher == WPA_CIPHER_GCMP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP); } else if (pairwise_cipher == WPA_CIPHER_TKIP) { RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); } else if (pairwise_cipher == WPA_CIPHER_NONE) { diff --git a/src/utils/common.c b/src/utils/common.c index 26fea1bf..e6369849 100644 --- a/src/utils/common.c +++ b/src/utils/common.c @@ -344,6 +344,135 @@ TCHAR * wpa_strdup_tchar(const char *str) #endif /* CONFIG_NATIVE_WINDOWS */ +void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len) +{ + char *end = txt + maxlen; + size_t i; + + for (i = 0; i < len; i++) { + if (txt + 4 > end) + break; + + switch (data[i]) { + case '\"': + *txt++ = '\\'; + *txt++ = '\"'; + break; + case '\\': + *txt++ = '\\'; + *txt++ = '\\'; + break; + case '\e': + *txt++ = '\\'; + *txt++ = 'e'; + break; + case '\n': + *txt++ = '\\'; + *txt++ = 'n'; + break; + case '\r': + *txt++ = '\\'; + *txt++ = 'r'; + break; + case '\t': + *txt++ = '\\'; + *txt++ = 't'; + break; + default: + if (data[i] >= 32 && data[i] <= 127) { + *txt++ = data[i]; + } else { + txt += os_snprintf(txt, end - txt, "\\x%02x", + data[i]); + } + break; + } + } + + *txt = '\0'; +} + + +size_t printf_decode(u8 *buf, size_t maxlen, const char *str) +{ + const char *pos = str; + size_t len = 0; + int val; + + while (*pos) { + if (len == maxlen) + break; + switch (*pos) { + case '\\': + pos++; + switch (*pos) { + case '\\': + buf[len++] = '\\'; + pos++; + break; + case '"': + buf[len++] = '"'; + pos++; + break; + case 'n': + buf[len++] = '\n'; + pos++; + break; + case 'r': + buf[len++] = '\r'; + pos++; + break; + case 't': + buf[len++] = '\t'; + pos++; + break; + case 'e': + buf[len++] = '\e'; + pos++; + break; + case 'x': + pos++; + val = hex2byte(pos); + if (val < 0) { + val = hex2num(*pos); + if (val < 0) + break; + buf[len++] = val; + pos++; + } else { + buf[len++] = val; + pos += 2; + } + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + val = *pos++ - '0'; + if (*pos >= '0' && *pos <= '7') + val = val * 8 + (*pos++ - '0'); + if (*pos >= '0' && *pos <= '7') + val = val * 8 + (*pos++ - '0'); + buf[len++] = val; + break; + default: + break; + } + break; + default: + buf[len++] = *pos++; + break; + } + } + + return len; +} + + /** * wpa_ssid_txt - Convert SSID to a printable string * @ssid: SSID (32-octet string) @@ -360,19 +489,14 @@ TCHAR * wpa_strdup_tchar(const char *str) */ const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len) { - static char ssid_txt[33]; - char *pos; - - if (ssid_len > 32) - ssid_len = 32; - os_memcpy(ssid_txt, ssid, ssid_len); - ssid_txt[ssid_len] = '\0'; - for (pos = ssid_txt; *pos != '\0'; pos++) { -#ifndef WPA_UNICODE_SSID - if ((u8) *pos < 32 || (u8) *pos >= 127) - *pos = '_'; -#endif + static char ssid_txt[32 * 4 + 1]; + + if (ssid == NULL) { + ssid_txt[0] = '\0'; + return ssid_txt; } + + printf_encode(ssid_txt, sizeof(ssid_txt), ssid, ssid_len); return ssid_txt; } @@ -381,3 +505,108 @@ void * __hide_aliasing_typecast(void *foo) { return foo; } + + +char * wpa_config_parse_string(const char *value, size_t *len) +{ + if (*value == '"') { + const char *pos; + char *str; + value++; + pos = os_strrchr(value, '"'); + if (pos == NULL || pos[1] != '\0') + return NULL; + *len = pos - value; + str = os_malloc(*len + 1); + if (str == NULL) + return NULL; + os_memcpy(str, value, *len); + str[*len] = '\0'; + return str; + } else if (*value == 'P' && value[1] == '"') { + const char *pos; + char *tstr, *str; + size_t tlen; + value += 2; + pos = os_strrchr(value, '"'); + if (pos == NULL || pos[1] != '\0') + return NULL; + tlen = pos - value; + tstr = os_malloc(tlen + 1); + if (tstr == NULL) + return NULL; + os_memcpy(tstr, value, tlen); + tstr[tlen] = '\0'; + + str = os_malloc(tlen + 1); + if (str == NULL) { + os_free(tstr); + return NULL; + } + + *len = printf_decode((u8 *) str, tlen + 1, tstr); + os_free(tstr); + + return str; + } else { + u8 *str; + size_t tlen, hlen = os_strlen(value); + if (hlen & 1) + return NULL; + tlen = hlen / 2; + str = os_malloc(tlen + 1); + if (str == NULL) + return NULL; + if (hexstr2bin(value, str, tlen)) { + os_free(str); + return NULL; + } + str[tlen] = '\0'; + *len = tlen; + return (char *) str; + } +} + + +int is_hex(const u8 *data, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) { + if (data[i] < 32 || data[i] >= 127) + return 1; + } + return 0; +} + + +size_t merge_byte_arrays(u8 *res, size_t res_len, + const u8 *src1, size_t src1_len, + const u8 *src2, size_t src2_len) +{ + size_t len = 0; + + os_memset(res, 0, res_len); + + if (src1) { + if (src1_len >= res_len) { + os_memcpy(res, src1, res_len); + return res_len; + } + + os_memcpy(res, src1, src1_len); + len += src1_len; + } + + if (src2) { + if (len + src2_len >= res_len) { + os_memcpy(res + len, src2, res_len - len); + return res_len; + } + + os_memcpy(res + len, src2, src2_len); + len += src2_len; + } + + return len; +} diff --git a/src/utils/common.h b/src/utils/common.h index 7f115efe..5fc916c0 100644 --- a/src/utils/common.h +++ b/src/utils/common.h @@ -441,8 +441,17 @@ TCHAR * wpa_strdup_tchar(const char *str); #define wpa_strdup_tchar(s) strdup((s)) #endif /* CONFIG_NATIVE_WINDOWS */ +void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len); +size_t printf_decode(u8 *buf, size_t maxlen, const char *str); + const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len); +char * wpa_config_parse_string(const char *value, size_t *len); +int is_hex(const u8 *data, size_t len); +size_t merge_byte_arrays(u8 *res, size_t res_len, + const u8 *src1, size_t src1_len, + const u8 *src2, size_t src2_len); + static inline int is_zero_ether_addr(const u8 *a) { return !(a[0] | a[1] | a[2] | a[3] | a[4] | a[5]); diff --git a/src/utils/edit.c b/src/utils/edit.c index 280d2447..b01e08df 100644 --- a/src/utils/edit.c +++ b/src/utils/edit.c @@ -20,6 +20,7 @@ static int cmdbuf_pos = 0; static int cmdbuf_len = 0; static char currbuf[CMD_BUF_LEN]; static int currbuf_valid = 0; +static const char *ps2 = NULL; #define HISTORY_MAX 100 @@ -47,7 +48,7 @@ void edit_clear_line(void) { int i; putchar('\r'); - for (i = 0; i < cmdbuf_len + 2; i++) + for (i = 0; i < cmdbuf_len + 2 + (ps2 ? (int) os_strlen(ps2) : 0); i++) putchar(' '); } @@ -346,7 +347,7 @@ static void process_cmd(void) { if (cmdbuf_len == 0) { - printf("\n> "); + printf("\n%s> ", ps2 ? ps2 : ""); fflush(stdout); return; } @@ -356,7 +357,7 @@ static void process_cmd(void) cmdbuf_pos = 0; cmdbuf_len = 0; edit_cmd_cb(edit_cb_ctx, cmdbuf); - printf("> "); + printf("%s> ", ps2 ? ps2 : ""); fflush(stdout); } @@ -1112,7 +1113,7 @@ static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx) int edit_init(void (*cmd_cb)(void *ctx, char *cmd), void (*eof_cb)(void *ctx), char ** (*completion_cb)(void *ctx, const char *cmd, int pos), - void *ctx, const char *history_file) + void *ctx, const char *history_file, const char *ps) { currbuf[0] = '\0'; dl_list_init(&history_list); @@ -1132,7 +1133,8 @@ int edit_init(void (*cmd_cb)(void *ctx, char *cmd), eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL); - printf("> "); + ps2 = ps; + printf("%s> ", ps2 ? ps2 : ""); fflush(stdout); return 0; @@ -1161,11 +1163,11 @@ void edit_redraw(void) { char tmp; cmdbuf[cmdbuf_len] = '\0'; - printf("\r> %s", cmdbuf); + printf("\r%s> %s", ps2 ? ps2 : "", cmdbuf); if (cmdbuf_pos != cmdbuf_len) { tmp = cmdbuf[cmdbuf_pos]; cmdbuf[cmdbuf_pos] = '\0'; - printf("\r> %s", cmdbuf); + printf("\r%s> %s", ps2 ? ps2 : "", cmdbuf); cmdbuf[cmdbuf_pos] = tmp; } fflush(stdout); diff --git a/src/utils/edit.h b/src/utils/edit.h index e8d04e7f..ad27f1c7 100644 --- a/src/utils/edit.h +++ b/src/utils/edit.h @@ -12,7 +12,7 @@ int edit_init(void (*cmd_cb)(void *ctx, char *cmd), void (*eof_cb)(void *ctx), char ** (*completion_cb)(void *ctx, const char *cmd, int pos), - void *ctx, const char *history_file); + void *ctx, const char *history_file, const char *ps); void edit_deinit(const char *history_file, int (*filter_cb)(void *ctx, const char *cmd)); void edit_clear_line(void); diff --git a/src/utils/edit_readline.c b/src/utils/edit_readline.c index c9ff0e6d..add26fa3 100644 --- a/src/utils/edit_readline.c +++ b/src/utils/edit_readline.c @@ -112,7 +112,7 @@ static void readline_cmd_handler(char *cmd) int edit_init(void (*cmd_cb)(void *ctx, char *cmd), void (*eof_cb)(void *ctx), char ** (*completion_cb)(void *ctx, const char *cmd, int pos), - void *ctx, const char *history_file) + void *ctx, const char *history_file, const char *ps) { edit_cb_ctx = ctx; edit_cmd_cb = cmd_cb; @@ -127,6 +127,17 @@ int edit_init(void (*cmd_cb)(void *ctx, char *cmd), eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL); + if (ps) { + size_t blen = os_strlen(ps) + 3; + char *ps2 = os_malloc(blen); + if (ps2) { + os_snprintf(ps2, blen, "%s> ", ps); + rl_callback_handler_install(ps2, readline_cmd_handler); + os_free(ps2); + return 0; + } + } + rl_callback_handler_install("> ", readline_cmd_handler); return 0; @@ -136,6 +147,9 @@ int edit_init(void (*cmd_cb)(void *ctx, char *cmd), void edit_deinit(const char *history_file, int (*filter_cb)(void *ctx, const char *cmd)) { + rl_set_prompt(""); + rl_replace_line("", 0); + rl_redisplay(); rl_callback_handler_remove(); readline_free_completions(); diff --git a/src/utils/edit_simple.c b/src/utils/edit_simple.c index 0e3637cb..a095ea6a 100644 --- a/src/utils/edit_simple.c +++ b/src/utils/edit_simple.c @@ -16,6 +16,7 @@ #define CMD_BUF_LEN 256 static char cmdbuf[CMD_BUF_LEN]; static int cmdbuf_pos = 0; +static const char *ps2 = NULL; static void *edit_cb_ctx; static void (*edit_cmd_cb)(void *ctx, char *cmd); @@ -41,7 +42,7 @@ static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx) cmdbuf[cmdbuf_pos] = '\0'; cmdbuf_pos = 0; edit_cmd_cb(edit_cb_ctx, cmdbuf); - printf("> "); + printf("%s> ", ps2 ? ps2 : ""); fflush(stdout); return; } @@ -57,14 +58,15 @@ static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx) int edit_init(void (*cmd_cb)(void *ctx, char *cmd), void (*eof_cb)(void *ctx), char ** (*completion_cb)(void *ctx, const char *cmd, int pos), - void *ctx, const char *history_file) + void *ctx, const char *history_file, const char *ps) { edit_cb_ctx = ctx; edit_cmd_cb = cmd_cb; edit_eof_cb = eof_cb; eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL); + ps2 = ps; - printf("> "); + printf("%s> ", ps2 ? ps2 : ""); fflush(stdout); return 0; diff --git a/src/utils/eloop.c b/src/utils/eloop.c index bb324011..d01ae64f 100644 --- a/src/utils/eloop.c +++ b/src/utils/eloop.c @@ -153,8 +153,8 @@ static int eloop_sock_table_add_sock(struct eloop_sock_table *table, #ifdef CONFIG_ELOOP_POLL if (new_max_sock >= eloop.max_pollfd_map) { struct pollfd **nmap; - nmap = os_realloc(eloop.pollfds_map, sizeof(struct pollfd *) * - (new_max_sock + 50)); + nmap = os_realloc_array(eloop.pollfds_map, new_max_sock + 50, + sizeof(struct pollfd *)); if (nmap == NULL) return -1; @@ -165,7 +165,8 @@ static int eloop_sock_table_add_sock(struct eloop_sock_table *table, if (eloop.count + 1 > eloop.max_poll_fds) { struct pollfd *n; int nmax = eloop.count + 1 + 50; - n = os_realloc(eloop.pollfds, sizeof(struct pollfd) * nmax); + n = os_realloc_array(eloop.pollfds, nmax, + sizeof(struct pollfd)); if (n == NULL) return -1; @@ -175,9 +176,8 @@ static int eloop_sock_table_add_sock(struct eloop_sock_table *table, #endif /* CONFIG_ELOOP_POLL */ eloop_trace_sock_remove_ref(table); - tmp = (struct eloop_sock *) - os_realloc(table->table, - (table->count + 1) * sizeof(struct eloop_sock)); + tmp = os_realloc_array(table->table, table->count + 1, + sizeof(struct eloop_sock)); if (tmp == NULL) return -1; @@ -639,10 +639,8 @@ int eloop_register_signal(int sig, eloop_signal_handler handler, { struct eloop_signal *tmp; - tmp = (struct eloop_signal *) - os_realloc(eloop.signals, - (eloop.signal_count + 1) * - sizeof(struct eloop_signal)); + tmp = os_realloc_array(eloop.signals, eloop.signal_count + 1, + sizeof(struct eloop_signal)); if (tmp == NULL) return -1; diff --git a/src/utils/eloop_win.c b/src/utils/eloop_win.c index 1cf55799..1fafeb2d 100644 --- a/src/utils/eloop_win.c +++ b/src/utils/eloop_win.c @@ -98,8 +98,8 @@ static int eloop_prepare_handles(void) if (eloop.num_handles > eloop.reader_count + eloop.event_count + 8) return 0; - n = os_realloc(eloop.handles, - eloop.num_handles * 2 * sizeof(eloop.handles[0])); + n = os_realloc_array(eloop.handles, eloop.num_handles * 2, + sizeof(eloop.handles[0])); if (n == NULL) return -1; eloop.handles = n; @@ -128,8 +128,8 @@ int eloop_register_read_sock(int sock, eloop_sock_handler handler, WSACloseEvent(event); return -1; } - tmp = os_realloc(eloop.readers, - (eloop.reader_count + 1) * sizeof(struct eloop_sock)); + tmp = os_realloc_array(eloop.readers, eloop.reader_count + 1, + sizeof(struct eloop_sock)); if (tmp == NULL) { WSAEventSelect(sock, event, 0); WSACloseEvent(event); @@ -191,8 +191,8 @@ int eloop_register_event(void *event, size_t event_size, if (eloop_prepare_handles()) return -1; - tmp = os_realloc(eloop.events, - (eloop.event_count + 1) * sizeof(struct eloop_event)); + tmp = os_realloc_array(eloop.events, eloop.event_count + 1, + sizeof(struct eloop_event)); if (tmp == NULL) return -1; @@ -392,9 +392,8 @@ int eloop_register_signal(int sig, eloop_signal_handler handler, { struct eloop_signal *tmp; - tmp = os_realloc(eloop.signals, - (eloop.signal_count + 1) * - sizeof(struct eloop_signal)); + tmp = os_realloc_array(eloop.signals, eloop.signal_count + 1, + sizeof(struct eloop_signal)); if (tmp == NULL) return -1; diff --git a/src/utils/ext_password.c b/src/utils/ext_password.c new file mode 100644 index 00000000..06131197 --- /dev/null +++ b/src/utils/ext_password.c @@ -0,0 +1,116 @@ +/* + * External password backend + * Copyright (c) 2012, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#ifdef __linux__ +#include <sys/mman.h> +#endif /* __linux__ */ + +#include "common.h" +#include "ext_password_i.h" + + +#ifdef CONFIG_EXT_PASSWORD_TEST +extern struct ext_password_backend ext_password_test; +#endif /* CONFIG_EXT_PASSWORD_TEST */ + +static const struct ext_password_backend *backends[] = { +#ifdef CONFIG_EXT_PASSWORD_TEST + &ext_password_test, +#endif /* CONFIG_EXT_PASSWORD_TEST */ + NULL +}; + +struct ext_password_data { + const struct ext_password_backend *backend; + void *priv; +}; + + +struct ext_password_data * ext_password_init(const char *backend, + const char *params) +{ + struct ext_password_data *data; + int i; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + + for (i = 0; backends[i]; i++) { + if (os_strcmp(backends[i]->name, backend) == 0) { + data->backend = backends[i]; + break; + } + } + + if (!data->backend) { + os_free(data); + return NULL; + } + + data->priv = data->backend->init(params); + if (data->priv == NULL) { + os_free(data); + return NULL; + } + + return data; +} + + +void ext_password_deinit(struct ext_password_data *data) +{ + if (data && data->backend && data->priv) + data->backend->deinit(data->priv); + os_free(data); +} + + +struct wpabuf * ext_password_get(struct ext_password_data *data, + const char *name) +{ + if (data == NULL) + return NULL; + return data->backend->get(data->priv, name); +} + + +struct wpabuf * ext_password_alloc(size_t len) +{ + struct wpabuf *buf; + + buf = wpabuf_alloc(len); + if (buf == NULL) + return NULL; + +#ifdef __linux__ + if (mlock(wpabuf_head(buf), wpabuf_len(buf)) < 0) { + wpa_printf(MSG_ERROR, "EXT PW: mlock failed: %s", + strerror(errno)); + } +#endif /* __linux__ */ + + return buf; +} + + +void ext_password_free(struct wpabuf *pw) +{ + if (pw == NULL) + return; + os_memset(wpabuf_mhead(pw), 0, wpabuf_len(pw)); +#ifdef __linux__ + if (munlock(wpabuf_head(pw), wpabuf_len(pw)) < 0) { + wpa_printf(MSG_ERROR, "EXT PW: munlock failed: %s", + strerror(errno)); + } +#endif /* __linux__ */ + wpabuf_free(pw); +} diff --git a/src/utils/ext_password.h b/src/utils/ext_password.h new file mode 100644 index 00000000..e3e46ea0 --- /dev/null +++ b/src/utils/ext_password.h @@ -0,0 +1,33 @@ +/* + * External password backend + * Copyright (c) 2012, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EXT_PASSWORD_H +#define EXT_PASSWORD_H + +struct ext_password_data; + +#ifdef CONFIG_EXT_PASSWORD + +struct ext_password_data * ext_password_init(const char *backend, + const char *params); +void ext_password_deinit(struct ext_password_data *data); + +struct wpabuf * ext_password_get(struct ext_password_data *data, + const char *name); +void ext_password_free(struct wpabuf *pw); + +#else /* CONFIG_EXT_PASSWORD */ + +#define ext_password_init(b, p) ((void *) 1) +#define ext_password_deinit(d) do { } while (0) +#define ext_password_get(d, n) (NULL) +#define ext_password_free(p) do { } while (0) + +#endif /* CONFIG_EXT_PASSWORD */ + +#endif /* EXT_PASSWORD_H */ diff --git a/src/utils/ext_password_i.h b/src/utils/ext_password_i.h new file mode 100644 index 00000000..043e7312 --- /dev/null +++ b/src/utils/ext_password_i.h @@ -0,0 +1,23 @@ +/* + * External password backend - internal definitions + * Copyright (c) 2012, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EXT_PASSWORD_I_H +#define EXT_PASSWORD_I_H + +#include "ext_password.h" + +struct ext_password_backend { + const char *name; + void * (*init)(const char *params); + void (*deinit)(void *ctx); + struct wpabuf * (*get)(void *ctx, const char *name); +}; + +struct wpabuf * ext_password_alloc(size_t len); + +#endif /* EXT_PASSWORD_I_H */ diff --git a/src/utils/ext_password_test.c b/src/utils/ext_password_test.c new file mode 100644 index 00000000..3801bb85 --- /dev/null +++ b/src/utils/ext_password_test.c @@ -0,0 +1,90 @@ +/* + * External password backend + * Copyright (c) 2012, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "ext_password_i.h" + + +struct ext_password_test_data { + char *params; +}; + + +static void * ext_password_test_init(const char *params) +{ + struct ext_password_test_data *data; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + + if (params) + data->params = os_strdup(params); + + return data; +} + + +static void ext_password_test_deinit(void *ctx) +{ + struct ext_password_test_data *data = ctx; + + os_free(data->params); + os_free(data); +} + + +static struct wpabuf * ext_password_test_get(void *ctx, const char *name) +{ + struct ext_password_test_data *data = ctx; + char *pos, *pos2; + size_t nlen; + + wpa_printf(MSG_DEBUG, "EXT PW TEST: get(%s)", name); + + pos = data->params; + if (pos == NULL) + return NULL; + nlen = os_strlen(name); + + while (pos && *pos) { + if (os_strncmp(pos, name, nlen) == 0 && pos[nlen] == '=') { + struct wpabuf *buf; + pos += nlen + 1; + pos2 = pos; + while (*pos2 != '|' && *pos2 != '\0') + pos2++; + buf = ext_password_alloc(pos2 - pos); + if (buf == NULL) + return NULL; + wpabuf_put_data(buf, pos, pos2 - pos); + wpa_hexdump_ascii_key(MSG_DEBUG, "EXT PW TEST: value", + wpabuf_head(buf), + wpabuf_len(buf)); + return buf; + } + + pos = os_strchr(pos + 1, '|'); + if (pos) + pos++; + } + + wpa_printf(MSG_DEBUG, "EXT PW TEST: get(%s) - not found", name); + + return NULL; +} + + +const struct ext_password_backend ext_password_test = { + .name = "test", + .init = ext_password_test_init, + .deinit = ext_password_test_deinit, + .get = ext_password_test_get, +}; diff --git a/src/utils/os.h b/src/utils/os.h index 3058b882..ad208341 100644 --- a/src/utils/os.h +++ b/src/utils/os.h @@ -180,6 +180,25 @@ char * os_readfile(const char *name, size_t *len); */ void * os_zalloc(size_t size); +/** + * os_calloc - Allocate and zero memory for an array + * @nmemb: Number of members in the array + * @size: Number of bytes in each member + * Returns: Pointer to allocated and zeroed memory or %NULL on failure + * + * This function can be used as a wrapper for os_zalloc(nmemb * size) when an + * allocation is used for an array. The main benefit over os_zalloc() is in + * having an extra check to catch integer overflows in multiplication. + * + * Caller is responsible for freeing the returned buffer with os_free(). + */ +static inline void * os_calloc(size_t nmemb, size_t size) +{ + if (size && nmemb > (~(size_t) 0) / size) + return NULL; + return os_zalloc(nmemb * size); +} + /* * The following functions are wrapper for standard ANSI C or POSIX functions. @@ -467,6 +486,14 @@ char * os_strdup(const char *s); #endif /* OS_NO_C_LIB_DEFINES */ +static inline void * os_realloc_array(void *ptr, size_t nmemb, size_t size) +{ + if (size && nmemb > (~(size_t) 0) / size) + return NULL; + return os_realloc(ptr, nmemb * size); +} + + /** * os_strlcpy - Copy a string with size bound and NUL-termination * @dest: Destination diff --git a/src/utils/wpabuf.c b/src/utils/wpabuf.c index b007e669..b257b365 100644 --- a/src/utils/wpabuf.c +++ b/src/utils/wpabuf.c @@ -1,6 +1,6 @@ /* * Dynamic data buffer - * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007-2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -68,12 +68,12 @@ int wpabuf_resize(struct wpabuf **_buf, size_t add_len) if (buf->used + add_len > buf->size) { unsigned char *nbuf; - if (buf->ext_data) { - nbuf = os_realloc(buf->ext_data, buf->used + add_len); + if (buf->flags & WPABUF_FLAG_EXT_DATA) { + nbuf = os_realloc(buf->buf, buf->used + add_len); if (nbuf == NULL) return -1; os_memset(nbuf + buf->used, 0, add_len); - buf->ext_data = nbuf; + buf->buf = nbuf; } else { #ifdef WPA_TRACE nbuf = os_realloc(trace, sizeof(struct wpabuf_trace) + @@ -95,6 +95,7 @@ int wpabuf_resize(struct wpabuf **_buf, size_t add_len) os_memset(nbuf + sizeof(struct wpabuf) + buf->used, 0, add_len); #endif /* WPA_TRACE */ + buf->buf = (u8 *) (buf + 1); *_buf = buf; } buf->size = buf->used + add_len; @@ -126,6 +127,7 @@ struct wpabuf * wpabuf_alloc(size_t len) #endif /* WPA_TRACE */ buf->size = len; + buf->buf = (u8 *) (buf + 1); return buf; } @@ -148,7 +150,8 @@ struct wpabuf * wpabuf_alloc_ext_data(u8 *data, size_t len) buf->size = len; buf->used = len; - buf->ext_data = data; + buf->buf = data; + buf->flags |= WPABUF_FLAG_EXT_DATA; return buf; } @@ -189,12 +192,14 @@ void wpabuf_free(struct wpabuf *buf) wpa_trace_show("wpabuf_free magic mismatch"); abort(); } - os_free(buf->ext_data); + if (buf->flags & WPABUF_FLAG_EXT_DATA) + os_free(buf->buf); os_free(trace); #else /* WPA_TRACE */ if (buf == NULL) return; - os_free(buf->ext_data); + if (buf->flags & WPABUF_FLAG_EXT_DATA) + os_free(buf->buf); os_free(buf); #endif /* WPA_TRACE */ } diff --git a/src/utils/wpabuf.h b/src/utils/wpabuf.h index 8c36b402..dbce925c 100644 --- a/src/utils/wpabuf.h +++ b/src/utils/wpabuf.h @@ -1,6 +1,6 @@ /* * Dynamic data buffer - * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007-2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -9,6 +9,9 @@ #ifndef WPABUF_H #define WPABUF_H +/* wpabuf::buf is a pointer to external data */ +#define WPABUF_FLAG_EXT_DATA BIT(0) + /* * Internal data structure for wpabuf. Please do not touch this directly from * elsewhere. This is only defined in header file to allow inline functions @@ -17,8 +20,8 @@ struct wpabuf { size_t size; /* total size of the allocated buffer */ size_t used; /* length of data in the buffer */ - u8 *ext_data; /* pointer to external data; NULL if data follows - * struct wpabuf */ + u8 *buf; /* pointer to the head of the buffer */ + unsigned int flags; /* optionally followed by the allocated buffer */ }; @@ -72,9 +75,7 @@ static inline size_t wpabuf_tailroom(const struct wpabuf *buf) */ static inline const void * wpabuf_head(const struct wpabuf *buf) { - if (buf->ext_data) - return buf->ext_data; - return buf + 1; + return buf->buf; } static inline const u8 * wpabuf_head_u8(const struct wpabuf *buf) @@ -89,9 +90,7 @@ static inline const u8 * wpabuf_head_u8(const struct wpabuf *buf) */ static inline void * wpabuf_mhead(struct wpabuf *buf) { - if (buf->ext_data) - return buf->ext_data; - return buf + 1; + return buf->buf; } static inline u8 * wpabuf_mhead_u8(struct wpabuf *buf) @@ -150,7 +149,8 @@ static inline void wpabuf_put_buf(struct wpabuf *dst, static inline void wpabuf_set(struct wpabuf *buf, const void *data, size_t len) { - buf->ext_data = (u8 *) data; + buf->buf = (u8 *) data; + buf->flags = WPABUF_FLAG_EXT_DATA; buf->size = buf->used = len; } diff --git a/src/wps/wps.c b/src/wps/wps.c index 5453962a..4c2322df 100644 --- a/src/wps/wps.c +++ b/src/wps/wps.c @@ -287,7 +287,8 @@ int wps_is_selected_pin_registrar(const struct wpabuf *msg) * @msg: WPS IE contents from Beacon or Probe Response frame * @addr: MAC address to search for * @ver1_compat: Whether to use version 1 compatibility mode - * Returns: 1 if address is authorized, 0 if not + * Returns: 2 if the specified address is explicit authorized, 1 if address is + * authorized (broadcast), 0 if not */ int wps_is_addr_authorized(const struct wpabuf *msg, const u8 *addr, int ver1_compat) @@ -313,8 +314,9 @@ int wps_is_addr_authorized(const struct wpabuf *msg, const u8 *addr, pos = attr.authorized_macs; for (i = 0; i < attr.authorized_macs_len / ETH_ALEN; i++) { - if (os_memcmp(pos, addr, ETH_ALEN) == 0 || - os_memcmp(pos, bcast, ETH_ALEN) == 0) + if (os_memcmp(pos, addr, ETH_ALEN) == 0) + return 2; + if (os_memcmp(pos, bcast, ETH_ALEN) == 0) return 1; pos += ETH_ALEN; } diff --git a/src/wps/wps_enrollee.c b/src/wps/wps_enrollee.c index da0c101b..389aa84e 100644 --- a/src/wps/wps_enrollee.c +++ b/src/wps/wps_enrollee.c @@ -1150,7 +1150,7 @@ static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps, return WPS_FAILURE; if (attr.enrollee_nonce == NULL || - os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) { + os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce"); return WPS_FAILURE; } @@ -1242,14 +1242,14 @@ static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps, } if (attr.registrar_nonce == NULL || - os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0)) + os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce"); return WPS_FAILURE; } if (attr.enrollee_nonce == NULL || - os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) { + os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce"); return WPS_FAILURE; } @@ -1289,7 +1289,7 @@ static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps, } if (attr.registrar_nonce == NULL || - os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0)) + os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce"); wpa_hexdump(MSG_DEBUG, "WPS: Received Registrar Nonce", @@ -1300,7 +1300,7 @@ static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps, } if (attr.enrollee_nonce == NULL || - os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) { + os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce"); wpa_hexdump(MSG_DEBUG, "WPS: Received Enrollee Nonce", attr.enrollee_nonce, WPS_NONCE_LEN); diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c index 44bb0063..53684d69 100644 --- a/src/wps/wps_registrar.c +++ b/src/wps/wps_registrar.c @@ -2854,7 +2854,7 @@ static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps, if (*attr.msg_type != WPS_M1 && (attr.registrar_nonce == NULL || os_memcmp(wps->nonce_r, attr.registrar_nonce, - WPS_NONCE_LEN != 0))) { + WPS_NONCE_LEN) != 0)) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce"); return WPS_FAILURE; } @@ -2950,14 +2950,14 @@ static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps, #endif /* CONFIG_WPS_UPNP */ if (attr.registrar_nonce == NULL || - os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0)) + os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce"); return WPS_FAILURE; } if (attr.enrollee_nonce == NULL || - os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) { + os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce"); return WPS_FAILURE; } @@ -3019,14 +3019,14 @@ static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps, #endif /* CONFIG_WPS_UPNP */ if (attr.registrar_nonce == NULL || - os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0)) + os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce"); return WPS_FAILURE; } if (attr.enrollee_nonce == NULL || - os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) { + os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce"); return WPS_FAILURE; } @@ -3105,14 +3105,14 @@ static enum wps_process_res wps_process_wsc_done(struct wps_data *wps, #endif /* CONFIG_WPS_UPNP */ if (attr.registrar_nonce == NULL || - os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0)) + os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce"); return WPS_FAILURE; } if (attr.enrollee_nonce == NULL || - os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) { + os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce"); return WPS_FAILURE; } diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk index 295a897a..eae00d83 100644 --- a/wpa_supplicant/Android.mk +++ b/wpa_supplicant/Android.mk @@ -72,6 +72,11 @@ ifdef CONFIG_DRIVER_NL80211 INCLUDES += external/libnl-headers endif +ifdef CONFIG_FIPS +CONFIG_NO_RANDOM_POOL= +CONFIG_OPENSSL_CMAC=y +endif + OBJS = config.c OBJS += notify.c OBJS += bss.c @@ -184,6 +189,11 @@ NEED_SHA256=y NEED_AES_OMAC1=y endif +ifdef CONFIG_IEEE80211V +L_CFLAGS += -DCONFIG_IEEE80211V +OBJS += wnm_sta.c +endif + ifdef CONFIG_TDLS L_CFLAGS += -DCONFIG_TDLS OBJS += src/rsn_supp/tdls.c @@ -244,6 +254,17 @@ L_CFLAGS += -DCONFIG_P2P_STRICT endif endif +ifdef CONFIG_WIFI_DISPLAY +L_CFLAGS += -DCONFIG_WIFI_DISPLAY +OBJS += wifi_display.c +endif + +ifdef CONFIG_HS20 +OBJS += hs20_supplicant.c +L_CFLAGS += -DCONFIG_HS20 +CONFIG_INTERWORKING=y +endif + ifdef CONFIG_INTERWORKING OBJS += interworking.c L_CFLAGS += -DCONFIG_INTERWORKING @@ -734,6 +755,10 @@ ifdef CONFIG_IEEE80211N L_CFLAGS += -DCONFIG_IEEE80211N endif +ifdef CONFIG_WNM +L_CFLAGS += -DCONFIG_WNM +endif + ifdef NEED_AP_MLME OBJS += src/ap/wmm.c OBJS += src/ap/ap_list.c @@ -746,6 +771,12 @@ L_CFLAGS += -DEAP_SERVER_WSC OBJS += src/ap/wps_hostapd.c OBJS += src/eap_server/eap_server_wsc.c endif +ifdef CONFIG_INTERWORKING +OBJS += src/ap/gas_serv.c +endif +ifdef CONFIG_HS20 +OBJS += src/ap/hs20.c +endif endif ifdef NEED_RSN_AUTHENTICATOR @@ -844,8 +875,10 @@ NEED_DES=y # Shared TLS functions (needed for EAP_TLS, EAP_PEAP, EAP_TTLS, and EAP_FAST) OBJS += src/eap_peer/eap_tls_common.c OBJS_h += src/eap_server/eap_server_tls_common.c +ifndef CONFIG_FIPS NEED_TLS_PRF=y endif +endif ifndef CONFIG_TLS CONFIG_TLS=openssl @@ -855,6 +888,11 @@ ifdef CONFIG_TLSV11 L_CFLAGS += -DCONFIG_TLSV11 endif +ifdef CONFIG_TLSV12 +L_CFLAGS += -DCONFIG_TLSV12 +NEED_SHA256=y +endif + ifeq ($(CONFIG_TLS), openssl) ifdef TLS_FUNCS L_CFLAGS += -DEAP_TLS_OPENSSL @@ -939,6 +977,9 @@ OBJS += src/tls/pkcs8.c NEED_SHA256=y NEED_BASE64=y NEED_TLS_PRF=y +ifdef CONFIG_TLSV12 +NEED_TLS_PRF_SHA256=y +endif NEED_MODEXP=y NEED_CIPHER=y L_CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT @@ -1044,8 +1085,12 @@ AESOBJS += src/crypto/aes-encblock.c endif ifdef NEED_AES_OMAC1 NEED_AES_ENC=y +ifdef CONFIG_OPENSSL_CMAC +CFLAGS += -DCONFIG_OPENSSL_CMAC +else AESOBJS += src/crypto/aes-omac1.c endif +endif ifdef NEED_AES_WRAP NEED_AES_ENC=y AESOBJS += src/crypto/aes-wrap.c @@ -1065,7 +1110,10 @@ endif SHA1OBJS = ifdef NEED_SHA1 +ifneq ($(CONFIG_TLS), openssl) SHA1OBJS += src/crypto/sha1.c +endif +SHA1OBJS += src/crypto/sha1-prf.c ifdef CONFIG_INTERNAL_SHA1 SHA1OBJS += src/crypto/sha1-internal.c ifdef NEED_FIPS186_2_PRF @@ -1075,8 +1123,10 @@ endif ifdef CONFIG_NO_WPA_PASSPHRASE L_CFLAGS += -DCONFIG_NO_PBKDF2 else +ifneq ($(CONFIG_TLS), openssl) SHA1OBJS += src/crypto/sha1-pbkdf2.c endif +endif ifdef NEED_T_PRF SHA1OBJS += src/crypto/sha1-tprf.c endif @@ -1085,14 +1135,13 @@ SHA1OBJS += src/crypto/sha1-tlsprf.c endif endif -MD5OBJS = src/crypto/md5.c +ifndef CONFIG_FIPS +MD5OBJS += src/crypto/md5.c +endif ifdef NEED_MD5 ifdef CONFIG_INTERNAL_MD5 MD5OBJS += src/crypto/md5-internal.c endif -ifdef CONFIG_FIPS -MD5OBJS += src/crypto/md5-non-fips.c -endif OBJS += $(MD5OBJS) OBJS_p += $(MD5OBJS) endif @@ -1119,10 +1168,16 @@ endif SHA256OBJS = # none by default ifdef NEED_SHA256 L_CFLAGS += -DCONFIG_SHA256 +ifneq ($(CONFIG_TLS), openssl) SHA256OBJS += src/crypto/sha256.c +endif +SHA256OBJS += src/crypto/sha256-prf.c ifdef CONFIG_INTERNAL_SHA256 SHA256OBJS += src/crypto/sha256-internal.c endif +ifdef NEED_TLS_PRF_SHA256 +SHA256OBJS += src/crypto/sha256-tlsprf.c +endif OBJS += $(SHA256OBJS) endif @@ -1162,6 +1217,11 @@ endif ifeq ($(CONFIG_CTRL_IFACE), named_pipe) L_CFLAGS += -DCONFIG_CTRL_IFACE_NAMED_PIPE endif +ifeq ($(CONFIG_CTRL_IFACE), udp-remote) +CONFIG_CTRL_IFACE=udp +L_CFLAGS += -DCONFIG_CTRL_IFACE_UDP +L_CFLAGS += -DCONFIG_CTRL_IFACE_UDP_REMOTE +endif OBJS += ctrl_iface.c ctrl_iface_$(CONFIG_CTRL_IFACE).c endif @@ -1282,6 +1342,10 @@ L_CFLAGS += -DLOG_HOSTAPD="$(CONFIG_DEBUG_SYSLOG_FACILITY)" endif endif +ifdef CONFIG_DEBUG_LINUX_TRACING +L_CFLAGS += -DCONFIG_DEBUG_LINUX_TRACING +endif + ifdef CONFIG_DEBUG_FILE L_CFLAGS += -DCONFIG_DEBUG_FILE endif @@ -1297,6 +1361,7 @@ endif OBJS += $(SHA1OBJS) $(DESOBJS) OBJS_p += $(SHA1OBJS) +OBJS_p += $(SHA256OBJS) ifdef CONFIG_BGSCAN_SIMPLE L_CFLAGS += -DCONFIG_BGSCAN_SIMPLE @@ -1332,8 +1397,19 @@ L_CFLAGS += -DCONFIG_AUTOSCAN OBJS += autoscan.c endif +ifdef CONFIG_EXT_PASSWORD_TEST +OBJS += src/utils/ext_password_test.c +L_CFLAGS += -DCONFIG_EXT_PASSWORD_TEST +NEED_EXT_PASSWORD=y +endif + +ifdef NEED_EXT_PASSWORD +OBJS += src/utils/ext_password.c +L_CFLAGS += -DCONFIG_EXT_PASSWORD +endif + ifdef NEED_GAS -OBJS += ../src/common/gas.c +OBJS += src/common/gas.c OBJS += gas_query.c L_CFLAGS += -DCONFIG_GAS NEED_OFFCHANNEL=y @@ -1371,6 +1447,9 @@ OBJS_priv += src/utils/common.c OBJS_priv += src/utils/wpa_debug.c OBJS_priv += src/utils/wpabuf.c OBJS_priv += wpa_priv.c +ifdef CONFIG_DRIVER_NL80211 +OBJS_priv += src/common/ieee802_11_common.c +endif ifdef CONFIG_DRIVER_TEST OBJS_priv += $(SHA1OBJS) OBJS_priv += $(MD5OBJS) diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index 6756e54e..227fb4fd 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -55,6 +55,11 @@ $(DESTDIR)$(BINDIR)/%: % install: $(addprefix $(DESTDIR)$(BINDIR)/,$(BINALL)) $(MAKE) -C ../src install +ifdef CONFIG_FIPS +CONFIG_NO_RANDOM_POOL= +CONFIG_OPENSSL_CMAC=y +endif + OBJS = config.o OBJS += notify.o OBJS += bss.o @@ -167,6 +172,11 @@ NEED_SHA256=y NEED_AES_OMAC1=y endif +ifdef CONFIG_IEEE80211V +CFLAGS += -DCONFIG_IEEE80211V +OBJS += wnm_sta.o +endif + ifdef CONFIG_TDLS CFLAGS += -DCONFIG_TDLS OBJS += ../src/rsn_supp/tdls.o @@ -227,6 +237,11 @@ CFLAGS += -DCONFIG_P2P_STRICT endif endif +ifdef CONFIG_WIFI_DISPLAY +CFLAGS += -DCONFIG_WIFI_DISPLAY +OBJS += wifi_display.o +endif + ifdef CONFIG_HS20 OBJS += hs20_supplicant.o CFLAGS += -DCONFIG_HS20 @@ -296,6 +311,17 @@ TLS_FUNCS=y CONFIG_IEEE8021X_EAPOL=y endif +ifdef CONFIG_EAP_UNAUTH_TLS +# EAP-UNAUTH-TLS +CFLAGS += -DEAP_UNAUTH_TLS +ifndef CONFIG_EAP_UNAUTH_TLS +OBJS += ../src/eap_peer/eap_tls.o +OBJS_h += ../src/eap_server/eap_server_tls.o +TLS_FUNCS=y +endif +CONFIG_IEEE8021X_EAPOL=y +endif + ifdef CONFIG_EAP_PEAP # EAP-PEAP ifeq ($(CONFIG_EAP_PEAP), dyn) @@ -741,6 +767,9 @@ endif ifdef CONFIG_INTERWORKING OBJS += ../src/ap/gas_serv.o endif +ifdef CONFIG_HS20 +OBJS += ../src/ap/hs20.o +endif endif ifdef NEED_RSN_AUTHENTICATOR @@ -839,8 +868,10 @@ NEED_DES=y # Shared TLS functions (needed for EAP_TLS, EAP_PEAP, EAP_TTLS, and EAP_FAST) OBJS += ../src/eap_peer/eap_tls_common.o OBJS_h += ../src/eap_server/eap_server_tls_common.o +ifndef CONFIG_FIPS NEED_TLS_PRF=y endif +endif ifndef CONFIG_TLS CONFIG_TLS=openssl @@ -868,6 +899,10 @@ OBJS += ../src/crypto/fips_prf_openssl.o endif LIBS += -lcrypto LIBS_p += -lcrypto +ifdef CONFIG_TLS_ADD_DL +LIBS += -ldl +LIBS_p += -ldl +endif endif ifeq ($(CONFIG_TLS), gnutls) @@ -1047,8 +1082,12 @@ AESOBJS += ../src/crypto/aes-encblock.o endif ifdef NEED_AES_OMAC1 NEED_AES_ENC=y +ifdef CONFIG_OPENSSL_CMAC +CFLAGS += -DCONFIG_OPENSSL_CMAC +else AESOBJS += ../src/crypto/aes-omac1.o endif +endif ifdef NEED_AES_WRAP NEED_AES_ENC=y AESOBJS += ../src/crypto/aes-wrap.o @@ -1067,7 +1106,10 @@ OBJS += $(AESOBJS) endif ifdef NEED_SHA1 +ifneq ($(CONFIG_TLS), openssl) SHA1OBJS += ../src/crypto/sha1.o +endif +SHA1OBJS += ../src/crypto/sha1-prf.o ifdef CONFIG_INTERNAL_SHA1 SHA1OBJS += ../src/crypto/sha1-internal.o ifdef NEED_FIPS186_2_PRF @@ -1077,8 +1119,10 @@ endif ifdef CONFIG_NO_WPA_PASSPHRASE CFLAGS += -DCONFIG_NO_PBKDF2 else +ifneq ($(CONFIG_TLS), openssl) SHA1OBJS += ../src/crypto/sha1-pbkdf2.o endif +endif ifdef NEED_T_PRF SHA1OBJS += ../src/crypto/sha1-tprf.o endif @@ -1087,14 +1131,13 @@ SHA1OBJS += ../src/crypto/sha1-tlsprf.o endif endif -MD5OBJS = ../src/crypto/md5.o +ifndef CONFIG_FIPS +MD5OBJS += ../src/crypto/md5.o +endif ifdef NEED_MD5 ifdef CONFIG_INTERNAL_MD5 MD5OBJS += ../src/crypto/md5-internal.o endif -ifdef CONFIG_FIPS -MD5OBJS += ../src/crypto/md5-non-fips.o -endif OBJS += $(MD5OBJS) OBJS_p += $(MD5OBJS) endif @@ -1121,7 +1164,10 @@ endif SHA256OBJS = # none by default ifdef NEED_SHA256 CFLAGS += -DCONFIG_SHA256 +ifneq ($(CONFIG_TLS), openssl) SHA256OBJS += ../src/crypto/sha256.o +endif +SHA256OBJS += ../src/crypto/sha256-prf.o ifdef CONFIG_INTERNAL_SHA256 SHA256OBJS += ../src/crypto/sha256-internal.o endif @@ -1167,6 +1213,11 @@ endif ifeq ($(CONFIG_CTRL_IFACE), named_pipe) CFLAGS += -DCONFIG_CTRL_IFACE_NAMED_PIPE endif +ifeq ($(CONFIG_CTRL_IFACE), udp-remote) +CONFIG_CTRL_IFACE=udp +CFLAGS += -DCONFIG_CTRL_IFACE_UDP +CFLAGS += -DCONFIG_CTRL_IFACE_UDP_REMOTE +endif OBJS += ctrl_iface.o ctrl_iface_$(CONFIG_CTRL_IFACE).o endif @@ -1297,6 +1348,9 @@ endif ifdef CONFIG_FIPS CFLAGS += -DCONFIG_FIPS +ifneq ($(CONFIG_TLS), openssl) +$(error CONFIG_FIPS=y requires CONFIG_TLS=openssl) +endif endif OBJS += $(SHA1OBJS) $(DESOBJS) @@ -1338,6 +1392,17 @@ CFLAGS += -DCONFIG_AUTOSCAN OBJS += autoscan.o endif +ifdef CONFIG_EXT_PASSWORD_TEST +OBJS += ../src/utils/ext_password_test.o +CFLAGS += -DCONFIG_EXT_PASSWORD_TEST +NEED_EXT_PASSWORD=y +endif + +ifdef NEED_EXT_PASSWORD +OBJS += ../src/utils/ext_password.o +CFLAGS += -DCONFIG_EXT_PASSWORD +endif + ifdef NEED_GAS OBJS += ../src/common/gas.o OBJS += gas_query.o @@ -1559,6 +1624,11 @@ test-eap_sim_common: $(TEST_EAP_SIM_COMMON_OBJS) tests: test-eap_sim_common +FIPSDIR=/usr/local/ssl/fips-2.0 +FIPSLD=$(FIPSDIR)/bin/fipsld +fips: + $(MAKE) CC=$(FIPSLD) FIPSLD_CC="$(CC)" + clean: $(MAKE) -C ../src clean $(MAKE) -C dbus clean diff --git a/wpa_supplicant/README-HS20 b/wpa_supplicant/README-HS20 new file mode 100644 index 00000000..feb90490 --- /dev/null +++ b/wpa_supplicant/README-HS20 @@ -0,0 +1,470 @@ +wpa_supplicant and Hotspot 2.0 +============================== + +This document describe how the IEEE 802.11u Interworking and Wi-Fi +Hotspot 2.0 (Release 1) implementation in wpa_supplicant can be +configured and how an external component on the client e.g., management +GUI or Wi-Fi framework) is used to manage this functionality. + + +Introduction to Wi-Fi Hotspot 2.0 +--------------------------------- + +Hotspot 2.0 is the name of the Wi-Fi Alliance specification that is used +in the Wi-Fi CERTIFIED Passpoint<TM> program. More information about +this is available in this white paper: + +http://www.wi-fi.org/knowledge-center/white-papers/wi-fi-certified-passpoint%E2%84%A2-new-program-wi-fi-alliance%C2%AE-enable-seamless + +The Hotspot 2.0 specification is also available from WFA: +https://www.wi-fi.org/knowledge-center/published-specifications + +The core Interworking functionality (network selection, GAS/ANQP) were +standardized in IEEE Std 802.11u-2011 which is now part of the IEEE Std +802.11-2012. + + +wpa_supplicant network selection +-------------------------------- + +Interworking support added option for configuring credentials that can +work with multiple networks as an alternative to configuration of +network blocks (e.g., per-SSID parameters). When requested to perform +network selection, wpa_supplicant picks the highest priority enabled +network block or credential. If a credential is picked (based on ANQP +information from APs), a temporary network block is created +automatically for the matching network. This temporary network block is +used similarly to the network blocks that can be configured by the user, +but it is not stored into the configuration file and is meant to be used +only for temporary period of time since a new one can be created +whenever needed based on ANQP information and the credential. + +By default, wpa_supplicant is not using automatic network selection +unless requested explicitly with the interworking_select command. This +can be changed with the auto_interworking=1 parameter to perform network +selection automatically whenever trying to find a network for connection +and none of the enabled network blocks match with the scan results. This +case works similarly to "interworking_select auto", i.e., wpa_supplicant +will internally determine which network or credential is going to be +used based on configured priorities, scan results, and ANQP information. + + +wpa_supplicant configuration +---------------------------- + +Interworking and Hotspot 2.0 functionality are optional components that +need to be enabled in the wpa_supplicant build configuration +(.config). This is done by adding following parameters into that file: + +CONFIG_INTERWORKING=y +CONFIG_HS20=y + +It should be noted that this functionality requires a driver that +supports GAS/ANQP operations. This uses the same design as P2P, i.e., +Action frame processing and building in user space within +wpa_supplicant. The Linux nl80211 driver interface provides the needed +functionality for this. + + +There are number of run-time configuration parameters (e.g., in +wpa_supplicant.conf when using the configuration file) that can be used +to control Hotspot 2.0 operations. + +# Enable Interworking +interworking=1 + +# Enable Hotspot 2.0 +hs20=1 + +# Parameters for controlling scanning + +# Homogenous ESS identifier +# If this is set, scans will be used to request response only from BSSes +# belonging to the specified Homogeneous ESS. This is used only if interworking +# is enabled. +#hessid=00:11:22:33:44:55 + +# Access Network Type +# When Interworking is enabled, scans can be limited to APs that advertise the +# specified Access Network Type (0..15; with 15 indicating wildcard match). +# This value controls the Access Network Type value in Probe Request frames. +#access_network_type=15 + +# Automatic network selection behavior +# 0 = do not automatically go through Interworking network selection +# (i.e., require explicit interworking_select command for this; default) +# 1 = perform Interworking network selection if one or more +# credentials have been configured and scan did not find a +# matching network block +#auto_interworking=0 + + +Credentials can be pre-configured for automatic network selection: + +# credential block +# +# Each credential used for automatic network selection is configured as a set +# of parameters that are compared to the information advertised by the APs when +# interworking_select and interworking_connect commands are used. +# +# credential fields: +# +# priority: Priority group +# By default, all networks and credentials get the same priority group +# (0). This field can be used to give higher priority for credentials +# (and similarly in struct wpa_ssid for network blocks) to change the +# Interworking automatic networking selection behavior. The matching +# network (based on either an enabled network block or a credential) +# with the highest priority value will be selected. +# +# pcsc: Use PC/SC and SIM/USIM card +# +# realm: Home Realm for Interworking +# +# username: Username for Interworking network selection +# +# password: Password for Interworking network selection +# +# ca_cert: CA certificate for Interworking network selection +# +# client_cert: File path to client certificate file (PEM/DER) +# This field is used with Interworking networking selection for a case +# where client certificate/private key is used for authentication +# (EAP-TLS). Full path to the file should be used since working +# directory may change when wpa_supplicant is run in the background. +# +# Alternatively, a named configuration blob can be used by setting +# this to blob://blob_name. +# +# private_key: File path to client private key file (PEM/DER/PFX) +# When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be +# commented out. Both the private key and certificate will be read +# from the PKCS#12 file in this case. Full path to the file should be +# used since working directory may change when wpa_supplicant is run +# in the background. +# +# Windows certificate store can be used by leaving client_cert out and +# configuring private_key in one of the following formats: +# +# cert://substring_to_match +# +# hash://certificate_thumbprint_in_hex +# +# For example: private_key="hash://63093aa9c47f56ae88334c7b65a4" +# +# Note that when running wpa_supplicant as an application, the user +# certificate store (My user account) is used, whereas computer store +# (Computer account) is used when running wpasvc as a service. +# +# Alternatively, a named configuration blob can be used by setting +# this to blob://blob_name. +# +# private_key_passwd: Password for private key file +# +# imsi: IMSI in <MCC> | <MNC> | '-' | <MSIN> format +# +# milenage: Milenage parameters for SIM/USIM simulator in <Ki>:<OPc>:<SQN> +# format +# +# domain: Home service provider FQDN +# This is used to compare against the Domain Name List to figure out +# whether the AP is operated by the Home SP. +# +# roaming_consortium: Roaming Consortium OI +# If roaming_consortium_len is non-zero, this field contains the +# Roaming Consortium OI that can be used to determine which access +# points support authentication with this credential. This is an +# alternative to the use of the realm parameter. When using Roaming +# Consortium to match the network, the EAP parameters need to be +# pre-configured with the credential since the NAI Realm information +# may not be available or fetched. +# +# eap: Pre-configured EAP method +# This optional field can be used to specify which EAP method will be +# used with this credential. If not set, the EAP method is selected +# automatically based on ANQP information (e.g., NAI Realm). +# +# phase1: Pre-configure Phase 1 (outer authentication) parameters +# This optional field is used with like the 'eap' parameter. +# +# phase2: Pre-configure Phase 2 (inner authentication) parameters +# This optional field is used with like the 'eap' parameter. +# +# for example: +# +#cred={ +# realm="example.com" +# username="user@example.com" +# password="password" +# ca_cert="/etc/wpa_supplicant/ca.pem" +# domain="example.com" +#} +# +#cred={ +# imsi="310026-000000000" +# milenage="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82" +#} +# +#cred={ +# realm="example.com" +# username="user" +# password="password" +# ca_cert="/etc/wpa_supplicant/ca.pem" +# domain="example.com" +# roaming_consortium=223344 +# eap=TTLS +# phase2="auth=MSCHAPV2" +#} + + +Control interface +----------------- + +wpa_supplicant provides a control interface that can be used from +external programs to manage various operations. The included command +line tool, wpa_cli, can be used for manual testing with this interface. + +Following wpa_cli interactive mode commands show some examples of manual +operations related to Hotspot 2.0: + +Remove configured networks and credentials: + +> remove_network all +OK +> remove_cred all +OK + + +Add a username/password credential: + +> add_cred +0 +> set_cred 0 realm "mail.example.com" +OK +> set_cred 0 username "username" +OK +> set_cred 0 password "password" +OK +> set_cred 0 priority 1 +OK + +Add a SIM credential using a simulated SIM/USIM card for testing: + +> add_cred +1 +> set_cred 1 imsi "23456-0000000000" +OK +> set_cred 1 milenage "90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123" +OK +> set_cred 1 priority 1 +OK + +Note: the return value of add_cred is used as the first argument to +the following set_cred commands. + + +Add a WPA2-Enterprise network: + +> add_network +0 +> set_network 0 key_mgmt WPA-EAP +OK +> set_network 0 ssid "enterprise" +OK +> set_network 0 eap TTLS +OK +> set_network 0 anonymous_identity "anonymous" +OK +> set_network 0 identity "user" +OK +> set_network 0 password "password" +OK +> set_network 0 priority 0 +OK +> enable_network 0 no-connect +OK + + +Add an open network: + +> add_network +3 +> set_network 3 key_mgmt NONE +OK +> set_network 3 ssid "coffee-shop" +OK +> select_network 3 +OK + +Note: the return value of add_network is used as the first argument to +the following set_network commands. + +The preferred credentials/networks can be indicated with the priority +parameter (1 is higher priority than 0). + + +Interworking network selection can be started with interworking_select +command. This instructs wpa_supplicant to run a network scan and iterate +through the discovered APs to request ANQP information from the APs that +advertise support for Interworking/Hotspot 2.0: + +> interworking_select +OK +<3>Starting ANQP fetch for 02:00:00:00:01:00 +<3>RX-ANQP 02:00:00:00:01:00 ANQP Capability list +<3>RX-ANQP 02:00:00:00:01:00 Roaming Consortium list +<3>RX-HS20-ANQP 02:00:00:00:01:00 HS Capability List +<3>ANQP fetch completed +<3>INTERWORKING-AP 02:00:00:00:01:00 type=unknown + + +INTERWORKING-AP event messages indicate the APs that support network +selection and for which there is a matching +credential. interworking_connect command can be used to select a network +to connect with: + + +> interworking_connect 02:00:00:00:01:00 +OK +<3>CTRL-EVENT-SCAN-RESULTS +<3>SME: Trying to authenticate with 02:00:00:00:01:00 (SSID='Example Network' freq=2412 MHz) +<3>Trying to associate with 02:00:00:00:01:00 (SSID='Example Network' freq=2412 MHz) +<3>Associated with 02:00:00:00:01:00 +<3>CTRL-EVENT-EAP-STARTED EAP authentication started +<3>CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=21 +<3>CTRL-EVENT-EAP-METHOD EAP vendor 0 method 21 (TTLS) selected +<3>CTRL-EVENT-EAP-SUCCESS EAP authentication completed successfully +<3>WPA: Key negotiation completed with 02:00:00:00:01:00 [PTK=CCMP GTK=CCMP] +<3>CTRL-EVENT-CONNECTED - Connection to 02:00:00:00:01:00 completed (auth) [id=0 id_str=] + + +wpa_supplicant creates a temporary network block for the selected +network based on the configured credential and ANQP information from the +AP: + +> list_networks +network id / ssid / bssid / flags +0 Example Network any [CURRENT] +> get_network 0 key_mgmt +WPA-EAP +> get_network 0 eap +TTLS + + +Alternatively to using an external program to select the network, +"interworking_select auto" command can be used to request wpa_supplicant +to select which network to use based on configured priorities: + + +> remove_network all +OK +<3>CTRL-EVENT-DISCONNECTED bssid=02:00:00:00:01:00 reason=1 locally_generated=1 +> interworking_select auto +OK +<3>Starting ANQP fetch for 02:00:00:00:01:00 +<3>RX-ANQP 02:00:00:00:01:00 ANQP Capability list +<3>RX-ANQP 02:00:00:00:01:00 Roaming Consortium list +<3>RX-HS20-ANQP 02:00:00:00:01:00 HS Capability List +<3>ANQP fetch completed +<3>INTERWORKING-AP 02:00:00:00:01:00 type=unknown +<3>CTRL-EVENT-SCAN-RESULTS +<3>SME: Trying to authenticate with 02:00:00:00:01:00 (SSID='Example Network' freq=2412 MHz) +<3>Trying to associate with 02:00:00:00:01:00 (SSID='Example Network' freq=2412 MHz) +<3>Associated with 02:00:00:00:01:00 +<3>CTRL-EVENT-EAP-STARTED EAP authentication started +<3>CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=21 +<3>CTRL-EVENT-EAP-METHOD EAP vendor 0 method 21 (TTLS) selected +<3>CTRL-EVENT-EAP-SUCCESS EAP authentication completed successfully +<3>WPA: Key negotiation completed with 02:00:00:00:01:00 [PTK=CCMP GTK=CCMP] +<3>CTRL-EVENT-CONNECTED - Connection to 02:00:00:00:01:00 completed (reauth) [id=0 id_str=] + + +The connection status can be shown with the status command: + +> status +bssid=02:00:00:00:01:00 +ssid=Example Network +id=0 +mode=station +pairwise_cipher=CCMP <--- link layer security indication +group_cipher=CCMP +key_mgmt=WPA2/IEEE 802.1X/EAP +wpa_state=COMPLETED +p2p_device_address=02:00:00:00:00:00 +address=02:00:00:00:00:00 +hs20=1 <--- HS 2.0 indication +Supplicant PAE state=AUTHENTICATED +suppPortStatus=Authorized +EAP state=SUCCESS +selectedMethod=21 (EAP-TTLS) +EAP TLS cipher=AES-128-SHA +EAP-TTLSv0 Phase2 method=PAP + + +> status +bssid=02:00:00:00:02:00 +ssid=coffee-shop +id=3 +mode=station +pairwise_cipher=NONE +group_cipher=NONE +key_mgmt=NONE +wpa_state=COMPLETED +p2p_device_address=02:00:00:00:00:00 +address=02:00:00:00:00:00 + + +Note: The Hotspot 2.0 indication is shown as "hs20=1" in the status +command output. Link layer security is indicated with the +pairwise_cipher (CCMP = secure, NONE = no encryption used). + + +Also the scan results include the Hotspot 2.0 indication: + +> scan_results +bssid / frequency / signal level / flags / ssid +02:00:00:00:01:00 2412 -30 [WPA2-EAP-CCMP][ESS][HS20] Example Network + + +ANQP information for the BSS can be fetched using the BSS command: + +> bss 02:00:00:00:01:00 +id=1 +bssid=02:00:00:00:01:00 +freq=2412 +beacon_int=100 +capabilities=0x0411 +qual=0 +noise=-92 +level=-30 +tsf=1345573286517276 +age=105 +ie=000f4578616d706c65204e6574776f726b010882848b960c1218240301012a010432043048606c30140100000fac040100000fac040100000fac0100007f04000000806b091e07010203040506076c027f006f1001531122331020304050010203040506dd05506f9a1000 +flags=[WPA2-EAP-CCMP][ESS][HS20] +ssid=Example Network +anqp_roaming_consortium=031122330510203040500601020304050603fedcba + + +ANQP queries can also be requested with the anqp_get and hs20_anqp_get +commands: + +> anqp_get 02:00:00:00:01:00 261 +OK +<3>RX-ANQP 02:00:00:00:01:00 Roaming Consortium list +> hs20_anqp_get 02:00:00:00:01:00 2 +OK +<3>RX-HS20-ANQP 02:00:00:00:01:00 HS Capability List + +In addition, fetch_anqp command can be used to request similar set of +ANQP queries to be done as is run as part of interworking_select: + +> scan +OK +<3>CTRL-EVENT-SCAN-RESULTS +> fetch_anqp +OK +<3>Starting ANQP fetch for 02:00:00:00:01:00 +<3>RX-ANQP 02:00:00:00:01:00 ANQP Capability list +<3>RX-ANQP 02:00:00:00:01:00 Roaming Consortium list +<3>RX-HS20-ANQP 02:00:00:00:01:00 HS Capability List +<3>ANQP fetch completed diff --git a/wpa_supplicant/README-P2P b/wpa_supplicant/README-P2P index bb4c2ade..6b81397f 100644 --- a/wpa_supplicant/README-P2P +++ b/wpa_supplicant/README-P2P @@ -71,7 +71,8 @@ over the main control interface. Device Discovery -p2p_find [timeout in seconds] [type=<social|progressive>] +p2p_find [timeout in seconds] [type=<social|progressive>] \ + [dev_id=<addr>] [delay=<search delay in ms>] The default behavior is to run a single full scan in the beginning and then scan only social channels. type=social will scan only social @@ -81,6 +82,11 @@ progressively one channel at the time in the Search state rounds. This will help in finding new groups or groups missed during the initial full scan. +The optional dev_id option can be used to specify a single P2P peer to +search for. The optional delay parameter can be used to request an extra +delay to be used between search iterations (e.g., to free up radio +resources for concurrent operations). + p2p_listen [timeout in seconds] Start Listen-only state (become discoverable without searching for @@ -250,6 +256,14 @@ p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 urn:schemas-upnp-org:service:Content p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 uuid:6859dede-8574-59ab-9332-123456789012 p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 urn:schemas-upnp-org:device:InternetGatewayDevice:1 +# Wi-Fi Display examples +# format: wifi-display <list of roles> <list of subelements> +p2p_serv_disc_req 00:00:00:00:00:00 wifi-display [source] 2,3,4,5 +p2p_serv_disc_req 02:01:02:03:04:05 wifi-display [pri-sink] 3 +p2p_serv_disc_req 00:00:00:00:00:00 wifi-display [sec-source] 2 +p2p_serv_disc_req 00:00:00:00:00:00 wifi-display [source+sink] 2,3,4,5 +p2p_serv_disc_req 00:00:00:00:00:00 wifi-display [source][pri-sink] 2,3,4,5 + p2p_serv_disc_cancel_req <query identifier> Cancel a pending P2P service discovery request. This command takes a diff --git a/wpa_supplicant/android.config b/wpa_supplicant/android.config index fc0614d3..d71d1d3a 100644 --- a/wpa_supplicant/android.config +++ b/wpa_supplicant/android.config @@ -495,3 +495,6 @@ CONFIG_NO_ROAMING=y # Enable P2P CONFIG_P2P=y CONFIG_AP=y + +#Enable Wifi Display +CONFIG_WIFI_DISPLAY=y diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c index f9e00457..3798f5a6 100644 --- a/wpa_supplicant/ap.c +++ b/wpa_supplicant/ap.c @@ -99,6 +99,18 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, if (!no_ht && mode && mode->ht_capab) { conf->ieee80211n = 1; +#ifdef CONFIG_P2P + if (conf->hw_mode == HOSTAPD_MODE_IEEE80211A && + (mode->ht_capab & + HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) && + ssid->ht40) + conf->secondary_channel = + wpas_p2p_get_ht40_mode(wpa_s, mode, + conf->channel); + if (conf->secondary_channel) + conf->ht_capab |= + HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET; +#endif /* CONFIG_P2P */ /* * white-list capabilities that won't cause issues @@ -150,7 +162,6 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, return -1; } os_memcpy(bss->ssid.ssid, ssid->ssid, ssid->ssid_len); - bss->ssid.ssid[ssid->ssid_len] = '\0'; bss->ssid.ssid_len = ssid->ssid_len; bss->ssid.ssid_set = 1; @@ -207,6 +218,9 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, } if (pairwise & WPA_CIPHER_TKIP) bss->wpa_group = WPA_CIPHER_TKIP; + else if ((pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) == + WPA_CIPHER_GCMP) + bss->wpa_group = WPA_CIPHER_GCMP; else bss->wpa_group = WPA_CIPHER_CCMP; @@ -450,6 +464,8 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, if (ssid->pairwise_cipher & WPA_CIPHER_CCMP) wpa_s->pairwise_cipher = WPA_CIPHER_CCMP; + else if (ssid->pairwise_cipher & WPA_CIPHER_GCMP) + wpa_s->pairwise_cipher = WPA_CIPHER_GCMP; else if (ssid->pairwise_cipher & WPA_CIPHER_TKIP) wpa_s->pairwise_cipher = WPA_CIPHER_TKIP; else if (ssid->pairwise_cipher & WPA_CIPHER_NONE) @@ -491,6 +507,10 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, return -1; } + os_memcpy(wpa_s->ap_iface->conf->wmm_ac_params, + wpa_s->conf->wmm_ac_params, + sizeof(wpa_s->conf->wmm_ac_params)); + if (params.uapsd > 0) { conf->bss->wmm_enabled = 1; conf->bss->wmm_uapsd = 1; @@ -511,7 +531,7 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, #endif /* CONFIG_P2P */ hapd_iface->num_bss = conf->num_bss; - hapd_iface->bss = os_zalloc(conf->num_bss * + hapd_iface->bss = os_calloc(conf->num_bss, sizeof(struct hostapd_data *)); if (hapd_iface->bss == NULL) { wpa_supplicant_ap_deinit(wpa_s); diff --git a/wpa_supplicant/bgscan_learn.c b/wpa_supplicant/bgscan_learn.c index ed4d879f..adf82d87 100644 --- a/wpa_supplicant/bgscan_learn.c +++ b/wpa_supplicant/bgscan_learn.c @@ -75,7 +75,7 @@ static void bgscan_learn_add_neighbor(struct bgscan_learn_bss *bss, if (bssid_in_array(bss->neigh, bss->num_neigh, bssid)) return; - n = os_realloc(bss->neigh, (bss->num_neigh + 1) * ETH_ALEN); + n = os_realloc_array(bss->neigh, bss->num_neigh + 1, ETH_ALEN); if (n == NULL) return; @@ -219,7 +219,7 @@ static int * bgscan_learn_get_freqs(struct bgscan_learn_data *data, dl_list_for_each(bss, &data->bss, struct bgscan_learn_bss, list) { if (in_array(freqs, bss->freq)) continue; - n = os_realloc(freqs, (*count + 2) * sizeof(int)); + n = os_realloc_array(freqs, *count + 2, sizeof(int)); if (n == NULL) return freqs; freqs = n; @@ -248,7 +248,7 @@ static int * bgscan_learn_get_probe_freq(struct bgscan_learn_data *data, wpa_printf(MSG_DEBUG, "bgscan learn: Probe new freq " "%u", data->supp_freqs[idx]); data->probe_idx = idx; - n = os_realloc(freqs, (count + 2) * sizeof(int)); + n = os_realloc_array(freqs, count + 2, sizeof(int)); if (n == NULL) return freqs; freqs = n; @@ -360,7 +360,7 @@ static int * bgscan_learn_get_supp_freqs(struct wpa_supplicant *wpa_s) for (j = 0; j < modes[i].num_channels; j++) { if (modes[i].channels[j].flag & HOSTAPD_CHAN_DISABLED) continue; - n = os_realloc(freqs, (count + 2) * sizeof(int)); + n = os_realloc_array(freqs, count + 2, sizeof(int)); if (n == NULL) continue; diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index b89732dd..be210297 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -53,42 +53,6 @@ struct parse_data { }; -static char * wpa_config_parse_string(const char *value, size_t *len) -{ - if (*value == '"') { - const char *pos; - char *str; - value++; - pos = os_strrchr(value, '"'); - if (pos == NULL || pos[1] != '\0') - return NULL; - *len = pos - value; - str = os_malloc(*len + 1); - if (str == NULL) - return NULL; - os_memcpy(str, value, *len); - str[*len] = '\0'; - return str; - } else { - u8 *str; - size_t tlen, hlen = os_strlen(value); - if (hlen & 1) - return NULL; - tlen = hlen / 2; - str = os_malloc(tlen + 1); - if (str == NULL) - return NULL; - if (hexstr2bin(value, str, tlen)) { - os_free(str); - return NULL; - } - str[tlen] = '\0'; - *len = tlen; - return (char *) str; - } -} - - static int wpa_config_parse_str(const struct parse_data *data, struct wpa_ssid *ssid, int line, const char *value) @@ -149,18 +113,6 @@ set: #ifndef NO_CONFIG_WRITE -static int is_hex(const u8 *data, size_t len) -{ - size_t i; - - for (i = 0; i < len; i++) { - if (data[i] < 32 || data[i] >= 127) - return 1; - } - return 0; -} - - static char * wpa_config_write_string_ascii(const u8 *value, size_t len) { char *buf; @@ -219,26 +171,6 @@ static char * wpa_config_write_str(const struct parse_data *data, return wpa_config_write_string((const u8 *) *src, len); } - -#ifdef WPA_UNICODE_SSID -static char * wpa_config_write_str_unicode(const struct parse_data *data, - struct wpa_ssid *ssid) -{ - size_t len; - char **src; - - src = (char **) (((u8 *) ssid) + (long) data->param1); - if (*src == NULL) - return NULL; - - if (data->param2) - len = *((size_t *) (((u8 *) ssid) + (long) data->param2)); - else - len = os_strlen(*src); - - return wpa_config_write_string_ascii((const u8 *) *src, len); -} -#endif #endif /* NO_CONFIG_WRITE */ @@ -344,6 +276,21 @@ static int wpa_config_parse_psk(const struct parse_data *data, struct wpa_ssid *ssid, int line, const char *value) { +#ifdef CONFIG_EXT_PASSWORD + if (os_strncmp(value, "ext:", 4) == 0) { + os_free(ssid->passphrase); + ssid->passphrase = NULL; + ssid->psk_set = 0; + os_free(ssid->ext_psk); + ssid->ext_psk = os_strdup(value + 4); + if (ssid->ext_psk == NULL) + return -1; + wpa_printf(MSG_DEBUG, "PSK: External password '%s'", + ssid->ext_psk); + return 0; + } +#endif /* CONFIG_EXT_PASSWORD */ + if (*value == '"') { #ifndef CONFIG_NO_PBKDF2 const char *pos; @@ -401,6 +348,17 @@ static int wpa_config_parse_psk(const struct parse_data *data, static char * wpa_config_write_psk(const struct parse_data *data, struct wpa_ssid *ssid) { +#ifdef CONFIG_EXT_PASSWORD + if (ssid->ext_psk) { + size_t len = 4 + os_strlen(ssid->ext_psk) + 1; + char *buf = os_malloc(len); + if (buf == NULL) + return NULL; + os_snprintf(buf, len, "ext:%s", ssid->ext_psk); + return buf; + } +#endif /* CONFIG_EXT_PASSWORD */ + if (ssid->passphrase) return wpa_config_write_string_ascii( (const u8 *) ssid->passphrase, @@ -685,6 +643,8 @@ static int wpa_config_parse_cipher(int line, const char *value) *end = '\0'; if (os_strcmp(start, "CCMP") == 0) val |= WPA_CIPHER_CCMP; + else if (os_strcmp(start, "GCMP") == 0) + val |= WPA_CIPHER_GCMP; else if (os_strcmp(start, "TKIP") == 0) val |= WPA_CIPHER_TKIP; else if (os_strcmp(start, "WEP104") == 0) @@ -736,6 +696,16 @@ static char * wpa_config_write_cipher(int cipher) pos += ret; } + if (cipher & WPA_CIPHER_GCMP) { + ret = os_snprintf(pos, end - pos, "%sGCMP", + pos == buf ? "" : " "); + if (ret < 0 || ret >= end - pos) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } + if (cipher & WPA_CIPHER_TKIP) { ret = os_snprintf(pos, end - pos, "%sTKIP", pos == buf ? "" : " "); @@ -789,7 +759,8 @@ static int wpa_config_parse_pairwise(const struct parse_data *data, val = wpa_config_parse_cipher(line, value); if (val == -1) return -1; - if (val & ~(WPA_CIPHER_CCMP | WPA_CIPHER_TKIP | WPA_CIPHER_NONE)) { + if (val & ~(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | + WPA_CIPHER_NONE)) { wpa_printf(MSG_ERROR, "Line %d: not allowed pairwise cipher " "(0x%x).", line, val); return -1; @@ -818,8 +789,8 @@ static int wpa_config_parse_group(const struct parse_data *data, val = wpa_config_parse_cipher(line, value); if (val == -1) return -1; - if (val & ~(WPA_CIPHER_CCMP | WPA_CIPHER_TKIP | WPA_CIPHER_WEP104 | - WPA_CIPHER_WEP40)) { + if (val & ~(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | + WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40)) { wpa_printf(MSG_ERROR, "Line %d: not allowed group cipher " "(0x%x).", line, val); return -1; @@ -949,7 +920,7 @@ static int * wpa_config_parse_freqs(const struct parse_data *data, used = 0; len = 10; - freqs = os_zalloc((len + 1) * sizeof(int)); + freqs = os_calloc(len + 1, sizeof(int)); if (freqs == NULL) return NULL; @@ -960,7 +931,7 @@ static int * wpa_config_parse_freqs(const struct parse_data *data, if (used == len) { int *n; size_t i; - n = os_realloc(freqs, (len * 2 + 1) * sizeof(int)); + n = os_realloc_array(freqs, len * 2 + 1, sizeof(int)); if (n == NULL) { os_free(freqs); return NULL; @@ -1089,8 +1060,8 @@ static int wpa_config_parse_eap(const struct parse_data *data, last = *end == '\0'; *end = '\0'; tmp = methods; - methods = os_realloc(methods, - (num_methods + 1) * sizeof(*methods)); + methods = os_realloc_array(methods, num_methods + 1, + sizeof(*methods)); if (methods == NULL) { os_free(tmp); os_free(buf); @@ -1120,7 +1091,7 @@ static int wpa_config_parse_eap(const struct parse_data *data, os_free(buf); tmp = methods; - methods = os_realloc(methods, (num_methods + 1) * sizeof(*methods)); + methods = os_realloc_array(methods, num_methods + 1, sizeof(*methods)); if (methods == NULL) { os_free(tmp); return -1; @@ -1186,6 +1157,20 @@ static int wpa_config_parse_password(const struct parse_data *data, return 0; } +#ifdef CONFIG_EXT_PASSWORD + if (os_strncmp(value, "ext:", 4) == 0) { + char *name = os_strdup(value + 4); + if (name == NULL) + return -1; + os_free(ssid->eap.password); + ssid->eap.password = (u8 *) name; + ssid->eap.password_len = os_strlen(name); + ssid->eap.flags &= ~EAP_CONFIG_FLAGS_PASSWORD_NTHASH; + ssid->eap.flags |= EAP_CONFIG_FLAGS_EXT_PASSWORD; + return 0; + } +#endif /* CONFIG_EXT_PASSWORD */ + if (os_strncmp(value, "hash:", 5) != 0) { char *tmp; size_t res_len; @@ -1203,6 +1188,7 @@ static int wpa_config_parse_password(const struct parse_data *data, ssid->eap.password = (u8 *) tmp; ssid->eap.password_len = res_len; ssid->eap.flags &= ~EAP_CONFIG_FLAGS_PASSWORD_NTHASH; + ssid->eap.flags &= ~EAP_CONFIG_FLAGS_EXT_PASSWORD; return 0; } @@ -1231,6 +1217,7 @@ static int wpa_config_parse_password(const struct parse_data *data, ssid->eap.password = hash; ssid->eap.password_len = 16; ssid->eap.flags |= EAP_CONFIG_FLAGS_PASSWORD_NTHASH; + ssid->eap.flags &= ~EAP_CONFIG_FLAGS_EXT_PASSWORD; return 0; } @@ -1244,6 +1231,17 @@ static char * wpa_config_write_password(const struct parse_data *data, if (ssid->eap.password == NULL) return NULL; +#ifdef CONFIG_EXT_PASSWORD + if (ssid->eap.flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) { + buf = os_zalloc(4 + ssid->eap.password_len + 1); + if (buf == NULL) + return NULL; + os_memcpy(buf, "ext:", 4); + os_memcpy(buf + 4, ssid->eap.password, ssid->eap.password_len); + return buf; + } +#endif /* CONFIG_EXT_PASSWORD */ + if (!(ssid->eap.flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH)) { return wpa_config_write_string( ssid->eap.password, ssid->eap.password_len); @@ -1391,18 +1389,27 @@ static int wpa_config_parse_p2p_client_list(const struct parse_data *data, pos++; if (hwaddr_aton(pos, addr)) { - wpa_printf(MSG_ERROR, "Line %d: Invalid " - "p2p_client_list address '%s'.", - line, value); - /* continue anyway */ + if (count == 0) { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "p2p_client_list address '%s'.", + line, value); + os_free(buf); + return -1; + } + /* continue anyway since this could have been from a + * truncated configuration file line */ + wpa_printf(MSG_INFO, "Line %d: Ignore likely " + "truncated p2p_client_list address '%s'", + line, pos); } else { - n = os_realloc(buf, (count + 1) * ETH_ALEN); + n = os_realloc_array(buf, count + 1, ETH_ALEN); if (n == NULL) { os_free(buf); return -1; } buf = n; - os_memcpy(buf + count * ETH_ALEN, addr, ETH_ALEN); + os_memmove(buf + ETH_ALEN, buf, count * ETH_ALEN); + os_memcpy(buf, addr, ETH_ALEN); count++; wpa_hexdump(MSG_MSGDUMP, "p2p_client_list", addr, ETH_ALEN); @@ -1436,10 +1443,10 @@ static char * wpa_config_write_p2p_client_list(const struct parse_data *data, pos = value; end = value + 20 * ssid->num_p2p_clients; - for (i = 0; i < ssid->num_p2p_clients; i++) { + for (i = ssid->num_p2p_clients; i > 0; i--) { res = os_snprintf(pos, end - pos, MACSTR " ", MAC2STR(ssid->p2p_client_list + - i * ETH_ALEN)); + (i - 1) * ETH_ALEN)); if (res < 0 || res >= end - pos) { os_free(value); return NULL; @@ -1500,15 +1507,6 @@ static char * wpa_config_write_p2p_client_list(const struct parse_data *data, OFFSET(f), (void *) 0 #define _INTe(f) #f, wpa_config_parse_int, wpa_config_write_int, \ OFFSET(eap.f), (void *) 0 -#ifdef WPA_UNICODE_SSID -/* STR_* variants that do not force conversion to ASCII */ -#define _STR_UNICODE(f) #f, wpa_config_parse_str, wpa_config_write_str_unicode, OFFSET(f) -#define STR_UNICODE(f) _STR_UNICODE(f), NULL, NULL, NULL, 0 -#define _STR_LEN_UNICODE(f) _STR_UNICODE(f), OFFSET(f ## _len) -#define STR_LEN_UNICODE(f) _STR_LEN_UNICODE(f), NULL, NULL, 0 -#define _STR_RANGE_UNICODE(f, min, max) _STR_LEN_UNICODE(f), (void *) (min), (void *) (max) -#define STR_RANGE_UNICODE(f, min, max) _STR_RANGE_UNICODE(f, min, max), 0 -#endif #endif /* NO_CONFIG_WRITE */ /* INT: Define an integer variable */ @@ -1553,11 +1551,7 @@ static char * wpa_config_write_p2p_client_list(const struct parse_data *data, * functions. */ static const struct parse_data ssid_fields[] = { -#ifdef WPA_UNICODE_SSID - { STR_RANGE_UNICODE(ssid, 0, MAX_SSID_LEN) }, -#else { STR_RANGE(ssid, 0, MAX_SSID_LEN) }, -#endif { INT_RANGE(scan_ssid, 0, 1) }, { FUNC(bssid) }, { FUNC_KEY(psk) }, @@ -1646,15 +1640,6 @@ static const struct parse_data ssid_fields[] = { { INT(dtim_period) }, }; -#ifdef WPA_UNICODE_SSID -#undef _STR_UNICODE -#undef STR_UNICODE -#undef _STR_LEN_UNICODE -#undef STR_LEN_UNICODE -#undef _STR_RANGE_UNICODE -#undef STR_RANGE_UNICODE -#endif - #undef OFFSET #undef _STR #undef STR @@ -1706,8 +1691,8 @@ int wpa_config_add_prio_network(struct wpa_config *config, } /* First network for this priority - add a new priority list */ - nlist = os_realloc(config->pssid, - (config->num_prio + 1) * sizeof(struct wpa_ssid *)); + nlist = os_realloc_array(config->pssid, config->num_prio + 1, + sizeof(struct wpa_ssid *)); if (nlist == NULL) return -1; @@ -1813,6 +1798,7 @@ void wpa_config_free_ssid(struct wpa_ssid *ssid) { os_free(ssid->ssid); os_free(ssid->passphrase); + os_free(ssid->ext_psk); #ifdef IEEE8021X_EAPOL eap_peer_config_free(&ssid->eap); #endif /* IEEE8021X_EAPOL */ @@ -1840,6 +1826,9 @@ void wpa_config_free_cred(struct wpa_cred *cred) os_free(cred->imsi); os_free(cred->milenage); os_free(cred->domain); + os_free(cred->eap_method); + os_free(cred->phase1); + os_free(cred->phase2); os_free(cred); } @@ -1905,6 +1894,7 @@ void wpa_config_free(struct wpa_config *config) wpabuf_free(config->wps_nfc_dh_pubkey); wpabuf_free(config->wps_nfc_dh_privkey); wpabuf_free(config->wps_nfc_dev_pw); + os_free(config->ext_password_backend); os_free(config); } @@ -2139,7 +2129,7 @@ char ** wpa_config_get_all(struct wpa_ssid *ssid, int get_keys) get_keys = get_keys && ssid->export_keys; - props = os_zalloc(sizeof(char *) * ((2 * NUM_SSID_FIELDS) + 1)); + props = os_calloc(2 * NUM_SSID_FIELDS + 1, sizeof(char *)); if (!props) return NULL; @@ -2267,8 +2257,7 @@ char * wpa_config_get_no_key(struct wpa_ssid *ssid, const char *var) void wpa_config_update_psk(struct wpa_ssid *ssid) { #ifndef CONFIG_NO_PBKDF2 - pbkdf2_sha1(ssid->passphrase, - (char *) ssid->ssid, ssid->ssid_len, 4096, + pbkdf2_sha1(ssid->passphrase, ssid->ssid, ssid->ssid_len, 4096, ssid->psk, PMK_LEN); wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)", ssid->psk, PMK_LEN); @@ -2293,6 +2282,31 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var, return 0; } + if (os_strcmp(var, "eap") == 0) { + struct eap_method_type method; + method.method = eap_peer_get_type(value, &method.vendor); + if (method.vendor == EAP_VENDOR_IETF && + method.method == EAP_TYPE_NONE) { + wpa_printf(MSG_ERROR, "Line %d: unknown EAP type '%s' " + "for a credential", line, value); + return -1; + } + os_free(cred->eap_method); + cred->eap_method = os_malloc(sizeof(*cred->eap_method)); + if (cred->eap_method == NULL) + return -1; + os_memcpy(cred->eap_method, &method, sizeof(method)); + return 0; + } + + if (os_strcmp(var, "password") == 0 && + os_strncmp(value, "ext:", 4) == 0) { + os_free(cred->password); + cred->password = os_strdup(value); + cred->ext_password = 1; + return 0; + } + val = wpa_config_parse_string(value, &len); if (val == NULL) { wpa_printf(MSG_ERROR, "Line %d: invalid field '%s' string " @@ -2315,6 +2329,7 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var, if (os_strcmp(var, "password") == 0) { os_free(cred->password); cred->password = val; + cred->ext_password = 0; return 0; } @@ -2360,6 +2375,32 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var, return 0; } + if (os_strcmp(var, "phase1") == 0) { + os_free(cred->phase1); + cred->phase1 = val; + return 0; + } + + if (os_strcmp(var, "phase2") == 0) { + os_free(cred->phase2); + cred->phase2 = val; + return 0; + } + + if (os_strcmp(var, "roaming_consortium") == 0) { + if (len < 3 || len > sizeof(cred->roaming_consortium)) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "roaming_consortium length %d (3..15 " + "expected)", line, (int) len); + os_free(val); + return -1; + } + os_memcpy(cred->roaming_consortium, val, len); + cred->roaming_consortium_len = len; + os_free(val); + return 0; + } + if (line) { wpa_printf(MSG_ERROR, "Line %d: unknown cred field '%s'.", line, var); @@ -2530,6 +2571,15 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface, const char *driver_param) { struct wpa_config *config; + const int aCWmin = 4, aCWmax = 10; + const struct hostapd_wmm_ac_params ac_bk = + { aCWmin, aCWmax, 7, 0, 0 }; /* background traffic */ + const struct hostapd_wmm_ac_params ac_be = + { aCWmin, aCWmax, 3, 0, 0 }; /* best effort traffic */ + const struct hostapd_wmm_ac_params ac_vi = /* video traffic */ + { aCWmin - 1, aCWmin, 2, 3000 / 32, 0 }; + const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */ + { aCWmin - 2, aCWmin - 1, 2, 1500 / 32, 0 }; config = os_zalloc(sizeof(*config)); if (config == NULL) @@ -2539,11 +2589,16 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface, 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_max_inactivity = DEFAULT_P2P_GO_MAX_INACTIVITY; config->bss_max_count = DEFAULT_BSS_MAX_COUNT; config->bss_expiration_age = DEFAULT_BSS_EXPIRATION_AGE; config->bss_expiration_scan_count = DEFAULT_BSS_EXPIRATION_SCAN_COUNT; config->max_num_sta = DEFAULT_MAX_NUM_STA; config->access_network_type = DEFAULT_ACCESS_NETWORK_TYPE; + config->wmm_ac_params[0] = ac_be; + config->wmm_ac_params[1] = ac_bk; + config->wmm_ac_params[2] = ac_vi; + config->wmm_ac_params[3] = ac_vo; if (ctrl_interface) config->ctrl_interface = os_strdup(ctrl_interface); @@ -2836,7 +2891,8 @@ static int wpa_config_process_p2p_pref_chan( pos2++; chan = atoi(pos2); - n = os_realloc(pref, (num + 1) * sizeof(struct p2p_channel)); + n = os_realloc_array(pref, num + 1, + sizeof(struct p2p_channel)); if (n == NULL) goto fail; pref = n; @@ -2950,6 +3006,7 @@ static const struct global_parse_data global_fields[] = { { INT(bss_expiration_age), 0 }, { INT(bss_expiration_scan_count), 0 }, { INT_RANGE(filter_ssids, 0, 1), 0 }, + { INT_RANGE(filter_rssi, -100, 0), 0 }, { INT(max_num_sta), 0 }, { INT_RANGE(disassoc_low_ack, 0, 1), 0 }, #ifdef CONFIG_HS20 @@ -2963,7 +3020,10 @@ static const struct global_parse_data global_fields[] = { { INT_RANGE(wps_nfc_dev_pw_id, 0x10, 0xffff), 0 }, { BIN(wps_nfc_dh_pubkey), 0 }, { BIN(wps_nfc_dh_privkey), 0 }, - { BIN(wps_nfc_dev_pw), 0 } + { BIN(wps_nfc_dev_pw), 0 }, + { STR(ext_password_backend), CFG_CHANGED_EXT_PW_BACKEND }, + { INT(p2p_go_max_inactivity), 0 }, + { INT_RANGE(auto_interworking, 0, 1), 0 }, }; #undef FUNC @@ -2998,6 +3058,25 @@ int wpa_config_process_global(struct wpa_config *config, char *pos, int line) break; } if (i == NUM_GLOBAL_FIELDS) { +#ifdef CONFIG_AP + if (os_strncmp(pos, "wmm_ac_", 7) == 0) { + char *tmp = os_strchr(pos, '='); + if (tmp == NULL) { + if (line < 0) + return -1; + wpa_printf(MSG_ERROR, "Line %d: invalid line " + "'%s'", line, pos); + return -1; + } + *tmp++ = '\0'; + if (hostapd_config_wmm_ac(config->wmm_ac_params, pos, + tmp)) { + wpa_printf(MSG_ERROR, "Line %d: invalid WMM " + "AC item", line); + return -1; + } + } +#endif /* CONFIG_AP */ if (line < 0) return -1; wpa_printf(MSG_ERROR, "Line %d: unknown global field '%s'.", diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h index 46c4da24..b889ab81 100644 --- a/wpa_supplicant/config.h +++ b/wpa_supplicant/config.h @@ -18,6 +18,7 @@ #define DEFAULT_FAST_REAUTH 1 #define DEFAULT_P2P_GO_INTENT 7 #define DEFAULT_P2P_INTRA_BSS 1 +#define DEFAULT_P2P_GO_MAX_INACTIVITY (5 * 60) #define DEFAULT_BSS_MAX_COUNT 200 #define DEFAULT_BSS_EXPIRATION_AGE 180 #define DEFAULT_BSS_EXPIRATION_SCAN_COUNT 2 @@ -26,6 +27,7 @@ #include "config_ssid.h" #include "wps/wps.h" +#include "common/ieee802_11_common.h" struct wpa_cred { @@ -81,6 +83,11 @@ struct wpa_cred { char *password; /** + * ext_password - Whether password is a name for external storage + */ + int ext_password; + + /** * ca_cert - CA certificate for Interworking network selection */ char *ca_cert; @@ -148,6 +155,47 @@ struct wpa_cred { * whether the AP is operated by the Home SP. */ char *domain; + + /** + * roaming_consortium - Roaming Consortium OI + * + * If roaming_consortium_len is non-zero, this field contains the + * Roaming Consortium OI that can be used to determine which access + * points support authentication with this credential. This is an + * alternative to the use of the realm parameter. When using Roaming + * Consortium to match the network, the EAP parameters need to be + * pre-configured with the credential since the NAI Realm information + * may not be available or fetched. + */ + u8 roaming_consortium[15]; + + /** + * roaming_consortium_len - Length of roaming_consortium + */ + size_t roaming_consortium_len; + + /** + * eap_method - EAP method to use + * + * Pre-configured EAP method to use with this credential or %NULL to + * indicate no EAP method is selected, i.e., the method will be + * selected automatically based on ANQP information. + */ + struct eap_method_type *eap_method; + + /** + * phase1 - Phase 1 (outer authentication) parameters + * + * Pre-configured EAP parameters or %NULL. + */ + char *phase1; + + /** + * phase2 - Phase 2 (inner authentication) parameters + * + * Pre-configured EAP parameters or %NULL. + */ + char *phase2; }; @@ -165,6 +213,7 @@ struct wpa_cred { #define CFG_CHANGED_P2P_LISTEN_CHANNEL BIT(11) #define CFG_CHANGED_P2P_OPER_CHANNEL BIT(12) #define CFG_CHANGED_P2P_PREF_CHAN BIT(13) +#define CFG_CHANGED_EXT_PW_BACKEND BIT(14) /** * struct wpa_config - wpa_supplicant configuration data @@ -575,6 +624,14 @@ struct wpa_config { int filter_ssids; /** + * filter_rssi - RSSI-based scan result filtering + * + * 0 = do not filter scan results + * -n = filter scan results below -n dBm + */ + int filter_rssi; + + /** * max_num_sta - Maximum number of STAs in an AP/P2P GO */ unsigned int max_num_sta; @@ -661,6 +718,35 @@ struct wpa_config { * wps_nfc_dh_pubkey - NFC Device Password for password token */ struct wpabuf *wps_nfc_dev_pw; + + /** + * ext_password_backend - External password backend or %NULL if none + * + * format: <backend name>[:<optional backend parameters>] + */ + char *ext_password_backend; + + /* + * p2p_go_max_inactivity - Timeout in seconds to detect STA inactivity + * + * This timeout value is used in P2P GO mode to clean up + * inactive stations. + * By default: 300 seconds. + */ + int p2p_go_max_inactivity; + + struct hostapd_wmm_ac_params wmm_ac_params[4]; + + /** + * auto_interworking - Whether to use network selection automatically + * + * 0 = do not automatically go through Interworking network selection + * (i.e., require explicit interworking_select command for this) + * 1 = perform Interworking network selection if one or more + * credentials have been configured and scan did not find a + * matching network block + */ + int auto_interworking; }; diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c index 8badc7b9..531957a9 100644 --- a/wpa_supplicant/config_file.c +++ b/wpa_supplicant/config_file.c @@ -19,6 +19,29 @@ #include "p2p/p2p.h" +static int newline_terminated(const char *buf, size_t buflen) +{ + size_t len = os_strlen(buf); + if (len == 0) + return 0; + if (len == buflen - 1 && buf[buflen - 1] != '\r' && + buf[len - 1] != '\n') + return 0; + return 1; +} + + +static void skip_line_end(FILE *stream) +{ + char buf[100]; + while (fgets(buf, sizeof(buf), stream)) { + buf[sizeof(buf) - 1] = '\0'; + if (newline_terminated(buf, sizeof(buf))) + return; + } +} + + /** * wpa_config_get_line - Read the next configuration file line * @s: Buffer for the line @@ -41,6 +64,15 @@ static char * wpa_config_get_line(char *s, int size, FILE *stream, int *line, while (fgets(s, size, stream)) { (*line)++; s[size - 1] = '\0'; + if (!newline_terminated(s, size)) { + /* + * The line was truncated - skip rest of it to avoid + * confusing error messages. + */ + wpa_printf(MSG_INFO, "Long line in configuration file " + "truncated"); + skip_line_end(stream); + } pos = s; /* Skip white space from the beginning of line. */ @@ -99,12 +131,6 @@ static int wpa_config_validate_network(struct wpa_ssid *ssid, int line) wpa_config_update_psk(ssid); } - if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && !ssid->psk_set) { - wpa_printf(MSG_ERROR, "Line %d: WPA-PSK accepted for key " - "management, but no PSK configured.", line); - errors++; - } - if ((ssid->group_cipher & WPA_CIPHER_CCMP) && !(ssid->pairwise_cipher & WPA_CIPHER_CCMP) && !(ssid->pairwise_cipher & WPA_CIPHER_NONE)) { @@ -123,7 +149,7 @@ static struct wpa_ssid * wpa_config_read_network(FILE *f, int *line, int id) { struct wpa_ssid *ssid; int errors = 0, end = 0; - char buf[256], *pos, *pos2; + char buf[2000], *pos, *pos2; wpa_printf(MSG_MSGDUMP, "Line: %d - start of a new network block", *line); @@ -329,11 +355,17 @@ struct wpa_config * wpa_config_read(const char *name) int cred_id = 0; config = wpa_config_alloc_empty(NULL, NULL); - if (config == NULL) + if (config == NULL) { + wpa_printf(MSG_ERROR, "Failed to allocate config file " + "structure"); return NULL; + } + wpa_printf(MSG_DEBUG, "Reading configuration file '%s'", name); f = fopen(name, "r"); if (f == NULL) { + wpa_printf(MSG_ERROR, "Failed to open config file '%s', " + "error: %s", name, strerror(errno)); os_free(config); return NULL; } @@ -378,6 +410,8 @@ struct wpa_config * wpa_config_read(const char *name) } else if (os_strncmp(pos, "blob-base64-", 12) == 0) { if (wpa_config_process_blob(config, f, &line, pos + 12) < 0) { + wpa_printf(MSG_ERROR, "Line %d: failed to " + "process blob.", line); errors++; continue; } @@ -875,6 +909,16 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) write_global_bin(f, "wps_nfc_dh_pubkey", config->wps_nfc_dh_pubkey); write_global_bin(f, "wps_nfc_dh_privkey", config->wps_nfc_dh_privkey); write_global_bin(f, "wps_nfc_dev_pw", config->wps_nfc_dev_pw); + + if (config->ext_password_backend) + fprintf(f, "ext_password_backend=%s\n", + config->ext_password_backend); + if (config->p2p_go_max_inactivity != DEFAULT_P2P_GO_MAX_INACTIVITY) + fprintf(f, "p2p_go_max_inactivity=%d\n", + config->p2p_go_max_inactivity); + if (config->auto_interworking) + fprintf(f, "auto_interworking=%d\n", + config->auto_interworking); } #endif /* CONFIG_NO_CONFIG_WRITE */ diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h index 08636071..ff973793 100644 --- a/wpa_supplicant/config_ssid.h +++ b/wpa_supplicant/config_ssid.h @@ -141,6 +141,14 @@ struct wpa_ssid { char *passphrase; /** + * ext_psk - PSK/passphrase name in external storage + * + * If this is set, PSK/passphrase will be fetched from external storage + * when requesting association with the network. + */ + char *ext_psk; + + /** * pairwise_cipher - Bitfield of allowed pairwise ciphers, WPA_CIPHER_* */ int pairwise_cipher; @@ -356,6 +364,8 @@ struct wpa_ssid { */ int frequency; + int ht40; + /** * wpa_ptk_rekey - Maximum lifetime for PTK in seconds * @@ -421,6 +431,10 @@ struct wpa_ssid { */ size_t num_p2p_clients; +#ifndef P2P_MAX_STORED_CLIENTS +#define P2P_MAX_STORED_CLIENTS 100 +#endif /* P2P_MAX_STORED_CLIENTS */ + /** * p2p_group - Network generated as a P2P group (used internally) */ @@ -512,6 +526,16 @@ struct wpa_ssid { * By default: 2 */ int dtim_period; + + /** + * auth_failures - Number of consecutive authentication failures + */ + unsigned int auth_failures; + + /** + * disabled_until - Network block disabled until this time if non-zero + */ + struct os_time disabled_until; }; #endif /* CONFIG_SSID_H */ diff --git a/wpa_supplicant/config_winreg.c b/wpa_supplicant/config_winreg.c index de2ec58b..6d9876cc 100644 --- a/wpa_supplicant/config_winreg.c +++ b/wpa_supplicant/config_winreg.c @@ -344,13 +344,6 @@ static struct wpa_ssid * wpa_config_read_network(HKEY hk, const TCHAR *netw, wpa_config_update_psk(ssid); } - if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && !ssid->psk_set) { - wpa_printf(MSG_ERROR, "WPA-PSK accepted for key management, " - "but no PSK configured for network '" TSTR "'.", - netw); - errors++; - } - if ((ssid->group_cipher & WPA_CIPHER_CCMP) && !(ssid->pairwise_cipher & WPA_CIPHER_CCMP) && !(ssid->pairwise_cipher & WPA_CIPHER_NONE)) { diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 041685a4..059c586d 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -12,6 +12,7 @@ #include "utils/eloop.h" #include "common/version.h" #include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" #include "common/wpa_ctrl.h" #include "eap_peer/eap.h" #include "eapol_supp/eapol_supp_sm.h" @@ -29,6 +30,7 @@ #include "p2p_supplicant.h" #include "p2p/p2p.h" #include "hs20_supplicant.h" +#include "wifi_display.h" #include "notify.h" #include "bss.h" #include "scan.h" @@ -98,6 +100,9 @@ static int pno_start(struct wpa_supplicant *wpa_s) ssid = ssid->next; } + if (wpa_s->conf->filter_rssi) + params.filter_rssi = wpa_s->conf->filter_rssi; + ret = wpa_drv_sched_scan(wpa_s, ¶ms, 10 * 1000); os_free(params.filter_ssids); if (ret == 0) @@ -130,7 +135,7 @@ static int set_bssid_filter(struct wpa_supplicant *wpa_s, char *val) os_free(filter); return -1; } - n = os_realloc(filter, (count + 1) * ETH_ALEN); + n = os_realloc_array(filter, count + 1, ETH_ALEN); if (n == NULL) { os_free(filter); return -1; @@ -280,6 +285,10 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, } } else if (os_strcasecmp(cmd, "ps") == 0) { ret = wpa_drv_set_p2p_powersave(wpa_s, atoi(value), -1, -1); +#ifdef CONFIG_WIFI_DISPLAY + } else if (os_strcasecmp(cmd, "wifi_display") == 0) { + wifi_display_enable(wpa_s->global, !!atoi(value)); +#endif /* CONFIG_WIFI_DISPLAY */ } else if (os_strcasecmp(cmd, "bssid_filter") == 0) { ret = set_bssid_filter(wpa_s, value); } else { @@ -307,6 +316,14 @@ static int wpa_supplicant_ctrl_iface_get(struct wpa_supplicant *wpa_s, res = os_snprintf(buf, buflen, "%c%c", wpa_s->conf->country[0], wpa_s->conf->country[1]); +#ifdef CONFIG_WIFI_DISPLAY + } else if (os_strcasecmp(cmd, "wifi_display") == 0) { + res = os_snprintf(buf, buflen, "%d", + wpa_s->global->wifi_display); + if (res < 0 || (unsigned int) res >= buflen) + return -1; + return res; +#endif /* CONFIG_WIFI_DISPLAY */ } if (res < 0 || (unsigned int) res >= buflen) @@ -1121,7 +1138,9 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, #ifdef CONFIG_HS20 if (wpa_s->current_bss && - wpa_bss_get_vendor_ie(wpa_s->current_bss, HS20_IE_VENDOR_TYPE)) { + wpa_bss_get_vendor_ie(wpa_s->current_bss, HS20_IE_VENDOR_TYPE) && + wpa_s->wpa_proto == WPA_PROTO_RSN && + wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) { ret = os_snprintf(pos, end - pos, "hs20=1\n"); if (ret < 0 || ret >= end - pos) return pos - buf; @@ -1377,10 +1396,12 @@ static int wpa_supplicant_ctrl_iface_list_networks( if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; - ret = os_snprintf(pos, end - pos, "\t%s%s%s", + ret = os_snprintf(pos, end - pos, "\t%s%s%s%s", ssid == wpa_s->current_ssid ? "[CURRENT]" : "", ssid->disabled ? "[DISABLED]" : "", + ssid->disabled_until.sec ? + "[TEMP-DISABLED]" : "", ssid->disabled == 2 ? "[P2P-PERSISTENT]" : ""); if (ret < 0 || ret >= end - pos) @@ -1441,6 +1462,13 @@ static char * wpa_supplicant_cipher_txt(char *pos, char *end, int cipher) pos += ret; first = 0; } + if (cipher & WPA_CIPHER_GCMP) { + ret = os_snprintf(pos, end - pos, "%sGCMP", first ? "" : "+"); + if (ret < 0 || ret >= end - pos) + return pos; + pos += ret; + first = 0; + } return pos; } @@ -1640,7 +1668,7 @@ static int wpa_supplicant_ctrl_iface_scan_result( pos += ret; } #ifdef CONFIG_HS20 - if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE)) { + if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE) && ie2) { ret = os_snprintf(pos, end - pos, "[HS20]"); if (ret < 0 || ret >= end - pos) return -1; @@ -2163,6 +2191,14 @@ static int ctrl_iface_get_capability_pairwise(int res, char *strict, first = 0; } + if (capa->enc & WPA_DRIVER_CAPA_ENC_GCMP) { + ret = os_snprintf(pos, end - pos, "%sGCMP", first ? "" : " "); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + first = 0; + } + if (capa->enc & WPA_DRIVER_CAPA_ENC_TKIP) { ret = os_snprintf(pos, end - pos, "%sTKIP", first ? "" : " "); if (ret < 0 || ret >= end - pos) @@ -2211,6 +2247,14 @@ static int ctrl_iface_get_capability_group(int res, char *strict, first = 0; } + if (capa->enc & WPA_DRIVER_CAPA_ENC_GCMP) { + ret = os_snprintf(pos, end - pos, "%sGCMP", first ? "" : " "); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + first = 0; + } + if (capa->enc & WPA_DRIVER_CAPA_ENC_TKIP) { ret = os_snprintf(pos, end - pos, "%sTKIP", first ? "" : " "); if (ret < 0 || ret >= end - pos) @@ -2406,7 +2450,7 @@ static int ctrl_iface_get_capability_channels(struct wpa_supplicant *wpa_s, hmode = "A"; break; default: - return pos - buf; + continue; } ret = os_snprintf(pos, end - pos, "Mode[%s] Channels:", hmode); if (ret < 0 || ret >= end - pos) @@ -2414,6 +2458,8 @@ static int ctrl_iface_get_capability_channels(struct wpa_supplicant *wpa_s, pos += ret; chnl = wpa_s->hw.modes[j].channels; for (i = 0; i < wpa_s->hw.modes[j].num_channels; i++) { + if (chnl[i].flag & HOSTAPD_CHAN_DISABLED) + continue; ret = os_snprintf(pos, end - pos, " %d", chnl[i].chan); if (ret < 0 || ret >= end - pos) return pos - buf; @@ -2715,6 +2761,31 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, } #endif /* CONFIG_P2P */ +#ifdef CONFIG_WIFI_DISPLAY + if (mask & WPA_BSS_MASK_WIFI_DISPLAY) { + struct wpabuf *wfd; + ie = (const u8 *) (bss + 1); + wfd = ieee802_11_vendor_ie_concat(ie, bss->ie_len, + WFD_IE_VENDOR_TYPE); + if (wfd) { + ret = os_snprintf(pos, end - pos, "wfd_subelems="); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + pos += wpa_snprintf_hex(pos, end - pos, + wpabuf_head(wfd), + wpabuf_len(wfd)); + wpabuf_free(wfd); + + ret = os_snprintf(pos, end - pos, "\n"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + } +#endif /* CONFIG_WIFI_DISPLAY */ + #ifdef CONFIG_INTERWORKING if (mask & WPA_BSS_MASK_INTERNETW) { pos = anqp_add_hex(pos, end, "anqp_venue_name", @@ -2991,6 +3062,7 @@ static int p2p_ctrl_find(struct wpa_supplicant *wpa_s, char *cmd) enum p2p_discovery_type type = P2P_FIND_START_WITH_FULL; u8 dev_id[ETH_ALEN], *_dev_id = NULL; char *pos; + unsigned int search_delay; if (os_strstr(cmd, "type=social")) type = P2P_FIND_ONLY_SOCIAL; @@ -3005,7 +3077,15 @@ static int p2p_ctrl_find(struct wpa_supplicant *wpa_s, char *cmd) _dev_id = dev_id; } - return wpas_p2p_find(wpa_s, timeout, type, 0, NULL, _dev_id); + pos = os_strstr(cmd, "delay="); + if (pos) { + pos += 6; + search_delay = atoi(pos); + } else + search_delay = wpas_p2p_search_delay(wpa_s); + + return wpas_p2p_find(wpa_s, timeout, type, 0, NULL, _dev_id, + search_delay); } @@ -3025,10 +3105,12 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd, int go_intent = -1; int freq = 0; int pd; + int ht40; /* <addr> <"pbc" | "pin" | PIN> [label|display|keypad] * [persistent|persistent=<network id>] - * [join] [auth] [go_intent=<0..15>] [freq=<in MHz>] [provdisc] */ + * [join] [auth] [go_intent=<0..15>] [freq=<in MHz>] [provdisc] + * [ht40] */ if (hwaddr_aton(cmd, addr)) return -1; @@ -3056,6 +3138,7 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd, auth = os_strstr(pos, " auth") != NULL; automatic = os_strstr(pos, " auto") != NULL; pd = os_strstr(pos, " provdisc") != NULL; + ht40 = os_strstr(pos, " ht40") != NULL; pos2 = os_strstr(pos, " go_intent="); if (pos2) { @@ -3095,7 +3178,8 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd, new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method, persistent_group, automatic, join, - auth, go_intent, freq, persistent_id, pd); + auth, go_intent, freq, persistent_id, pd, + ht40); if (new_pin == -2) { os_memcpy(buf, "FAIL-CHANNEL-UNAVAILABLE\n", 25); return 25; @@ -3195,6 +3279,10 @@ static int p2p_ctrl_serv_disc_req(struct wpa_supplicant *wpa_s, char *cmd, return -1; pos++; ref = wpas_p2p_sd_request_upnp(wpa_s, dst, version, pos); +#ifdef CONFIG_WIFI_DISPLAY + } else if (os_strncmp(pos, "wifi-display ", 13) == 0) { + ref = wpas_p2p_sd_request_wifi_display(wpa_s, dst, pos + 13); +#endif /* CONFIG_WIFI_DISPLAY */ } else { len = os_strlen(pos); if (len & 1) @@ -3517,7 +3605,7 @@ static int p2p_ctrl_invite(struct wpa_supplicant *wpa_s, char *cmd) static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s, - char *cmd, int freq) + char *cmd, int freq, int ht40) { int id; struct wpa_ssid *ssid; @@ -3531,26 +3619,31 @@ static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s, return -1; } - return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq); + return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, ht40); } static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd) { - int freq = 0; + int freq = 0, ht40; char *pos; pos = os_strstr(cmd, "freq="); if (pos) freq = atoi(pos + 5); + ht40 = os_strstr(cmd, "ht40") != NULL; + if (os_strncmp(cmd, "persistent=", 11) == 0) - return p2p_ctrl_group_add_persistent(wpa_s, cmd + 11, freq); + return p2p_ctrl_group_add_persistent(wpa_s, cmd + 11, freq, + ht40); if (os_strcmp(cmd, "persistent") == 0 || os_strncmp(cmd, "persistent ", 11) == 0) - return wpas_p2p_group_add(wpa_s, 1, freq); + return wpas_p2p_group_add(wpa_s, 1, freq, ht40); if (os_strncmp(cmd, "freq=", 5) == 0) - return wpas_p2p_group_add(wpa_s, 0, freq); + return wpas_p2p_group_add(wpa_s, 0, freq, ht40); + if (ht40) + return wpas_p2p_group_add(wpa_s, 0, freq, ht40); wpa_printf(MSG_DEBUG, "CTRL: Invalid P2P_GROUP_ADD parameters '%s'", cmd); @@ -3653,8 +3746,8 @@ static int p2p_ctrl_disallow_freq(struct wpa_supplicant *wpa_s, */ pos = param; while (pos && pos[0]) { - n = os_realloc(freq, - (count + 1) * sizeof(struct wpa_freq_range)); + n = os_realloc_array(freq, count + 1, + sizeof(struct wpa_freq_range)); if (n == NULL) { os_free(freq); return -1; @@ -3957,6 +4050,122 @@ static int get_anqp(struct wpa_supplicant *wpa_s, char *dst) return anqp_send_req(wpa_s, dst_addr, id, num_id); } + + +static int gas_request(struct wpa_supplicant *wpa_s, char *cmd) +{ + u8 dst_addr[ETH_ALEN]; + struct wpabuf *advproto, *query = NULL; + int used, ret = -1; + char *pos, *end; + size_t len; + + used = hwaddr_aton2(cmd, dst_addr); + if (used < 0) + return -1; + + pos = cmd + used; + while (*pos == ' ') + pos++; + + /* Advertisement Protocol ID */ + end = os_strchr(pos, ' '); + if (end) + len = end - pos; + else + len = os_strlen(pos); + if (len & 0x01) + return -1; + len /= 2; + if (len == 0) + return -1; + advproto = wpabuf_alloc(len); + if (advproto == NULL) + return -1; + if (hexstr2bin(pos, wpabuf_put(advproto, len), len) < 0) + goto fail; + + if (end) { + /* Optional Query Request */ + pos = end + 1; + while (*pos == ' ') + pos++; + + len = os_strlen(pos); + if (len) { + if (len & 0x01) + goto fail; + len /= 2; + if (len == 0) + goto fail; + query = wpabuf_alloc(len); + if (query == NULL) + goto fail; + if (hexstr2bin(pos, wpabuf_put(query, len), len) < 0) + goto fail; + } + } + + ret = gas_send_request(wpa_s, dst_addr, advproto, query); + +fail: + wpabuf_free(advproto); + wpabuf_free(query); + + return ret; +} + + +static int gas_response_get(struct wpa_supplicant *wpa_s, char *cmd, char *buf, + size_t buflen) +{ + u8 addr[ETH_ALEN]; + int dialog_token; + int used; + char *pos; + size_t resp_len, start, requested_len; + + if (!wpa_s->last_gas_resp) + return -1; + + used = hwaddr_aton2(cmd, addr); + if (used < 0) + return -1; + + pos = cmd + used; + while (*pos == ' ') + pos++; + dialog_token = atoi(pos); + + if (os_memcmp(addr, wpa_s->last_gas_addr, ETH_ALEN) != 0 || + dialog_token != wpa_s->last_gas_dialog_token) + return -1; + + resp_len = wpabuf_len(wpa_s->last_gas_resp); + start = 0; + requested_len = resp_len; + + pos = os_strchr(pos, ' '); + if (pos) { + start = atoi(pos); + if (start > resp_len) + return os_snprintf(buf, buflen, "FAIL-Invalid range"); + pos = os_strchr(pos, ','); + if (pos == NULL) + return -1; + pos++; + requested_len = atoi(pos); + if (start + requested_len > resp_len) + return os_snprintf(buf, buflen, "FAIL-Invalid range"); + } + + if (requested_len * 2 + 1 > buflen) + return os_snprintf(buf, buflen, "FAIL-Too long response"); + + return wpa_snprintf_hex(buf, buflen, + wpabuf_head_u8(wpa_s->last_gas_resp) + start, + requested_len); +} #endif /* CONFIG_INTERWORKING */ @@ -4185,6 +4394,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, if (os_strcmp(buf, "PING") == 0) { os_memcpy(reply, "PONG\n", 5); reply_len = 5; + } else if (os_strcmp(buf, "IFNAME") == 0) { + reply_len = os_strlen(wpa_s->ifname); + os_memcpy(reply, wpa_s->ifname, reply_len); } else if (os_strncmp(buf, "RELOG", 5) == 0) { if (wpa_debug_reopen_file() < 0) reply_len = -1; @@ -4375,7 +4587,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, if (wpas_p2p_group_remove(wpa_s, buf + 17)) reply_len = -1; } else if (os_strcmp(buf, "P2P_GROUP_ADD") == 0) { - if (wpas_p2p_group_add(wpa_s, 0, 0)) + if (wpas_p2p_group_add(wpa_s, 0, 0, 0)) reply_len = -1; } else if (os_strncmp(buf, "P2P_GROUP_ADD ", 14) == 0) { if (p2p_ctrl_group_add(wpa_s, buf + 14)) @@ -4447,6 +4659,14 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, if (p2p_ctrl_ext_listen(wpa_s, "") < 0) reply_len = -1; #endif /* CONFIG_P2P */ +#ifdef CONFIG_WIFI_DISPLAY + } else if (os_strncmp(buf, "WFD_SUBELEM_SET ", 16) == 0) { + if (wifi_display_subelem_set(wpa_s->global, buf + 16) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "WFD_SUBELEM_GET ", 16) == 0) { + reply_len = wifi_display_subelem_get(wpa_s->global, buf + 16, + reply, reply_size); +#endif /* CONFIG_WIFI_DISPLAY */ #ifdef CONFIG_INTERWORKING } else if (os_strcmp(buf, "FETCH_ANQP") == 0) { if (interworking_fetch_anqp(wpa_s) < 0) @@ -4463,6 +4683,12 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "ANQP_GET ", 9) == 0) { if (get_anqp(wpa_s, buf + 9) < 0) reply_len = -1; + } else if (os_strncmp(buf, "GAS_REQUEST ", 12) == 0) { + if (gas_request(wpa_s, buf + 12) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "GAS_RESPONSE_GET ", 17) == 0) { + reply_len = gas_response_get(wpa_s, buf + 17, reply, + reply_size); #endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_HS20 } else if (os_strncmp(buf, "HS20_ANQP_GET ", 14) == 0) { diff --git a/wpa_supplicant/ctrl_iface_udp.c b/wpa_supplicant/ctrl_iface_udp.c index c831e6c9..994f9b18 100644 --- a/wpa_supplicant/ctrl_iface_udp.c +++ b/wpa_supplicant/ctrl_iface_udp.c @@ -163,6 +163,8 @@ static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, perror("recvfrom(ctrl_iface)"); return; } + +#ifndef CONFIG_CTRL_IFACE_UDP_REMOTE if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) { /* * The OS networking stack is expected to drop this kind of @@ -174,6 +176,8 @@ static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, "source %s", inet_ntoa(from.sin_addr)); return; } +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ + buf[res] = '\0'; if (os_strcmp(buf, "GET_COOKIE") == 0) { @@ -266,6 +270,7 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) { struct ctrl_iface_priv *priv; struct sockaddr_in addr; + int port = WPA_CTRL_IFACE_PORT; priv = os_zalloc(sizeof(*priv)); if (priv == NULL) @@ -285,13 +290,25 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) os_memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + addr.sin_addr.s_addr = INADDR_ANY; +#else /* CONFIG_CTRL_IFACE_UDP_REMOTE */ addr.sin_addr.s_addr = htonl((127 << 24) | 1); - addr.sin_port = htons(WPA_CTRL_IFACE_PORT); +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ +try_again: + addr.sin_port = htons(port); if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + port--; + if ((WPA_CTRL_IFACE_PORT - port) < WPA_CTRL_IFACE_PORT_LIMIT) + goto try_again; perror("bind(AF_INET)"); goto fail; } +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + wpa_msg(wpa_s, MSG_DEBUG, "ctrl_iface_init UDP port: %d", port); +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ + eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive, wpa_s, priv); wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb); @@ -442,6 +459,8 @@ static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx, perror("recvfrom(ctrl_iface)"); return; } + +#ifndef CONFIG_CTRL_IFACE_UDP_REMOTE if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) { /* * The OS networking stack is expected to drop this kind of @@ -453,6 +472,8 @@ static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx, "source %s", inet_ntoa(from.sin_addr)); return; } +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ + buf[res] = '\0'; if (os_strcmp(buf, "GET_COOKIE") == 0) { @@ -502,6 +523,7 @@ wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global) { struct ctrl_iface_global_priv *priv; struct sockaddr_in addr; + int port = WPA_GLOBAL_CTRL_IFACE_PORT; priv = os_zalloc(sizeof(*priv)); if (priv == NULL) @@ -523,13 +545,26 @@ wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global) os_memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + addr.sin_addr.s_addr = INADDR_ANY; +#else /* CONFIG_CTRL_IFACE_UDP_REMOTE */ addr.sin_addr.s_addr = htonl((127 << 24) | 1); - addr.sin_port = htons(WPA_GLOBAL_CTRL_IFACE_PORT); +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ +try_again: + addr.sin_port = htons(port); if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + port++; + if ((port - WPA_GLOBAL_CTRL_IFACE_PORT) < + WPA_GLOBAL_CTRL_IFACE_PORT_LIMIT) + goto try_again; perror("bind(AF_INET)"); goto fail; } +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + wpa_printf(MSG_DEBUG, "global_ctrl_iface_init UDP port: %d", port); +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ + eloop_register_read_sock(priv->sock, wpa_supplicant_global_ctrl_iface_receive, global, priv); diff --git a/wpa_supplicant/ctrl_iface_unix.c b/wpa_supplicant/ctrl_iface_unix.c index 67a0a8b1..5d81b43b 100644 --- a/wpa_supplicant/ctrl_iface_unix.c +++ b/wpa_supplicant/ctrl_iface_unix.c @@ -11,6 +11,8 @@ #include <sys/stat.h> #include <grp.h> #include <stddef.h> +#include <unistd.h> +#include <fcntl.h> #ifdef ANDROID #include <cutils/sockets.h> #endif /* ANDROID */ @@ -282,6 +284,7 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) char *buf, *dir = NULL, *gid_str = NULL; struct group *grp; char *endp; + int flags; priv = os_zalloc(sizeof(*priv)); if (priv == NULL) @@ -428,6 +431,20 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) #ifdef ANDROID havesock: #endif /* ANDROID */ + + /* + * Make socket non-blocking so that we don't hang forever if + * target dies unexpectedly. + */ + flags = fcntl(priv->sock, F_GETFL); + if (flags >= 0) { + flags |= O_NONBLOCK; + if (fcntl(priv->sock, F_SETFL, flags) < 0) { + perror("fcntl(ctrl, O_NONBLOCK)"); + /* Not fatal, continue on.*/ + } + } + eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive, wpa_s, priv); wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb); diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.c b/wpa_supplicant/dbus/dbus_dict_helpers.c index 67924e0b..61a94304 100644 --- a/wpa_supplicant/dbus/dbus_dict_helpers.c +++ b/wpa_supplicant/dbus/dbus_dict_helpers.c @@ -738,7 +738,7 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_byte_array( entry->bytearray_value = NULL; entry->array_type = DBUS_TYPE_BYTE; - buffer = os_zalloc(BYTE_ARRAY_ITEM_SIZE * BYTE_ARRAY_CHUNK_SIZE); + buffer = os_calloc(BYTE_ARRAY_CHUNK_SIZE, BYTE_ARRAY_ITEM_SIZE); if (!buffer) return FALSE; @@ -748,8 +748,9 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_byte_array( char byte; if ((count % BYTE_ARRAY_CHUNK_SIZE) == 0 && count != 0) { - nbuffer = os_realloc(buffer, BYTE_ARRAY_ITEM_SIZE * - (count + BYTE_ARRAY_CHUNK_SIZE)); + nbuffer = os_realloc_array( + buffer, count + BYTE_ARRAY_CHUNK_SIZE, + BYTE_ARRAY_ITEM_SIZE); if (nbuffer == NULL) { os_free(buffer); wpa_printf(MSG_ERROR, "dbus: _wpa_dbus_dict_" @@ -795,7 +796,7 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_string_array( entry->strarray_value = NULL; entry->array_type = DBUS_TYPE_STRING; - buffer = os_zalloc(STR_ARRAY_ITEM_SIZE * STR_ARRAY_CHUNK_SIZE); + buffer = os_calloc(STR_ARRAY_CHUNK_SIZE, STR_ARRAY_ITEM_SIZE); if (buffer == NULL) return FALSE; @@ -806,8 +807,9 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_string_array( char *str; if ((count % STR_ARRAY_CHUNK_SIZE) == 0 && count != 0) { - nbuffer = os_realloc(buffer, STR_ARRAY_ITEM_SIZE * - (count + STR_ARRAY_CHUNK_SIZE)); + nbuffer = os_realloc_array( + buffer, count + STR_ARRAY_CHUNK_SIZE, + STR_ARRAY_ITEM_SIZE); if (nbuffer == NULL) { os_free(buffer); wpa_printf(MSG_ERROR, "dbus: _wpa_dbus_dict_" @@ -871,8 +873,8 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_binarray( buflen += BIN_ARRAY_CHUNK_SIZE; - newbuf = os_realloc(entry->binarray_value, - buflen * BIN_ARRAY_ITEM_SIZE); + newbuf = os_realloc_array(entry->binarray_value, + buflen, BIN_ARRAY_ITEM_SIZE); if (!newbuf) goto cleanup; entry->binarray_value = newbuf; diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c index a9957abb..4eeb93a4 100644 --- a/wpa_supplicant/dbus/dbus_new.c +++ b/wpa_supplicant/dbus/dbus_new.c @@ -1104,7 +1104,6 @@ void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s, DBusMessage *msg; DBusMessageIter iter, dict_iter; struct wpas_dbus_priv *iface; - char net_obj_path[WPAS_DBUS_OBJECT_PATH_MAX]; char group_obj_path[WPAS_DBUS_OBJECT_PATH_MAX]; iface = wpa_s->parent->global->dbus; @@ -1142,14 +1141,8 @@ void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s, client ? "client" : "GO")) goto nomem; - os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, - "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%u", - wpa_s->parent->dbus_new_path, network_id); - if (!wpa_dbus_dict_append_object_path(&dict_iter, "group_object", group_obj_path) || - !wpa_dbus_dict_append_object_path(&dict_iter, "network_object", - net_obj_path) || !wpa_dbus_dict_close_write(&iter, &dict_iter)) goto nomem; @@ -2391,6 +2384,12 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { END_ARGS } }, + { "Reassociate", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) &wpas_dbus_handler_reassociate, + { + END_ARGS + } + }, { "RemoveNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE, (WPADBusMethodHandler) &wpas_dbus_handler_remove_network, { diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c index 8145a702..5668e1a1 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers.c +++ b/wpa_supplicant/dbus/dbus_new_handlers.c @@ -609,6 +609,7 @@ DBusMessage * wpas_dbus_handler_create_interface(DBusMessage *message, out: os_free(driver); os_free(ifname); + os_free(confname); os_free(bridge_ifname); return reply; @@ -870,7 +871,7 @@ dbus_bool_t wpas_dbus_getter_interfaces(DBusMessageIter *iter, for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) num++; - paths = os_zalloc(num * sizeof(char*)); + paths = os_calloc(num, sizeof(char *)); if (!paths) { dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); return FALSE; @@ -1158,8 +1159,9 @@ static int wpas_dbus_get_scan_channels(DBusMessage *message, #define FREQS_ALLOC_CHUNK 32 if (freqs_num % FREQS_ALLOC_CHUNK == 0) { - nfreqs = os_realloc(freqs, sizeof(int) * - (freqs_num + FREQS_ALLOC_CHUNK)); + nfreqs = os_realloc_array( + freqs, freqs_num + FREQS_ALLOC_CHUNK, + sizeof(int)); if (nfreqs == NULL) os_free(freqs); freqs = nfreqs; @@ -1179,8 +1181,7 @@ static int wpas_dbus_get_scan_channels(DBusMessage *message, dbus_message_iter_next(&array_iter); } - nfreqs = os_realloc(freqs, - sizeof(int) * (freqs_num + 1)); + nfreqs = os_realloc_array(freqs, freqs_num + 1, sizeof(int)); if (nfreqs == NULL) os_free(freqs); freqs = nfreqs; @@ -1404,6 +1405,33 @@ err: /** + * wpas_dbus_handler_reassociate - Reassociate to current AP + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NotConnected DBus error message if not connected + * or NULL otherwise. + * + * Handler function for "Reassociate" method call of network interface. + */ +DBusMessage * wpas_dbus_handler_reassociate(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + if (wpa_s->current_ssid != NULL) { + wpa_s->normal_scans = 0; + wpa_supplicant_reinit_autoscan(wpa_s); + wpa_s->disconnected = 0; + wpa_s->reassociate = 1; + wpa_supplicant_req_scan(wpa_s, 0, 0); + + return NULL; + } + + return dbus_message_new_error(message, WPAS_DBUS_ERROR_NOT_CONNECTED, + "This interface is not connected"); +} + + +/** * wpas_dbus_handler_remove_network - Remove a configured network * @message: Pointer to incoming dbus message * @wpa_s: wpa_supplicant structure for a network interface @@ -1901,6 +1929,12 @@ dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter, goto nomem; } + if (capa.enc & WPA_DRIVER_CAPA_ENC_GCMP) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "gcmp")) + goto nomem; + } + if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) { if (!wpa_dbus_dict_string_array_add_element( &iter_array, "tkip")) @@ -1942,6 +1976,12 @@ dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter, goto nomem; } + if (capa.enc & WPA_DRIVER_CAPA_ENC_GCMP) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "gcmp")) + goto nomem; + } + if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) { if (!wpa_dbus_dict_string_array_add_element( &iter_array, "tkip")) @@ -2430,7 +2470,7 @@ dbus_bool_t wpas_dbus_getter_bss_expire_count(DBusMessageIter *iter, void *user_data) { struct wpa_supplicant *wpa_s = user_data; - dbus_uint32_t expire_count = wpa_s->conf->bss_expiration_age; + dbus_uint32_t expire_count = wpa_s->conf->bss_expiration_scan_count; return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT32, &expire_count, error); @@ -2759,7 +2799,7 @@ dbus_bool_t wpas_dbus_getter_bsss(DBusMessageIter *iter, DBusError *error, unsigned int i = 0; dbus_bool_t success = FALSE; - paths = os_zalloc(wpa_s->num_bss * sizeof(char *)); + paths = os_calloc(wpa_s->num_bss, sizeof(char *)); if (!paths) { dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); return FALSE; @@ -2822,7 +2862,7 @@ dbus_bool_t wpas_dbus_getter_networks(DBusMessageIter *iter, DBusError *error, if (!network_is_persistent_group(ssid)) num++; - paths = os_zalloc(num * sizeof(char *)); + paths = os_calloc(num, sizeof(char *)); if (!paths) { dbus_set_error(error, DBUS_ERROR_NO_MEMORY, "no memory"); return FALSE; @@ -3154,7 +3194,7 @@ static dbus_bool_t wpas_dbus_get_bss_security_prop(DBusMessageIter *iter, { DBusMessageIter iter_dict, variant_iter; const char *group; - const char *pairwise[2]; /* max 2 pairwise ciphers is supported */ + const char *pairwise[3]; /* max 3 pairwise ciphers is supported */ const char *key_mgmt[7]; /* max 7 key managements may be supported */ int n; @@ -3197,6 +3237,9 @@ static dbus_bool_t wpas_dbus_get_bss_security_prop(DBusMessageIter *iter, case WPA_CIPHER_CCMP: group = "ccmp"; break; + case WPA_CIPHER_GCMP: + group = "gcmp"; + break; case WPA_CIPHER_WEP104: group = "wep104"; break; @@ -3214,6 +3257,8 @@ static dbus_bool_t wpas_dbus_get_bss_security_prop(DBusMessageIter *iter, pairwise[n++] = "tkip"; if (ie_data->pairwise_cipher & WPA_CIPHER_CCMP) pairwise[n++] = "ccmp"; + if (ie_data->pairwise_cipher & WPA_CIPHER_GCMP) + pairwise[n++] = "gcmp"; if (!wpa_dbus_dict_append_string_array(&iter_dict, "Pairwise", pairwise, n)) diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h index cff218f3..178a76b7 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers.h +++ b/wpa_supplicant/dbus/dbus_new_handlers.h @@ -94,6 +94,9 @@ dbus_bool_t set_network_properties(struct wpa_supplicant *wpa_s, DBusMessage * wpas_dbus_handler_add_network(DBusMessage *message, struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_reassociate(DBusMessage *message, + struct wpa_supplicant *wpa_s); + DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message, struct wpa_supplicant *wpa_s); diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c index f4541f73..aee8b3a5 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c +++ b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c @@ -127,7 +127,7 @@ DBusMessage * wpas_dbus_handler_p2p_find(DBusMessage *message, } wpas_p2p_find(wpa_s, timeout, type, num_req_dev_types, req_dev_types, - NULL); + NULL, 0); os_free(req_dev_types); return reply; @@ -346,13 +346,13 @@ DBusMessage * wpas_dbus_handler_p2p_group_add(DBusMessage *message, if (ssid == NULL || ssid->disabled != 2) goto inv_args; - if (wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq)) { + if (wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0)) { reply = wpas_dbus_error_unknown_error( message, "Failed to reinvoke a persistent group"); goto out; } - } else if (wpas_p2p_group_add(wpa_s, persistent_group, freq)) + } else if (wpas_p2p_group_add(wpa_s, persistent_group, freq, 0)) goto inv_args; out: @@ -504,7 +504,7 @@ DBusMessage * wpas_dbus_handler_p2p_connect(DBusMessage *message, new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method, persistent_group, 0, join, authorize_only, - go_intent, freq, -1, 0); + go_intent, freq, -1, 0, 0); if (new_pin >= 0) { char npin[9]; @@ -1049,7 +1049,7 @@ dbus_bool_t wpas_dbus_getter_p2p_peers(DBusMessageIter *iter, DBusError *error, * Now construct the peer object paths in a form suitable for * array_property_getter helper below. */ - peer_obj_paths = os_zalloc(num * sizeof(char *)); + peer_obj_paths = os_calloc(num, sizeof(char *)); if (!peer_obj_paths) { out_of_mem = 1; @@ -1509,7 +1509,7 @@ dbus_bool_t wpas_dbus_getter_persistent_groups(DBusMessageIter *iter, if (network_is_persistent_group(ssid)) num++; - paths = os_zalloc(num * sizeof(char *)); + paths = os_calloc(num, sizeof(char *)); if (!paths) { dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); return FALSE; @@ -1816,7 +1816,7 @@ dbus_bool_t wpas_dbus_getter_p2p_group_members(DBusMessageIter *iter, num_members = p2p_get_group_num_members(wpa_s->p2p_group); - paths = os_zalloc(num_members * sizeof(char *)); + paths = os_calloc(num_members, sizeof(char *)); if (!paths) goto out_of_memory; diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig index fe1401f7..711b4073 100644 --- a/wpa_supplicant/defconfig +++ b/wpa_supplicant/defconfig @@ -232,6 +232,7 @@ CONFIG_SMARTCARD=y # unix = UNIX domain sockets (default for Linux/*BSD) # udp = UDP sockets using localhost (127.0.0.1) # named_pipe = Windows Named Pipe (default for Windows) +# udp-remote = UDP sockets with remote access (only for tests systems/purpose) # y = use default (backwards compatibility) # If this option is commented out, control interface is not included in the # build. @@ -505,10 +506,18 @@ CONFIG_PEERKEY=y # Autoscan # This can be used to enable automatic scan support in wpa_supplicant. -# See wpa_supplicant.conf for more information on autoscan usage. +#Â See wpa_supplicant.conf for more information on autoscan usage. # # Enabling directly a module will enable autoscan support. # For exponential module: #CONFIG_AUTOSCAN_EXPONENTIAL=y # For periodic module: #CONFIG_AUTOSCAN_PERIODIC=y + +# Password (and passphrase, etc.) backend for external storage +# These optional mechanisms can be used to add support for storing passwords +# and other secrets in external (to wpa_supplicant) location. This allows, for +# example, operating system specific key storage to be used +# +# External password backend for testing purposes (developer use) +#CONFIG_EXT_PASSWORD_TEST=y diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h index 82a7bcb1..ee9f6b05 100644 --- a/wpa_supplicant/driver_i.h +++ b/wpa_supplicant/driver_i.h @@ -698,4 +698,14 @@ static inline int wpa_drv_switch_channel(struct wpa_supplicant *wpa_s, return wpa_s->driver->switch_channel(wpa_s->drv_priv, freq); } +static inline int wpa_drv_wnm_oper(struct wpa_supplicant *wpa_s, + enum wnm_oper oper, const u8 *peer, + u8 *buf, u16 *buf_len) +{ + if (!wpa_s->driver->wnm_oper) + return -1; + return wpa_s->driver->wnm_oper(wpa_s->drv_priv, oper, peer, buf, + buf_len); +} + #endif /* DRIVER_I_H */ diff --git a/wpa_supplicant/eap_register.c b/wpa_supplicant/eap_register.c index c220aec4..d1eb4ff3 100644 --- a/wpa_supplicant/eap_register.c +++ b/wpa_supplicant/eap_register.c @@ -35,6 +35,11 @@ int eap_register_methods(void) ret = eap_peer_tls_register(); #endif /* EAP_TLS */ +#ifdef EAP_UNAUTH_TLS + if (ret == 0) + ret = eap_peer_unauth_tls_register(); +#endif /* EAP_UNAUTH_TLS */ + #ifdef EAP_MSCHAPv2 if (ret == 0) ret = eap_peer_mschapv2_register(); @@ -145,6 +150,11 @@ int eap_register_methods(void) ret = eap_server_tls_register(); #endif /* EAP_SERVER_TLS */ +#ifdef EAP_SERVER_UNAUTH_TLS + if (ret == 0) + ret = eap_server_unauth_tls_register(); +#endif /* EAP_SERVER_UNAUTH_TLS */ + #ifdef EAP_SERVER_MSCHAPV2 if (ret == 0) ret = eap_server_mschapv2_register(); diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c index e53e1563..7d63c1b4 100644 --- a/wpa_supplicant/eapol_test.c +++ b/wpa_supplicant/eapol_test.c @@ -13,6 +13,7 @@ #include <assert.h> #include "common.h" +#include "utils/ext_password.h" #include "config.h" #include "eapol_supp/eapol_supp_sm.h" #include "eap_peer/eap.h" @@ -55,9 +56,8 @@ struct eapol_test_data { struct radius_client_data *radius; struct hostapd_radius_servers *radius_conf; - u8 *last_eap_radius; /* last received EAP Response from Authentication - * Server */ - size_t last_eap_radius_len; + /* last received EAP Response from Authentication Server */ + struct wpabuf *last_eap_radius; u8 authenticator_pmk[PMK_LEN]; size_t authenticator_pmk_len; @@ -488,7 +488,7 @@ static void test_eapol_clean(struct eapol_test_data *e, struct extra_radius_attr *p, *prev; radius_client_deinit(e->radius); - os_free(e->last_eap_radius); + wpabuf_free(e->last_eap_radius); radius_msg_free(e->last_recv_radius); e->last_recv_radius = NULL; os_free(e->eap_identity); @@ -506,6 +506,10 @@ static void test_eapol_clean(struct eapol_test_data *e, wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface); wpa_s->ctrl_iface = NULL; } + + ext_password_deinit(wpa_s->ext_pw); + wpa_s->ext_pw = NULL; + wpa_config_free(wpa_s->conf); p = e->extra_attrs; @@ -574,9 +578,8 @@ static char *eap_type_text(u8 type) static void ieee802_1x_decapsulate_radius(struct eapol_test_data *e) { - u8 *eap; - size_t len; - struct eap_hdr *hdr; + struct wpabuf *eap; + const struct eap_hdr *hdr; int eap_type = -1; char buf[64]; struct radius_msg *msg; @@ -586,30 +589,29 @@ static void ieee802_1x_decapsulate_radius(struct eapol_test_data *e) msg = e->last_recv_radius; - eap = radius_msg_get_eap(msg, &len); + eap = radius_msg_get_eap(msg); if (eap == NULL) { /* draft-aboba-radius-rfc2869bis-20.txt, Chap. 2.6.3: * RADIUS server SHOULD NOT send Access-Reject/no EAP-Message * attribute */ wpa_printf(MSG_DEBUG, "could not extract " "EAP-Message from RADIUS message"); - os_free(e->last_eap_radius); + wpabuf_free(e->last_eap_radius); e->last_eap_radius = NULL; - e->last_eap_radius_len = 0; return; } - if (len < sizeof(*hdr)) { + if (wpabuf_len(eap) < sizeof(*hdr)) { wpa_printf(MSG_DEBUG, "too short EAP packet " "received from authentication server"); - os_free(eap); + wpabuf_free(eap); return; } - if (len > sizeof(*hdr)) - eap_type = eap[sizeof(*hdr)]; + if (wpabuf_len(eap) > sizeof(*hdr)) + eap_type = (wpabuf_head_u8(eap))[sizeof(*hdr)]; - hdr = (struct eap_hdr *) eap; + hdr = wpabuf_head(eap); switch (hdr->code) { case EAP_CODE_REQUEST: os_snprintf(buf, sizeof(buf), "EAP-Request-%s (%d)", @@ -632,7 +634,7 @@ static void ieee802_1x_decapsulate_radius(struct eapol_test_data *e) break; default: os_strlcpy(buf, "unknown EAP code", sizeof(buf)); - wpa_hexdump(MSG_DEBUG, "Decapsulated EAP packet", eap, len); + wpa_hexdump_buf(MSG_DEBUG, "Decapsulated EAP packet", eap); break; } wpa_printf(MSG_DEBUG, "decapsulated EAP packet (code=%d " @@ -641,20 +643,21 @@ static void ieee802_1x_decapsulate_radius(struct eapol_test_data *e) /* sta->eapol_sm->be_auth.idFromServer = hdr->identifier; */ - os_free(e->last_eap_radius); + wpabuf_free(e->last_eap_radius); e->last_eap_radius = eap; - e->last_eap_radius_len = len; { struct ieee802_1x_hdr *dot1x; - dot1x = os_malloc(sizeof(*dot1x) + len); + dot1x = os_malloc(sizeof(*dot1x) + wpabuf_len(eap)); assert(dot1x != NULL); dot1x->version = EAPOL_VERSION; dot1x->type = IEEE802_1X_TYPE_EAP_PACKET; - dot1x->length = htons(len); - os_memcpy((u8 *) (dot1x + 1), eap, len); + dot1x->length = htons(wpabuf_len(eap)); + os_memcpy((u8 *) (dot1x + 1), wpabuf_head(eap), + wpabuf_len(eap)); eapol_sm_rx_eapol(e->wpa_s->eapol, e->wpa_s->bssid, - (u8 *) dot1x, sizeof(*dot1x) + len); + (u8 *) dot1x, + sizeof(*dot1x) + wpabuf_len(eap)); os_free(dot1x); } } @@ -1228,6 +1231,9 @@ int main(int argc, char *argv[]) if (test_eapol(&eapol_test, &wpa_s, wpa_s.conf->ssid)) return -1; + if (wpas_init_ext_pw(&wpa_s) < 0) + return -1; + if (wait_for_monitor) wpa_supplicant_ctrl_iface_wait(wpa_s.ctrl_iface); diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index 451bfd47..a6100082 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -23,6 +23,7 @@ #include "eap_peer/eap.h" #include "ap/hostapd.h" #include "p2p/p2p.h" +#include "wnm_sta.h" #include "notify.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" @@ -40,11 +41,31 @@ #include "bss.h" #include "scan.h" #include "offchannel.h" +#include "interworking.h" + + +static int wpas_temp_disabled(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + struct os_time now; + + if (ssid == NULL || ssid->disabled_until.sec == 0) + return 0; + + os_get_time(&now); + if (ssid->disabled_until.sec > now.sec) + return ssid->disabled_until.sec - now.sec; + + wpas_clear_temp_disabled(wpa_s, ssid, 0); + + return 0; +} static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s) { struct wpa_ssid *ssid, *old_ssid; + int res; if (wpa_s->conf->ap_scan == 1 && wpa_s->current_ssid) return 0; @@ -63,6 +84,13 @@ static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s) return -1; } + res = wpas_temp_disabled(wpa_s, ssid); + if (res > 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is temporarily " + "disabled for %d second(s)", res); + return -1; + } + wpa_dbg(wpa_s, MSG_DEBUG, "Network configuration found for the " "current AP"); if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) { @@ -148,6 +176,8 @@ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s) if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) eapol_sm_notify_eap_success(wpa_s->eapol, FALSE); wpa_s->ap_ies_from_associnfo = 0; + wpa_s->current_ssid = NULL; + wpa_s->key_mgmt = 0; } @@ -450,6 +480,12 @@ static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s, return 1; } + if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && !wpa_ie && + !rsn_ie) { + wpa_dbg(wpa_s, MSG_DEBUG, " allow for non-WPA IEEE 802.1X"); + return 1; + } + if ((ssid->proto & (WPA_PROTO_WPA | WPA_PROTO_RSN)) && wpa_key_mgmt_wpa(ssid->key_mgmt) && proto_match == 0) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - no WPA/RSN proto match"); @@ -648,12 +684,20 @@ static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, for (ssid = group; ssid; ssid = ssid->pnext) { int check_ssid = wpa ? 1 : (ssid->ssid_len != 0); + int res; if (wpas_network_disabled(wpa_s, ssid)) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - disabled"); continue; } + res = wpas_temp_disabled(wpa_s, ssid); + if (res > 0) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - disabled " + "temporarily for %d second(s)", res); + continue; + } + #ifdef CONFIG_WPS if ((ssid->key_mgmt & WPA_KEY_MGMT_WPS) && e && e->count > 0) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - blacklisted " @@ -1032,7 +1076,7 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, #else if (wpa_s->p2p_cb_on_scan_complete && !wpa_s->global->p2p_disabled && #endif - wpa_s->global->p2p != NULL) { + wpa_s->global->p2p != NULL && !wpa_s->sta_scan_pending) { wpa_s->p2p_cb_on_scan_complete = 0; if (p2p_other_scan_completed(wpa_s->global->p2p) == 1) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Pending P2P operation " @@ -1040,6 +1084,7 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, return -1; } } + wpa_s->sta_scan_pending = 0; #endif /* CONFIG_P2P */ scan_res = wpa_supplicant_get_scan_results(wpa_s, @@ -1134,6 +1179,8 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, return 0; } + wpas_wps_update_ap_info(wpa_s, scan_res); + selected = wpa_supplicant_pick_network(wpa_s, scan_res, &ssid); if (selected) { @@ -1178,6 +1225,19 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, return 0; } #endif /* CONFIG_P2P */ +#ifdef CONFIG_INTERWORKING + if (wpa_s->conf->auto_interworking && + wpa_s->conf->interworking && + wpa_s->conf->cred) { + wpa_dbg(wpa_s, MSG_DEBUG, "Interworking: " + "start ANQP fetch since no matching " + "networks found"); + wpa_s->network_select = 1; + wpa_s->auto_network_select = 1; + interworking_start_fetch_anqp(wpa_s); + return 0; + } +#endif /* CONFIG_INTERWORKING */ if (wpa_supplicant_req_sched_scan(wpa_s)) wpa_supplicant_req_new_scan(wpa_s, timeout_sec, timeout_usec); @@ -1548,7 +1608,6 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, { u8 bssid[ETH_ALEN]; int ft_completed; - int bssid_changed; struct wpa_driver_capa capa; #ifdef CONFIG_AP @@ -1566,17 +1625,21 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, if (data && wpa_supplicant_event_associnfo(wpa_s, data) < 0) return; + if (wpa_drv_get_bssid(wpa_s, bssid) < 0) { + wpa_dbg(wpa_s, MSG_ERROR, "Failed to get BSSID"); + wpa_supplicant_disassociate( + wpa_s, WLAN_REASON_DEAUTH_LEAVING); + return; + } + wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATED); - if (wpa_drv_get_bssid(wpa_s, bssid) >= 0 && - os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0) { + 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)); random_add_randomness(bssid, ETH_ALEN); - bssid_changed = os_memcmp(wpa_s->bssid, bssid, ETH_ALEN); os_memcpy(wpa_s->bssid, bssid, ETH_ALEN); os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); - if (bssid_changed) - wpas_notify_bssid_changed(wpa_s); + wpas_notify_bssid_changed(wpa_s); if (wpa_supplicant_dynamic_keys(wpa_s) && !ft_completed) { wpa_clear_keys(wpa_s, bssid); @@ -1728,6 +1791,8 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, ibss_rsn_set_psk(wpa_s->ibss_rsn, wpa_s->current_ssid->psk); } #endif /* CONFIG_IBSS_RSN */ + + wpas_wps_notify_assoc(wpa_s, bssid); } @@ -1767,6 +1832,7 @@ static void wpa_supplicant_event_disassoc(struct wpa_supplicant *wpa_s, wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) { wpa_msg(wpa_s, MSG_INFO, "WPA: 4-Way Handshake failed - " "pre-shared key may be incorrect"); + wpas_auth_failed(wpa_s); } if (!wpa_s->auto_reconnect_disabled || wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) { @@ -2037,6 +2103,25 @@ static void wpa_supplicant_event_tdls(struct wpa_supplicant *wpa_s, #endif /* CONFIG_TDLS */ +#ifdef CONFIG_IEEE80211V +static void wpa_supplicant_event_wnm(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + if (data == NULL) + return; + switch (data->wnm.oper) { + case WNM_OPER_SLEEP: + wpa_printf(MSG_DEBUG, "Start sending WNM-Sleep Request " + "(action=%d, intval=%d)", + data->wnm.sleep_action, data->wnm.sleep_intval); + ieee802_11_send_wnmsleep_req(wpa_s, data->wnm.sleep_action, + data->wnm.sleep_intval); + break; + } +} +#endif /* CONFIG_IEEE80211V */ + + #ifdef CONFIG_IEEE80211R static void wpa_supplicant_event_ft_response(struct wpa_supplicant *wpa_s, @@ -2323,6 +2408,11 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, #endif /* CONFIG_AP */ wpa_supplicant_event_disassoc(wpa_s, reason_code, locally_generated); + if (reason_code == WLAN_REASON_IEEE_802_1X_AUTH_FAILED || + ((wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) || + (wpa_s->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) && + eapol_sm_failed(wpa_s->eapol))) + wpas_auth_failed(wpa_s); #ifdef CONFIG_P2P if (event == EVENT_DEAUTH && data) { wpas_p2p_deauth_notif(wpa_s, data->deauth_info.addr, @@ -2339,6 +2429,18 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, #ifndef CONFIG_NO_SCAN_PROCESSING case EVENT_SCAN_RESULTS: wpa_supplicant_event_scan_results(wpa_s, data); +#ifdef CONFIG_P2P + if (wpa_s->p2p_cb_on_scan_complete && !wpa_s->global->p2p_disabled && + wpa_s->global->p2p != NULL && + wpa_s->wpa_state != WPA_AUTHENTICATING && + wpa_s->wpa_state != WPA_ASSOCIATING) { + wpa_s->p2p_cb_on_scan_complete = 0; + if (p2p_other_scan_completed(wpa_s->global->p2p) == 1) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Pending P2P operation " + "continued after scan result processing"); + } + } +#endif /* CONFIG_P2P */ break; #endif /* CONFIG_NO_SCAN_PROCESSING */ case EVENT_ASSOCINFO: @@ -2360,6 +2462,11 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, wpa_supplicant_event_tdls(wpa_s, data); break; #endif /* CONFIG_TDLS */ +#ifdef CONFIG_IEEE80211V + case EVENT_WNM: + wpa_supplicant_event_wnm(wpa_s, data); + break; +#endif /* CONFIG_IEEE80211V */ #ifdef CONFIG_IEEE80211R case EVENT_FT_RESPONSE: wpa_supplicant_event_ft_response(wpa_s, data); @@ -2585,6 +2692,12 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, } #endif /* CONFIG_SME */ #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_IEEE80211V + if (data->rx_action.category == WLAN_ACTION_WNM) { + ieee802_11_rx_wnm_action(wpa_s, &data->rx_action); + break; + } +#endif /* CONFIG_IEEE80211V */ #ifdef CONFIG_GAS if (data->rx_action.category == WLAN_ACTION_PUBLIC && gas_query_rx(wpa_s->gas, data->rx_action.da, diff --git a/wpa_supplicant/examples/p2p/p2p_connect.py b/wpa_supplicant/examples/p2p/p2p_connect.py new file mode 100644 index 00000000..59b0a9d3 --- /dev/null +++ b/wpa_supplicant/examples/p2p/p2p_connect.py @@ -0,0 +1,299 @@ +#!/usr/bin/python +# Tests p2p_connect +# Will try to connect to another peer +# and form a group +######### MAY NEED TO RUN AS SUDO ############# + +import dbus +import sys, os +import time +import gobject +import getopt +from dbus.mainloop.glib import DBusGMainLoop + + +def usage(): + print "Usage:" + print " %s -i <interface_name> -m <wps_method> \ " \ + % sys.argv[0] + print " -a <addr> [-p <pin>] [-g <go_intent>] \ " + print " [-w <wpas_dbus_interface>]" + print "Options:" + print " -i = interface name" + print " -m = wps method" + print " -a = peer address" + print " -p = pin number (8 digits)" + print " -g = group owner intent" + print " -w = wpas dbus interface = fi.w1.wpa_supplicant1" + print "Example:" + print " %s -i wlan0 -a 0015008352c0 -m display -p 12345670" % sys.argv[0] + + +# Required Signals +def GONegotiationSuccess(status): + print "Go Negotiation Success" + +def GONegotiationFailure(status): + print 'Go Negotiation Failed. Status:' + print format(status) + os._exit(0) + +def GroupStarted(properties): + if properties.has_key("group_object"): + print 'Group Formation Complete %s' \ + % properties["group_object"] + os._exit(0) + +def WpsFailure(status, etc): + print "WPS Authentication Failure".format(status) + print etc + os._exit(0) + +class P2P_Connect(): + # Needed Variables + global bus + global wpas_object + global interface_object + global p2p_interface + global ifname + global wpas + global wpas_dbus_interface + global timeout + global path + global wps_method + global go_intent + global addr + global pin + + # Dbus Paths + global wpas_dbus_opath + global wpas_dbus_interfaces_opath + global wpas_dbus_interfaces_interface + global wpas_dbus_interfaces_p2pdevice + + # Dictionary of Arguements + global p2p_connect_arguements + + # Constructor + def __init__(self,ifname,wpas_dbus_interface,addr, + pin,wps_method,go_intent): + # Initializes variables and threads + self.ifname = ifname + self.wpas_dbus_interface = wpas_dbus_interface + self.wps_method = wps_method + self.go_intent = go_intent + self.addr = addr + self.pin = pin + + # Generating interface/object paths + self.wpas_dbus_opath = \ + "/" + self.wpas_dbus_interface.replace(".","/") + self.wpas_wpas_dbus_interfaces_opath = \ + self.wpas_dbus_opath + "/Interfaces" + self.wpas_dbus_interfaces_interface = \ + self.wpas_dbus_interface + ".Interface" + self.wpas_dbus_interfaces_p2pdevice = \ + self.wpas_dbus_interfaces_interface + ".P2PDevice" + + # Getting interfaces and objects + DBusGMainLoop(set_as_default=True) + self.bus = dbus.SystemBus() + self.wpas_object = self.bus.get_object( + self.wpas_dbus_interface, + self.wpas_dbus_opath) + self.wpas = dbus.Interface( + self.wpas_object, self.wpas_dbus_interface) + + # See if wpa_supplicant already knows about this interface + self.path = None + try: + self.path = self.wpas.GetInterface(ifname) + except: + if not str(exc).startswith( + self.wpas_dbus_interface + \ + ".InterfaceUnknown:"): + raise exc + try: + path = self.wpas.CreateInterface( + {'Ifname': ifname, 'Driver': 'test'}) + time.sleep(1) + + except dbus.DBusException, exc: + if not str(exc).startswith( + self.wpas_dbus_interface + \ + ".InterfaceExists:"): + raise exc + + # Get Interface and objects + self.interface_object = self.bus.get_object( + self.wpas_dbus_interface,self.path) + self.p2p_interface = dbus.Interface( + self.interface_object, + self.wpas_dbus_interfaces_p2pdevice) + + # Add signals + self.bus.add_signal_receiver(GONegotiationSuccess, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="GONegotiationSuccess") + self.bus.add_signal_receiver(GONegotiationFailure, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="GONegotiationFailure") + self.bus.add_signal_receiver(GroupStarted, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="GroupStarted") + self.bus.add_signal_receiver(WpsFailure, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="WpsFailed") + + + #Constructing all the arguements needed to connect + def constructArguements(self): + # Adding required arguements + self.p2p_connect_arguements = {'wps_method':self.wps_method, + 'peer':dbus.ObjectPath(self.path+'/Peers/'+self.addr)} + + # Display requires a pin, and a go intent of 15 + if (self.wps_method == 'display'): + if (self.pin != None): + self.p2p_connect_arguements.update({'pin':self.pin}) + else: + print "Error:\n Pin required for wps_method=display" + usage() + quit() + + if (self.go_intent != None and int(self.go_intent) != 15): + print "go_intent overwritten to 15" + + self.go_intent = '15' + + # Keypad requires a pin, and a go intent of less than 15 + elif (self.wps_method == 'keypad'): + if (self.pin != None): + self.p2p_connect_arguements.update({'pin':self.pin}) + else: + print "Error:\n Pin required for wps_method=keypad" + usage() + quit() + + if (self.go_intent != None and int(self.go_intent) == 15): + error = "Error :\n Group Owner intent cannot be" + \ + " 15 for wps_method=keypad" + print error + usage() + quit() + + # Doesn't require pin + # for ./wpa_cli, p2p_connect [mac] [pin#], wps_method=keypad + elif (self.wps_method == 'pin'): + if (self.pin != None): + print "pin ignored" + + # No pin is required for pbc so it is ignored + elif (self.wps_method == 'pbc'): + if (self.pin != None): + print "pin ignored" + + else: + print "Error:\n wps_method not supported or does not exist" + usage() + quit() + + # Go_intent is optional for all arguements + if (self.go_intent != None): + self.p2p_connect_arguements.update( + {'go_intent':dbus.Int32(self.go_intent)}) + + # Running p2p_connect + def run(self): + try: + result_pin = self.p2p_interface.Connect( + self.p2p_connect_arguements) + + except dbus.DBusException, exc: + raise exc + + if (self.wps_method == 'pin' and \ + not self.p2p_connect_arguements.has_key('pin') ): + print "Connect return with pin value of %d " % int(result_pin) + gobject.MainLoop().run() + +if __name__ == "__main__": + + # Required + interface_name = None + wps_method = None + addr = None + + # Conditionally optional + pin = None + + # Optional + wpas_dbus_interface = 'fi.w1.wpa_supplicant1' + go_intent = None + + # Using getopts to handle options + try: + options, args = getopt.getopt(sys.argv[1:],"hi:m:a:p:g:w:") + + except getopt.GetoptError: + usage() + quit() + + # If theres a switch, override default option + for key, value in options: + # Help + if (key == "-h"): + usage() + quit() + # Interface Name + elif (key == "-i"): + interface_name = value + # WPS Method + elif (key == "-m"): + wps_method = value + # Address + elif (key == "-a"): + addr = value + # Pin + elif (key == "-p"): + pin = value + # Group Owner Intent + elif (key == "-g"): + go_intent = value + # Dbus interface + elif (key == "-w"): + wpas_dbus_interface = value + else: + assert False, "unhandled option" + + # Required Arguements check + if (interface_name == None or wps_method == None or addr == None): + print "Error:\n Required arguements not specified" + usage() + quit() + + # Group Owner Intent Check + if (go_intent != None and (int(go_intent) > 15 or int(go_intent) < 0) ): + print "Error:\n Group Owner Intent must be between 0 and 15 inclusive" + usage() + quit() + + # Pin Check + if (pin != None and len(pin) != 8): + print "Error:\n Pin is not 8 digits" + usage() + quit() + + try: + p2p_connect_test = P2P_Connect(interface_name,wpas_dbus_interface, + addr,pin,wps_method,go_intent) + + except: + print "Error:\n Invalid Arguements" + usage() + quit() + + p2p_connect_test.constructArguements() + p2p_connect_test.run() + + os._exit(0) diff --git a/wpa_supplicant/examples/p2p/p2p_disconnect.py b/wpa_supplicant/examples/p2p/p2p_disconnect.py new file mode 100644 index 00000000..c3e39b3d --- /dev/null +++ b/wpa_supplicant/examples/p2p/p2p_disconnect.py @@ -0,0 +1,169 @@ +#!/usr/bin/python +# Tests P2P_Disconnect +# Will perform disconnect on interface_name +######### MAY NEED TO RUN AS SUDO ############# + +import dbus +import sys, os +import time +import gobject +import threading +import getopt +from dbus.mainloop.glib import DBusGMainLoop + +def usage(): + print "Usage:" + print " %s -i <interface_name> \ " \ + % sys.argv[0] + print " [-w <wpas_dbus_interface>]" + print "Options:" + print " -i = interface name" + print " -w = wpas dbus interface = fi.w1.wpa_supplicant1" + print "Example:" + print " %s -i p2p-wlan0-0" % sys.argv[0] + +# Required Signals +def GroupFinished(status, etc): + print "Disconnected" + os._exit(0) + +class P2P_Disconnect (threading.Thread): + # Needed Variables + global bus + global wpas_object + global interface_object + global p2p_interface + global interface_name + global wpas + global wpas_dbus_interface + global path + global timeout + + # Dbus Paths + global wpas_dbus_opath + global wpas_dbus_interfaces_opath + global wpas_dbus_interfaces_interface + global wpas_dbus_interfaces_p2pdevice + + # Constructor + def __init__(self,interface_name,wpas_dbus_interface,timeout): + # Initializes variables and threads + self.interface_name = interface_name + self.wpas_dbus_interface = wpas_dbus_interface + self.timeout = timeout + + # Initializes thread and daemon allows for ctrl-c kill + threading.Thread.__init__(self) + self.daemon = True + + # Generating interface/object paths + self.wpas_dbus_opath = "/" + \ + self.wpas_dbus_interface.replace(".","/") + self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \ + "/Interfaces" + self.wpas_dbus_interfaces_interface = \ + self.wpas_dbus_interface + ".Interface" + self.wpas_dbus_interfaces_p2pdevice = \ + self.wpas_dbus_interfaces_interface \ + + ".P2PDevice" + + # Getting interfaces and objects + DBusGMainLoop(set_as_default=True) + self.bus = dbus.SystemBus() + self.wpas_object = self.bus.get_object( + self.wpas_dbus_interface, + self.wpas_dbus_opath) + self.wpas = dbus.Interface(self.wpas_object, + self.wpas_dbus_interface) + + # Try to see if supplicant knows about interface + # If not, throw an exception + try: + self.path = self.wpas.GetInterface( + self.interface_name) + except dbus.DBusException, exc: + error = 'Error:\n Interface ' + self.interface_name \ + + ' was not found' + print error + usage() + os._exit(0) + + self.interface_object = self.bus.get_object( + self.wpas_dbus_interface, self.path) + self.p2p_interface = dbus.Interface(self.interface_object, + self.wpas_dbus_interfaces_p2pdevice) + + # Signals + self.bus.add_signal_receiver(GroupFinished, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="GroupFinished") + + # Runs p2p_disconnect + def run(self): + # Allows other threads to keep working while MainLoop runs + # Required for timeout implementation + gobject.MainLoop().get_context().iteration(True) + gobject.threads_init() + self.p2p_interface.Disconnect() + gobject.MainLoop().run() + + +if __name__ == "__main__": + + timeout = 5 + # Defaults for optional inputs + wpas_dbus_interface = 'fi.w1.wpa_supplicant1' + + # interface_name is required + interface_name = None + + # Using getopts to handle options + try: + options, args = getopt.getopt(sys.argv[1:],"hi:w:") + + except getopt.GetoptError: + usage() + quit() + + # If theres a switch, override default option + for key, value in options: + # Help + if (key == "-h"): + usage() + quit() + # Interface Name + elif (key == "-i"): + interface_name = value + # Dbus interface + elif (key == "-w"): + wpas_dbus_interface = value + else: + assert False, "unhandled option" + + # Interface name is required and was not given + if (interface_name == None): + print "Error:\n interface_name is required" + usage() + quit() + + # Constructor + try: + p2p_disconnect_test = P2P_Disconnect(interface_name, + wpas_dbus_interface,timeout) + + except: + print "Error:\n Invalid wpas_dbus_interface" + usage() + quit() + + # Start P2P_Disconnect + p2p_disconnect_test.start() + + try: + time.sleep(int(p2p_disconnect_test.timeout)) + + except: + pass + + print "Disconnect timed out" + quit() diff --git a/wpa_supplicant/examples/p2p/p2p_find.py b/wpa_supplicant/examples/p2p/p2p_find.py new file mode 100644 index 00000000..973d46ab --- /dev/null +++ b/wpa_supplicant/examples/p2p/p2p_find.py @@ -0,0 +1,192 @@ +#!/usr/bin/python +# Tests p2p_find +# Will list all devices found/lost within a time frame (timeout) +# Then Program will exit +######### MAY NEED TO RUN AS SUDO ############# + +import dbus +import sys, os +import time +import gobject +import threading +import getopt +from dbus.mainloop.glib import DBusGMainLoop + +def usage(): + print "Usage:" + print " %s -i <interface_name> [-t <timeout>] \ " \ + % sys.argv[0] + print " [-w <wpas_dbus_interface>]" + print "Options:" + print " -i = interface name" + print " -t = timeout = 0s (infinite)" + print " -w = wpas dbus interface = fi.w1.wpa_supplicant1" + print "Example:" + print " %s -i wlan0 -t 10" % sys.argv[0] + +# Required Signals +def deviceFound(devicepath): + print "Device found: %s" % (devicepath) + +def deviceLost(devicepath): + print "Device lost: %s" % (devicepath) + +class P2P_Find (threading.Thread): + # Needed Variables + global bus + global wpas_object + global interface_object + global p2p_interface + global interface_name + global wpas + global wpas_dbus_interface + global timeout + global path + + # Dbus Paths + global wpas_dbus_opath + global wpas_dbus_interfaces_opath + global wpas_dbus_interfaces_interface + global wpas_dbus_interfaces_p2pdevice + + # Constructor + def __init__(self,interface_name,wpas_dbus_interface,timeout): + # Initializes variables and threads + self.timeout = int(timeout) + self.interface_name = interface_name + self.wpas_dbus_interface = wpas_dbus_interface + + # Initializes thread and daemon allows for ctrl-c kill + threading.Thread.__init__(self) + self.daemon = True + + # Generating interface/object paths + self.wpas_dbus_opath = "/" + \ + self.wpas_dbus_interface.replace(".","/") + self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \ + "/Interfaces" + self.wpas_dbus_interfaces_interface = \ + self.wpas_dbus_interface + ".Interface" + self.wpas_dbus_interfaces_p2pdevice = \ + self.wpas_dbus_interfaces_interface \ + + ".P2PDevice" + + # Getting interfaces and objects + DBusGMainLoop(set_as_default=True) + self.bus = dbus.SystemBus() + self.wpas_object = self.bus.get_object( + self.wpas_dbus_interface, + self.wpas_dbus_opath) + self.wpas = dbus.Interface(self.wpas_object, + self.wpas_dbus_interface) + + # Try to see if supplicant knows about interface + # If not, throw an exception + try: + self.path = self.wpas.GetInterface( + self.interface_name) + except dbus.DBusException, exc: + error = 'Error:\n Interface ' + self.interface_name \ + + ' was not found' + print error + usage() + os._exit(0) + + self.interface_object = self.bus.get_object( + self.wpas_dbus_interface, self.path) + self.p2p_interface = dbus.Interface(self.interface_object, + self.wpas_dbus_interfaces_p2pdevice) + + #Adds listeners for find and lost + self.bus.add_signal_receiver(deviceFound, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="DeviceFound") + self.bus.add_signal_receiver(deviceLost, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="DeviceLost") + + + # Sets up p2p_find + P2PFindDict = dbus.Dictionary( + {'Timeout':int(self.timeout)}) + self.p2p_interface.Find(P2PFindDict) + + # Run p2p_find + def run(self): + # Allows other threads to keep working while MainLoop runs + # Required for timeout implementation + gobject.MainLoop().get_context().iteration(True) + gobject.threads_init() + gobject.MainLoop().run() + +if __name__ == "__main__": + + # Defaults for optional inputs + timeout = 0 + wpas_dbus_interface = 'fi.w1.wpa_supplicant1' + + # interface_name is required + interface_name = None + + # Using getopts to handle options + try: + options, args = getopt.getopt(sys.argv[1:],"hi:t:w:") + + except getopt.GetoptError: + usage() + quit() + + # If theres a switch, override default option + for key, value in options: + # Help + if (key == "-h"): + usage() + quit() + # Interface Name + elif (key == "-i"): + interface_name = value + # Timeout + elif (key == "-t"): + if ( int(value) >= 0): + timeout = value + else: + print "Error:\n Timeout cannot be negative" + usage() + quit() + # Dbus interface + elif (key == "-w"): + wpas_dbus_interface = value + else: + assert False, "unhandled option" + + # Interface name is required and was not given + if (interface_name == None): + print "Error:\n interface_name is required" + usage() + quit() + + # Constructor + try: + p2p_find_test = P2P_Find(interface_name, wpas_dbus_interface, timeout) + + except: + print "Error:\n Invalid wpas_dbus_interface" + usage() + quit() + + # Start P2P_Find + p2p_find_test.start() + + try: + # If timeout is 0, then run forever + if (timeout == 0): + while(True): + pass + # Else sleep for (timeout) + else: + time.sleep(p2p_find_test.timeout) + + except: + pass + + quit() diff --git a/wpa_supplicant/examples/p2p/p2p_flush.py b/wpa_supplicant/examples/p2p/p2p_flush.py new file mode 100644 index 00000000..ff8509d6 --- /dev/null +++ b/wpa_supplicant/examples/p2p/p2p_flush.py @@ -0,0 +1,168 @@ +#!/usr/bin/python +# Tests P2P_Flush +# Will flush the p2p interface +# Then Program will exit +######### MAY NEED TO RUN AS SUDO ############# + +import dbus +import sys, os +import time +import gobject +import threading +import getopt +from dbus.mainloop.glib import DBusGMainLoop + +def usage(): + print "Usage:" + print " %s -i <interface_name> \ " \ + % sys.argv[0] + print " [-w <wpas_dbus_interface>]" + print "Options:" + print " -i = interface name" + print " -w = wpas dbus interface = fi.w1.wpa_supplicant1" + print "Example:" + print " %s -i wlan0" % sys.argv[0] + +# Required Signals\ +def deviceLost(devicepath): + print "Device lost: %s" % (devicepath) + +class P2P_Flush (threading.Thread): + # Needed Variables + global bus + global wpas_object + global interface_object + global p2p_interface + global interface_name + global wpas + global wpas_dbus_interface + global path + global timeout + + # Dbus Paths + global wpas_dbus_opath + global wpas_dbus_interfaces_opath + global wpas_dbus_interfaces_interface + global wpas_dbus_interfaces_p2pdevice + + # Constructor + def __init__(self,interface_name,wpas_dbus_interface,timeout): + # Initializes variables and threads + self.interface_name = interface_name + self.wpas_dbus_interface = wpas_dbus_interface + self.timeout = timeout + + # Initializes thread and daemon allows for ctrl-c kill + threading.Thread.__init__(self) + self.daemon = True + + # Generating interface/object paths + self.wpas_dbus_opath = "/" + \ + self.wpas_dbus_interface.replace(".","/") + self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \ + "/Interfaces" + self.wpas_dbus_interfaces_interface = \ + self.wpas_dbus_interface + ".Interface" + self.wpas_dbus_interfaces_p2pdevice = \ + self.wpas_dbus_interfaces_interface \ + + ".P2PDevice" + + # Getting interfaces and objects + DBusGMainLoop(set_as_default=True) + self.bus = dbus.SystemBus() + self.wpas_object = self.bus.get_object( + self.wpas_dbus_interface, + self.wpas_dbus_opath) + self.wpas = dbus.Interface(self.wpas_object, + self.wpas_dbus_interface) + + # Try to see if supplicant knows about interface + # If not, throw an exception + try: + self.path = self.wpas.GetInterface( + self.interface_name) + except dbus.DBusException, exc: + error = 'Error:\n Interface ' + self.interface_name \ + + ' was not found' + print error + usage() + os._exit(0) + + self.interface_object = self.bus.get_object( + self.wpas_dbus_interface, self.path) + self.p2p_interface = dbus.Interface(self.interface_object, + self.wpas_dbus_interfaces_p2pdevice) + + # Signals + self.bus.add_signal_receiver(deviceLost, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="DeviceLost") + + # Runs p2p_flush + def run(self): + # Allows other threads to keep working while MainLoop runs + # Required for timeout implementation + gobject.MainLoop().get_context().iteration(True) + gobject.threads_init() + self.p2p_interface.Flush() + gobject.MainLoop().run() + + +if __name__ == "__main__": + # Needed to show which devices were lost + timeout = 5 + # Defaults for optional inputs + wpas_dbus_interface = 'fi.w1.wpa_supplicant1' + + # interface_name is required + interface_name = None + + # Using getopts to handle options + try: + options, args = getopt.getopt(sys.argv[1:],"hi:w:") + + except getopt.GetoptError: + usage() + quit() + + # If theres a switch, override default option + for key, value in options: + # Help + if (key == "-h"): + usage() + quit() + # Interface Name + elif (key == "-i"): + interface_name = value + # Dbus interface + elif (key == "-w"): + wpas_dbus_interface = value + else: + assert False, "unhandled option" + + # Interface name is required and was not given + if (interface_name == None): + print "Error:\n interface_name is required" + usage() + quit() + + # Constructor + try: + p2p_flush_test = P2P_Flush(interface_name, wpas_dbus_interface,timeout) + + except: + print "Error:\n Invalid wpas_dbus_interface" + usage() + quit() + + # Start P2P_Find + p2p_flush_test.start() + + try: + time.sleep(int(p2p_flush_test.timeout)) + + except: + pass + + print "p2p_flush complete" + quit() diff --git a/wpa_supplicant/examples/p2p/p2p_group_add.py b/wpa_supplicant/examples/p2p/p2p_group_add.py new file mode 100644 index 00000000..5c8fdafd --- /dev/null +++ b/wpa_supplicant/examples/p2p/p2p_group_add.py @@ -0,0 +1,222 @@ +#!/usr/bin/python +# Tests p2p_group_add +######### MAY NEED TO RUN AS SUDO ############# + +import dbus +import sys, os +import time +import gobject +import getopt +import threading +from dbus.mainloop.glib import DBusGMainLoop + +def usage(): + print "Usage:" + print " %s -i <interface_name> [-p <persistent>] \ " \ + % sys.argv[0] + print " [-f <frequency>] [-o <group_object_path>] \ " + print " [-w <wpas_dbus_interface>]" + print "Options:" + print " -i = interface name" + print " -p = persistant group = 0 (0=false, 1=true)" + print " -f = frequency" + print " -o = persistent group object path" + print " -w = wpas dbus interface = fi.w1.wpa_supplicant1" + print "Example:" + print " %s -i wlan0" % sys.argv[0] + +# Required Signals +def GroupStarted(properties): + if properties.has_key("group_object"): + print 'Group Formation Complete %s' \ + % properties["group_object"] + os._exit(0) + +def WpsFailure(status, etc): + print "WPS Authentication Failure".format(status) + print etc + os._exit(0) + +class P2P_Group_Add (threading.Thread): + # Needed Variables + global bus + global wpas_object + global interface_object + global p2p_interface + global interface_name + global wpas + global wpas_dbus_interface + global path + global persistent + global frequency + global persistent_group_object + + # Dbus Paths + global wpas_dbus_opath + global wpas_dbus_interfaces_opath + global wpas_dbus_interfaces_interface + global wpas_dbus_interfaces_p2pdevice + + # Arguements + global P2PDictionary + + # Constructor + def __init__(self,interface_name,wpas_dbus_interface,persistent,frequency, + persistent_group_object): + # Initializes variables and threads + self.interface_name = interface_name + self.wpas_dbus_interface = wpas_dbus_interface + self.persistent = persistent + self.frequency = frequency + self.persistent_group_object = persistent_group_object + + # Initializes thread and daemon allows for ctrl-c kill + threading.Thread.__init__(self) + self.daemon = True + + # Generating interface/object paths + self.wpas_dbus_opath = "/" + \ + self.wpas_dbus_interface.replace(".","/") + self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \ + "/Interfaces" + self.wpas_dbus_interfaces_interface = \ + self.wpas_dbus_interface + ".Interface" + self.wpas_dbus_interfaces_p2pdevice = \ + self.wpas_dbus_interfaces_interface \ + + ".P2PDevice" + + # Getting interfaces and objects + DBusGMainLoop(set_as_default=True) + self.bus = dbus.SystemBus() + self.wpas_object = self.bus.get_object( + self.wpas_dbus_interface, + self.wpas_dbus_opath) + self.wpas = dbus.Interface(self.wpas_object, + self.wpas_dbus_interface) + + # Try to see if supplicant knows about interface + # If not, throw an exception + try: + self.path = self.wpas.GetInterface( + self.interface_name) + except dbus.DBusException, exc: + error = 'Error:\n Interface ' + self.interface_name \ + + ' was not found' + print error + usage() + os._exit(0) + + self.interface_object = self.bus.get_object( + self.wpas_dbus_interface, self.path) + self.p2p_interface = dbus.Interface(self.interface_object, + self.wpas_dbus_interfaces_p2pdevice) + + #Adds listeners + self.bus.add_signal_receiver(GroupStarted, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="GroupStarted") + self.bus.add_signal_receiver(WpsFailure, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="WpsFailed") + + # Sets up p2p_group_add dictionary + def constructArguements(self): + self.P2PDictionary = {'persistent':self.persistent} + + if (self.frequency != None): + if (int(self.frequency) > 0): + self.P2PDictionary.update({'frequency':int(self.frequency)}) + else: + print "Error:\n Frequency must be greater than 0" + usage() + os._exit(0) + + if (self.persistent_group_object != None): + self.P2PDictionary.update({'persistent_group_object': + self.persistent_group_object}) + + # Run p2p_group_remove + def run(self): + try: + self.p2p_interface.GroupAdd(self.P2PDictionary) + + except: + print "Error:\n Could not preform group add" + usage() + os._exit(0) + + # Allows other threads to keep working while MainLoop runs + # Required for timeout implementation + gobject.MainLoop().get_context().iteration(True) + gobject.threads_init() + gobject.MainLoop().run() + + +if __name__ == "__main__": + + # Defaults for optional inputs + # 0 = false, 1 = true + persistent = False + frequency = None + persistent_group_object = None + wpas_dbus_interface = 'fi.w1.wpa_supplicant1' + + # interface_name is required + interface_name = None + + # Using getopts to handle options + try: + options, args = getopt.getopt(sys.argv[1:],"hi:p:f:o:w:") + + except getopt.GetoptError: + usage() + quit() + + # If theres a switch, override default option + for key, value in options: + # Help + if (key == "-h"): + usage() + quit() + # Interface Name + elif (key == "-i"): + interface_name = value + # Timeout + elif (key == "-p"): + if (value == '0'): + persistent = False + elif (value == '1'): + persistent = True + else: + print "Error:\n Persistent can only be 1 or 0" + usage() + os._exit(0) + # Frequency + elif (key == "-f"): + frequency = value + # Persistent group object path + elif (key == "-o"): + persistent_group_object = value + # Dbus interface + elif (key == "-w"): + wpas_dbus_interface = value + else: + assert False, "unhandled option" + + # Interface name is required and was not given + if (interface_name == None): + print "Error:\n interface_name is required" + usage() + quit() + + try: + p2p_group_add_test = P2P_Group_Add(interface_name,wpas_dbus_interface, + persistent,frequency,persistent_group_object) + except: + print "Error:\n Invalid Arguements" + + p2p_group_add_test.constructArguements() + p2p_group_add_test.start() + time.sleep(5) + print "Error:\n Group formation timed out" + os._exit(0) diff --git a/wpa_supplicant/examples/p2p/p2p_invite.py b/wpa_supplicant/examples/p2p/p2p_invite.py new file mode 100644 index 00000000..6deb397e --- /dev/null +++ b/wpa_supplicant/examples/p2p/p2p_invite.py @@ -0,0 +1,201 @@ +#!/usr/bin/python +# Tests p2p_invite +######### MAY NEED TO RUN AS SUDO ############# + +import dbus +import sys, os +import time +import gobject +import getopt +import threading +from dbus.mainloop.glib import DBusGMainLoop + +def usage(): + print "Usage:" + print " %s -i <interface_name> -a <addr> \ " \ + % sys.argv[0] + print " [-o <persistent_group_object>] [-w <wpas_dbus_interface>]" + print "Options:" + print " -i = interface name" + print " -a = address of peer" + print " -o = persistent group object path" + print " -w = wpas dbus interface = fi.w1.wpa_supplicant1" + print "Example:" + print " %s -i p2p-wlan0-0 -a 00150083523c" % sys.argv[0] + +# Required Signals +def InvitationResult(invite_result): + print "Inviation Result signal :" + status = invite_result['status'] + print "status = ", status + if invite_result.has_key('BSSID'): + bssid = invite_result['BSSID'] + print "BSSID = ", hex(bssid[0]) , ":" , \ + hex(bssid[1]) , ":" , hex(bssid[2]) , ":", \ + hex(bssid[3]) , ":" , hex(bssid[4]) , ":" , \ + hex(bssid[5]) + os._exit(0) + +class P2P_Invite (threading.Thread): + # Needed Variables + global bus + global wpas_object + global interface_object + global p2p_interface + global interface_name + global wpas + global wpas_dbus_interface + global path + global addr + global persistent_group_object + + # Dbus Paths + global wpas_dbus_opath + global wpas_dbus_interfaces_opath + global wpas_dbus_interfaces_interface + global wpas_dbus_interfaces_p2pdevice + + # Arguements + global P2PDictionary + + # Constructor + def __init__(self,interface_name,wpas_dbus_interface,addr, + persistent_group_object): + # Initializes variables and threads + self.interface_name = interface_name + self.wpas_dbus_interface = wpas_dbus_interface + self.addr = addr + self.persistent_group_object = persistent_group_object + + # Initializes thread and daemon allows for ctrl-c kill + threading.Thread.__init__(self) + self.daemon = True + + # Generating interface/object paths + self.wpas_dbus_opath = "/" + \ + self.wpas_dbus_interface.replace(".","/") + self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \ + "/Interfaces" + self.wpas_dbus_interfaces_interface = \ + self.wpas_dbus_interface + ".Interface" + self.wpas_dbus_interfaces_p2pdevice = \ + self.wpas_dbus_interfaces_interface \ + + ".P2PDevice" + + # Getting interfaces and objects + DBusGMainLoop(set_as_default=True) + self.bus = dbus.SystemBus() + self.wpas_object = self.bus.get_object( + self.wpas_dbus_interface, + self.wpas_dbus_opath) + self.wpas = dbus.Interface(self.wpas_object, + self.wpas_dbus_interface) + + # Try to see if supplicant knows about interface + # If not, throw an exception + try: + self.path = self.wpas.GetInterface( + self.interface_name) + except dbus.DBusException, exc: + error = 'Error:\n Interface ' + self.interface_name \ + + ' was not found' + print error + usage() + os._exit(0) + + self.interface_object = self.bus.get_object( + self.wpas_dbus_interface, self.path) + self.p2p_interface = dbus.Interface(self.interface_object, + self.wpas_dbus_interfaces_p2pdevice) + + #Adds listeners + self.bus.add_signal_receiver(InvitationResult, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="InvitationResult") + + # Sets up p2p_invite dictionary + def constructArguements(self): + self.P2PDictionary = \ + {'peer':dbus.ObjectPath(self.path+'/Peers/'+self.addr)} + if (self.persistent_group_object != None): + self.P2PDictionary.update({"persistent_group_object": + self.persistent_group_object}) + + # Run p2p_invite + def run(self): + try: + self.p2p_interface.Invite(self.P2PDictionary) + + except: + print "Error:\n Invalid Arguements" + usage() + os._exit(0) + + # Allows other threads to keep working while MainLoop runs + # Required for timeout implementation + gobject.MainLoop().get_context().iteration(True) + gobject.threads_init() + gobject.MainLoop().run() + +if __name__ == "__main__": + # Defaults for optional inputs + addr = None + persistent_group_object = None + wpas_dbus_interface = 'fi.w1.wpa_supplicant1' + + # interface_name is required + interface_name = None + + # Using getopts to handle options + try: + options, args = getopt.getopt(sys.argv[1:],"hi:o:w:a:") + + except getopt.GetoptError: + usage() + quit() + + # If theres a switch, override default option + for key, value in options: + # Help + if (key == "-h"): + usage() + quit() + # Interface Name + elif (key == "-i"): + interface_name = value + elif (key == "-a"): + addr = value + # Persistent group object path + elif (key == "-o"): + persistent_group_object = value + # Dbus interface + elif (key == "-w"): + wpas_dbus_interface = value + else: + assert False, "unhandled option" + + # Interface name is required and was not given + if (interface_name == None): + print "Error:\n interface_name is required" + usage() + quit() + + if (addr == None): + print "Error:\n peer address is required" + usage() + quit() + + try: + p2p_invite_test = \ + P2P_Invite(interface_name,wpas_dbus_interface, + addr,persistent_group_object) + except: + print "Error:\n Invalid Arguements" + usage() + os._exit(1) + + p2p_invite_test.constructArguements() + p2p_invite_test.start() + time.sleep(10) + print "Error:\n p2p_invite timed out" + os._exit(0) diff --git a/wpa_supplicant/examples/p2p/p2p_listen.py b/wpa_supplicant/examples/p2p/p2p_listen.py new file mode 100644 index 00000000..bb3c1e49 --- /dev/null +++ b/wpa_supplicant/examples/p2p/p2p_listen.py @@ -0,0 +1,182 @@ +#!/usr/bin/python +# Tests P2P_Find +# Will listen +# Then Program will exit +######### MAY NEED TO RUN AS SUDO ############# + +import dbus +import sys, os +import time +import gobject +import threading +import getopt +from dbus.mainloop.glib import DBusGMainLoop + +def usage(): + print "Usage:" + print " %s -i <interface_name> [-t <timeout>] \ " \ + % sys.argv[0] + print " [-w <wpas_dbus_interface>]" + print "Options:" + print " -i = interface name" + print " -t = timeout = 0s (infinite)" + print " -w = wpas dbus interface = fi.w1.wpa_supplicant1" + print "Example:" + print " %s -i wlan0 -t 5" % sys.argv[0] + +# Required Signals +def p2pStateChange(status): + print status + +class P2P_Listen(threading.Thread): + # Needed Variables + global bus + global wpas_object + global interface_object + global p2p_interface + global interface_name + global wpas + global wpas_dbus_interface + global path + global timeout + + # Dbus Paths + global wpas_dbus_opath + global wpas_dbus_interfaces_opath + global wpas_dbus_interfaces_interface + global wpas_dbus_interfaces_p2pdevice + + # Constructor + def __init__(self,interface_name,wpas_dbus_interface,timeout): + # Initializes variables and threads + self.timeout = int(timeout) + self.interface_name = interface_name + self.wpas_dbus_interface = wpas_dbus_interface + + # Initializes thread and daemon allows for ctrl-c kill + threading.Thread.__init__(self) + self.daemon = True + + # Generating interface/object paths + self.wpas_dbus_opath = "/" + \ + self.wpas_dbus_interface.replace(".","/") + self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \ + "/Interfaces" + self.wpas_dbus_interfaces_interface = \ + self.wpas_dbus_interface + ".Interface" + self.wpas_dbus_interfaces_p2pdevice = \ + self.wpas_dbus_interfaces_interface \ + + ".P2PDevice" + + # Getting interfaces and objects + DBusGMainLoop(set_as_default=True) + self.bus = dbus.SystemBus() + self.wpas_object = self.bus.get_object( + self.wpas_dbus_interface, + self.wpas_dbus_opath) + self.wpas = dbus.Interface(self.wpas_object, + self.wpas_dbus_interface) + + # Try to see if supplicant knows about interface + # If not, throw an exception + try: + self.path = self.wpas.GetInterface( + self.interface_name) + except dbus.DBusException, exc: + error = 'Error:\n Interface ' + self.interface_name \ + + ' was not found' + print error + usage() + os._exit(0) + + self.interface_object = self.bus.get_object( + self.wpas_dbus_interface, self.path) + self.p2p_interface = dbus.Interface(self.interface_object, + self.wpas_dbus_interfaces_p2pdevice) + + self.bus.add_signal_receiver(p2pStateChange, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="P2PStateChanged") + + # Run p2p_find + def run(self): + # Sets up p2p_listen + self.p2p_interface.Listen(int(self.timeout)) + + # Allows other threads to keep working while MainLoop runs + # Required for timeout implementation + gobject.MainLoop().get_context().iteration(True) + gobject.threads_init() + gobject.MainLoop().run() + +if __name__ == "__main__": + + # Defaults for optional inputs + timeout = 0 + wpas_dbus_interface = 'fi.w1.wpa_supplicant1' + + # interface_name is required + interface_name = None + + # Using getopts to handle options + try: + options, args = getopt.getopt(sys.argv[1:],"hi:t:w:") + + except getopt.GetoptError: + usage() + quit() + + # If theres a switch, override default option + for key, value in options: + # Help + if (key == "-h"): + usage() + quit() + # Interface Name + elif (key == "-i"): + interface_name = value + # Timeout + elif (key == "-t"): + if ( int(value) >= 0): + timeout = value + else: + print "Error:\n Timeout cannot be negative" + usage() + quit() + # Dbus interface + elif (key == "-w"): + wpas_dbus_interface = value + else: + assert False, "unhandled option" + + # Interface name is required and was not given + if (interface_name == None): + print "Error:\n interface_name is required" + usage() + quit() + + # Constructor + try: + p2p_listen_test = P2P_Listen(interface_name, wpas_dbus_interface, timeout) + + except: + print "Error:\n Invalid wpas_dbus_interface" + usage() + quit() + + # Start P2P_Find + p2p_listen_test.start() + + try: + # If timeout is 0, then run forever + if (int(p2p_listen_test.timeout) == 0): + while(True): + pass + # Else sleep for (timeout) + else: + time.sleep(int(p2p_listen_test.timeout)) + + except: + pass + + quit() diff --git a/wpa_supplicant/examples/p2p/p2p_stop_find.py b/wpa_supplicant/examples/p2p/p2p_stop_find.py new file mode 100644 index 00000000..f6c03b02 --- /dev/null +++ b/wpa_supplicant/examples/p2p/p2p_stop_find.py @@ -0,0 +1,174 @@ +#!/usr/bin/python +# Tests p2p_stop_find +######### MAY NEED TO RUN AS SUDO ############# + +import dbus +import sys, os +import time +import gobject +import threading +import getopt +from dbus.mainloop.glib import DBusGMainLoop + +def usage(): + print "Usage:" + print " %s -i <interface_name> \ " \ + % sys.argv[0] + print " [-w <wpas_dbus_interface>]" + print "Options:" + print " -i = interface name" + print " -w = wpas dbus interface = fi.w1.wpa_supplicant1" + print "Example:" + print " %s -i wlan0" % sys.argv[0] + +# Required Signals +def deviceLost(devicepath): + print "Device lost: %s" % (devicepath) + +def p2pStateChange(status): + print status + os._exit(0) + +class P2P_Stop_Find (threading.Thread): + # Needed Variables + global bus + global wpas_object + global interface_object + global p2p_interface + global interface_name + global wpas + global wpas_dbus_interface + global path + global timeout + + # Dbus Paths + global wpas_dbus_opath + global wpas_dbus_interfaces_opath + global wpas_dbus_interfaces_interface + global wpas_dbus_interfaces_p2pdevice + + # Constructor + def __init__(self,interface_name,wpas_dbus_interface,timeout): + # Initializes variables and threads + self.interface_name = interface_name + self.wpas_dbus_interface = wpas_dbus_interface + self.timeout = timeout + + # Initializes thread and daemon allows for ctrl-c kill + threading.Thread.__init__(self) + self.daemon = True + + # Generating interface/object paths + self.wpas_dbus_opath = "/" + \ + self.wpas_dbus_interface.replace(".","/") + self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \ + "/Interfaces" + self.wpas_dbus_interfaces_interface = \ + self.wpas_dbus_interface + ".Interface" + self.wpas_dbus_interfaces_p2pdevice = \ + self.wpas_dbus_interfaces_interface \ + + ".P2PDevice" + + # Getting interfaces and objects + DBusGMainLoop(set_as_default=True) + self.bus = dbus.SystemBus() + self.wpas_object = self.bus.get_object( + self.wpas_dbus_interface, + self.wpas_dbus_opath) + self.wpas = dbus.Interface(self.wpas_object, + self.wpas_dbus_interface) + + # Try to see if supplicant knows about interface + # If not, throw an exception + try: + self.path = self.wpas.GetInterface( + self.interface_name) + except dbus.DBusException, exc: + error = 'Error:\n Interface ' + self.interface_name \ + + ' was not found' + print error + usage() + os._exit(0) + + self.interface_object = self.bus.get_object( + self.wpas_dbus_interface, self.path) + self.p2p_interface = dbus.Interface(self.interface_object, + self.wpas_dbus_interfaces_p2pdevice) + + # Signals + self.bus.add_signal_receiver(deviceLost, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="DeviceLost") + self.bus.add_signal_receiver(p2pStateChange, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="P2PStateChanged") + + # Runs p2p_stop_find + def run(self): + # Allows other threads to keep working while MainLoop runs + # Required for timeout implementation + gobject.MainLoop().get_context().iteration(True) + gobject.threads_init() + self.p2p_interface.StopFind() + gobject.MainLoop().run() + + +if __name__ == "__main__": + # Needed because P2PStateChanged signal is not caught + timeout = 5 + # Defaults for optional inputs + wpas_dbus_interface = 'fi.w1.wpa_supplicant1' + + # interface_name is required + interface_name = None + + # Using getopts to handle options + try: + options, args = getopt.getopt(sys.argv[1:],"ht:i:w:") + + except getopt.GetoptError: + usage() + quit() + + # If theres a switch, override default option + for key, value in options: + # Help + if (key == "-h"): + usage() + quit() + # Interface Name + elif (key == "-i"): + interface_name = value + # Dbus interface + elif (key == "-w"): + wpas_dbus_interface = value + else: + assert False, "unhandled option" + + # Interface name is required and was not given + if (interface_name == None): + print "Error:\n interface_name is required" + usage() + quit() + + # Constructor + try: + p2p_stop_find_test = P2P_Stop_Find(interface_name, + wpas_dbus_interface,timeout) + + except: + print "Error:\n Invalid wpas_dbus_interface" + usage() + quit() + + # Start P2P_Find + p2p_stop_find_test.start() + + try: + time.sleep(int(p2p_stop_find_test.timeout)) + + except: + pass + + print "p2p find stopped" + quit() diff --git a/wpa_supplicant/gas_query.c b/wpa_supplicant/gas_query.c index 1fd6e00e..efa9be8e 100644 --- a/wpa_supplicant/gas_query.c +++ b/wpa_supplicant/gas_query.c @@ -19,7 +19,7 @@ #include "gas_query.h" -#define GAS_QUERY_TIMEOUT 5 +#define GAS_QUERY_TIMEOUT_PERIOD 5 struct gas_query_pending { @@ -457,7 +457,7 @@ int gas_query_req(struct gas_query *gas, const u8 *dst, int freq, return -1; } - eloop_register_timeout(GAS_QUERY_TIMEOUT, 0, gas_query_timeout, + eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0, gas_query_timeout, gas, query); return dialog_token; diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c index 515d94bb..7b5b20eb 100644 --- a/wpa_supplicant/interworking.c +++ b/wpa_supplicant/interworking.c @@ -15,6 +15,7 @@ #include "utils/pcsc_funcs.h" #include "drivers/driver.h" #include "eap_common/eap_defs.h" +#include "eap_peer/eap.h" #include "eap_peer/eap_methods.h" #include "wpa_supplicant_i.h" #include "config.h" @@ -329,7 +330,7 @@ static const u8 * nai_realm_parse_realm(struct nai_realm *r, const u8 *pos, wpa_printf(MSG_DEBUG, "No room for EAP Methods"); return NULL; } - r->eap = os_zalloc(r->eap_count * sizeof(struct nai_realm_eap)); + r->eap = os_calloc(r->eap_count, sizeof(struct nai_realm_eap)); if (r->eap == NULL) return NULL; @@ -365,7 +366,7 @@ static struct nai_realm * nai_realm_parse(struct wpabuf *anqp, u16 *count) return NULL; } - realm = os_zalloc(num * sizeof(struct nai_realm)); + realm = os_calloc(num, sizeof(struct nai_realm)); if (realm == NULL) return NULL; @@ -550,6 +551,7 @@ static int plmn_id_match(struct wpabuf *anqp, const char *imsi, int mnc_len) break; if (os_memcmp(pos, plmn, 3) == 0) return 1; /* Found matching PLMN */ + pos += 3; } } @@ -561,7 +563,7 @@ static int plmn_id_match(struct wpabuf *anqp, const char *imsi, int mnc_len) static int build_root_nai(char *nai, size_t nai_len, const char *imsi, - char prefix) + size_t mnc_len, char prefix) { const char *sep, *msin; char *end, *pos; @@ -579,12 +581,16 @@ static int build_root_nai(char *nai, size_t nai_len, const char *imsi, return -1; } sep = os_strchr(imsi, '-'); - if (sep == NULL) + if (sep) { + plmn_len = sep - imsi; + msin = sep + 1; + } else if (mnc_len && os_strlen(imsi) >= 3 + mnc_len) { + plmn_len = 3 + mnc_len; + msin = imsi + plmn_len; + } else return -1; - plmn_len = sep - imsi; if (plmn_len != 5 && plmn_len != 6) return -1; - msin = sep + 1; msin_len = os_strlen(msin); pos = nai; @@ -615,7 +621,7 @@ static int build_root_nai(char *nai, size_t nai_len, const char *imsi, static int set_root_nai(struct wpa_ssid *ssid, const char *imsi, char prefix) { char nai[100]; - if (build_root_nai(nai, sizeof(nai), imsi, prefix) < 0) + if (build_root_nai(nai, sizeof(nai), imsi, 0, prefix) < 0) return -1; return wpa_config_set_quoted(ssid, "identity", nai); } @@ -623,6 +629,18 @@ static int set_root_nai(struct wpa_ssid *ssid, const char *imsi, char prefix) #endif /* INTERWORKING_3GPP */ +static int interworking_set_hs20_params(struct wpa_ssid *ssid) +{ + if (wpa_config_set(ssid, "key_mgmt", "WPA-EAP", 0) < 0) + return -1; + if (wpa_config_set(ssid, "proto", "RSN", 0) < 0) + return -1; + if (wpa_config_set(ssid, "pairwise", "CCMP", 0) < 0) + return -1; + return 0; +} + + static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) { @@ -688,6 +706,9 @@ static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s, os_memcpy(ssid->ssid, ie + 2, ie[1]); ssid->ssid_len = ie[1]; + if (interworking_set_hs20_params(ssid) < 0) + goto fail; + /* TODO: figure out whether to use EAP-SIM, EAP-AKA, or EAP-AKA' */ if (wpa_config_set(ssid, "eap", "SIM", 0) < 0) { wpa_printf(MSG_DEBUG, "EAP-SIM not supported"); @@ -730,6 +751,255 @@ fail: } +static int roaming_consortium_element_match(const u8 *ie, const u8 *rc_id, + size_t rc_len) +{ + const u8 *pos, *end; + u8 lens; + + if (ie == NULL) + return 0; + + pos = ie + 2; + end = ie + 2 + ie[1]; + + /* Roaming Consortium element: + * Number of ANQP OIs + * OI #1 and #2 lengths + * OI #1, [OI #2], [OI #3] + */ + + if (pos + 2 > end) + return 0; + + pos++; /* skip Number of ANQP OIs */ + lens = *pos++; + if (pos + (lens & 0x0f) + (lens >> 4) > end) + return 0; + + if ((lens & 0x0f) == rc_len && os_memcmp(pos, rc_id, rc_len) == 0) + return 1; + pos += lens & 0x0f; + + if ((lens >> 4) == rc_len && os_memcmp(pos, rc_id, rc_len) == 0) + return 1; + pos += lens >> 4; + + if (pos < end && (size_t) (end - pos) == rc_len && + os_memcmp(pos, rc_id, rc_len) == 0) + return 1; + + return 0; +} + + +static int roaming_consortium_anqp_match(const struct wpabuf *anqp, + const u8 *rc_id, size_t rc_len) +{ + const u8 *pos, *end; + u8 len; + + if (anqp == NULL) + return 0; + + pos = wpabuf_head(anqp); + end = pos + wpabuf_len(anqp); + + /* Set of <OI Length, OI> duples */ + while (pos < end) { + len = *pos++; + if (pos + len > end) + break; + if (len == rc_len && os_memcmp(pos, rc_id, rc_len) == 0) + return 1; + pos += len; + } + + return 0; +} + + +static int roaming_consortium_match(const u8 *ie, const struct wpabuf *anqp, + const u8 *rc_id, size_t rc_len) +{ + return roaming_consortium_element_match(ie, rc_id, rc_len) || + roaming_consortium_anqp_match(anqp, rc_id, rc_len); +} + + +static struct wpa_cred * interworking_credentials_available_roaming_consortium( + struct wpa_supplicant *wpa_s, struct wpa_bss *bss) +{ + struct wpa_cred *cred, *selected = NULL; + const u8 *ie; + + ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM); + + if (ie == NULL && bss->anqp_roaming_consortium == NULL) + return NULL; + + if (wpa_s->conf->cred == NULL) + return NULL; + + for (cred = wpa_s->conf->cred; cred; cred = cred->next) { + if (cred->roaming_consortium_len == 0) + continue; + + if (!roaming_consortium_match(ie, bss->anqp_roaming_consortium, + cred->roaming_consortium, + cred->roaming_consortium_len)) + continue; + + if (selected == NULL || + selected->priority < cred->priority) + selected = cred; + } + + return selected; +} + + +static int interworking_set_eap_params(struct wpa_ssid *ssid, + struct wpa_cred *cred, int ttls) +{ + if (cred->eap_method) { + ttls = cred->eap_method->vendor == EAP_VENDOR_IETF && + cred->eap_method->method == EAP_TYPE_TTLS; + + os_free(ssid->eap.eap_methods); + ssid->eap.eap_methods = + os_malloc(sizeof(struct eap_method_type) * 2); + if (ssid->eap.eap_methods == NULL) + return -1; + os_memcpy(ssid->eap.eap_methods, cred->eap_method, + sizeof(*cred->eap_method)); + ssid->eap.eap_methods[1].vendor = EAP_VENDOR_IETF; + ssid->eap.eap_methods[1].method = EAP_TYPE_NONE; + } + + if (ttls && cred->username && cred->username[0]) { + const char *pos; + char *anon; + /* Use anonymous NAI in Phase 1 */ + pos = os_strchr(cred->username, '@'); + if (pos) { + size_t buflen = 9 + os_strlen(pos) + 1; + anon = os_malloc(buflen); + if (anon == NULL) + return -1; + os_snprintf(anon, buflen, "anonymous%s", pos); + } else if (cred->realm) { + size_t buflen = 10 + os_strlen(cred->realm) + 1; + anon = os_malloc(buflen); + if (anon == NULL) + return -1; + os_snprintf(anon, buflen, "anonymous@%s", cred->realm); + } else { + anon = os_strdup("anonymous"); + if (anon == NULL) + return -1; + } + if (wpa_config_set_quoted(ssid, "anonymous_identity", anon) < + 0) { + os_free(anon); + return -1; + } + os_free(anon); + } + + if (cred->username && cred->username[0] && + wpa_config_set_quoted(ssid, "identity", cred->username) < 0) + return -1; + + if (cred->password && cred->password[0]) { + if (cred->ext_password && + wpa_config_set(ssid, "password", cred->password, 0) < 0) + return -1; + if (!cred->ext_password && + wpa_config_set_quoted(ssid, "password", cred->password) < + 0) + return -1; + } + + if (cred->client_cert && cred->client_cert[0] && + wpa_config_set_quoted(ssid, "client_cert", cred->client_cert) < 0) + return -1; + + if (cred->private_key && cred->private_key[0] && + wpa_config_set_quoted(ssid, "private_key", cred->private_key) < 0) + return -1; + + if (cred->private_key_passwd && cred->private_key_passwd[0] && + wpa_config_set_quoted(ssid, "private_key_passwd", + cred->private_key_passwd) < 0) + return -1; + + if (cred->phase1) { + os_free(ssid->eap.phase1); + ssid->eap.phase1 = os_strdup(cred->phase1); + } + if (cred->phase2) { + os_free(ssid->eap.phase2); + ssid->eap.phase2 = os_strdup(cred->phase2); + } + + if (cred->ca_cert && cred->ca_cert[0] && + wpa_config_set_quoted(ssid, "ca_cert", cred->ca_cert) < 0) + return -1; + + return 0; +} + + +static int interworking_connect_roaming_consortium( + struct wpa_supplicant *wpa_s, struct wpa_cred *cred, + struct wpa_bss *bss, const u8 *ssid_ie) +{ + struct wpa_ssid *ssid; + + wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR " based on " + "roaming consortium match", MAC2STR(bss->bssid)); + + ssid = wpa_config_add_network(wpa_s->conf); + if (ssid == NULL) + return -1; + wpas_notify_network_added(wpa_s, ssid); + wpa_config_set_network_defaults(ssid); + ssid->priority = cred->priority; + ssid->temporary = 1; + ssid->ssid = os_zalloc(ssid_ie[1] + 1); + if (ssid->ssid == NULL) + goto fail; + os_memcpy(ssid->ssid, ssid_ie + 2, ssid_ie[1]); + ssid->ssid_len = ssid_ie[1]; + + if (interworking_set_hs20_params(ssid) < 0) + goto fail; + + if (cred->eap_method == NULL) { + wpa_printf(MSG_DEBUG, "Interworking: No EAP method set for " + "credential using roaming consortium"); + goto fail; + } + + if (interworking_set_eap_params( + ssid, cred, + cred->eap_method->vendor == EAP_VENDOR_IETF && + cred->eap_method->method == EAP_TYPE_TTLS) < 0) + goto fail; + + wpa_config_update_prio_list(wpa_s->conf); + interworking_reconnect(wpa_s); + + return 0; + +fail: + wpas_notify_network_removed(wpa_s, ssid); + wpa_config_remove_network(wpa_s->conf, ssid->id); + return -1; +} + + int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) { struct wpa_cred *cred; @@ -749,6 +1019,22 @@ int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) return -1; } + if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) { + /* + * We currently support only HS 2.0 networks and those are + * required to use WPA2-Enterprise. + */ + wpa_printf(MSG_DEBUG, "Interworking: Network does not use " + "RSN"); + return -1; + } + + cred = interworking_credentials_available_roaming_consortium(wpa_s, + bss); + if (cred) + return interworking_connect_roaming_consortium(wpa_s, cred, + bss, ie); + realm = nai_realm_parse(bss->anqp_nai_realm, &count); if (realm == NULL) { wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI " @@ -800,60 +1086,11 @@ int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) os_memcpy(ssid->ssid, ie + 2, ie[1]); ssid->ssid_len = ie[1]; - if (wpa_config_set(ssid, "eap", eap_get_name(EAP_VENDOR_IETF, - eap->method), 0) < 0) - goto fail; - - if (eap->method == EAP_TYPE_TTLS && - cred->username && cred->username[0]) { - const char *pos; - char *anon; - /* Use anonymous NAI in Phase 1 */ - pos = os_strchr(cred->username, '@'); - if (pos) { - size_t buflen = 9 + os_strlen(pos) + 1; - anon = os_malloc(buflen); - if (anon == NULL) - goto fail; - os_snprintf(anon, buflen, "anonymous%s", pos); - } else if (cred->realm) { - size_t buflen = 10 + os_strlen(cred->realm) + 1; - anon = os_malloc(buflen); - if (anon == NULL) - goto fail; - os_snprintf(anon, buflen, "anonymous@%s", cred->realm); - } else { - anon = os_strdup("anonymous"); - if (anon == NULL) - goto fail; - } - if (wpa_config_set_quoted(ssid, "anonymous_identity", anon) < - 0) { - os_free(anon); - goto fail; - } - os_free(anon); - } - - if (cred->username && cred->username[0] && - wpa_config_set_quoted(ssid, "identity", cred->username) < 0) - goto fail; - - if (cred->password && cred->password[0] && - wpa_config_set_quoted(ssid, "password", cred->password) < 0) - goto fail; - - if (cred->client_cert && cred->client_cert[0] && - wpa_config_set_quoted(ssid, "client_cert", cred->client_cert) < 0) - goto fail; - - if (cred->private_key && cred->private_key[0] && - wpa_config_set_quoted(ssid, "private_key", cred->private_key) < 0) + if (interworking_set_hs20_params(ssid) < 0) goto fail; - if (cred->private_key_passwd && cred->private_key_passwd[0] && - wpa_config_set_quoted(ssid, "private_key_passwd", - cred->private_key_passwd) < 0) + if (wpa_config_set(ssid, "eap", eap_get_name(EAP_VENDOR_IETF, + eap->method), 0) < 0) goto fail; switch (eap->method) { @@ -899,8 +1136,8 @@ int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) break; } - if (cred->ca_cert && cred->ca_cert[0] && - wpa_config_set_quoted(ssid, "ca_cert", cred->ca_cert) < 0) + if (interworking_set_eap_params(ssid, cred, + eap->method == EAP_TYPE_TTLS) < 0) goto fail; nai_realm_free(realm, count); @@ -1027,6 +1264,13 @@ static struct wpa_cred * interworking_credentials_available( if (!cred) cred = cred2; + cred2 = interworking_credentials_available_roaming_consortium(wpa_s, + bss); + if (cred && cred2 && cred2->priority >= cred->priority) + cred = cred2; + if (!cred) + cred = cred2; + return cred; } @@ -1071,8 +1315,17 @@ static int interworking_home_sp(struct wpa_supplicant *wpa_s, for (cred = wpa_s->conf->cred; cred; cred = cred->next) { #ifdef INTERWORKING_3GPP - if (cred->imsi && - build_root_nai(nai, sizeof(nai), cred->imsi, 0) == 0) { + char *imsi = NULL; + int mnc_len = 0; + if (cred->imsi) + imsi = cred->imsi; + else if (cred->pcsc && wpa_s->conf->pcsc_reader && + wpa_s->scard && wpa_s->imsi[0]) { + imsi = wpa_s->imsi; + mnc_len = wpa_s->mnc_len; + } + if (imsi && build_root_nai(nai, sizeof(nai), imsi, mnc_len, 0) + == 0) { realm = os_strchr(nai, '@'); if (realm) realm++; @@ -1138,6 +1391,16 @@ static void interworking_select_network(struct wpa_supplicant *wpa_s) cred = interworking_credentials_available(wpa_s, bss); if (!cred) continue; + if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) { + /* + * We currently support only HS 2.0 networks and those + * are required to use WPA2-Enterprise. + */ + wpa_printf(MSG_DEBUG, "Interworking: Credential match " + "with " MACSTR " but network does not use " + "RSN", MAC2STR(bss->bssid)); + continue; + } count++; res = interworking_home_sp(wpa_s, bss->anqp_domain_name); if (res > 0) @@ -1148,7 +1411,9 @@ static void interworking_select_network(struct wpa_supplicant *wpa_s) type = "unknown"; wpa_msg(wpa_s, MSG_INFO, INTERWORKING_AP MACSTR " type=%s", MAC2STR(bss->bssid), type); - if (wpa_s->auto_select) { + if (wpa_s->auto_select || + (wpa_s->conf->auto_interworking && + wpa_s->auto_network_select)) { if (selected == NULL || cred->priority > selected_prio) { selected = bss; @@ -1178,7 +1443,16 @@ static void interworking_select_network(struct wpa_supplicant *wpa_s) if (interworking_find_network_match(wpa_s)) { wpa_printf(MSG_DEBUG, "Interworking: Possible BSS " "match for enabled network configurations"); - interworking_reconnect(wpa_s); + if (wpa_s->auto_select) + interworking_reconnect(wpa_s); + return; + } + + if (wpa_s->auto_network_select) { + wpa_printf(MSG_DEBUG, "Interworking: Continue " + "scanning after ANQP fetch"); + wpa_supplicant_req_scan(wpa_s, wpa_s->scan_interval, + 0); return; } @@ -1226,7 +1500,7 @@ static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s) } -static void interworking_start_fetch_anqp(struct wpa_supplicant *wpa_s) +void interworking_start_fetch_anqp(struct wpa_supplicant *wpa_s) { struct wpa_bss *bss; @@ -1484,6 +1758,7 @@ int interworking_select(struct wpa_supplicant *wpa_s, int auto_select) { interworking_stop_fetch_anqp(wpa_s); wpa_s->network_select = 1; + wpa_s->auto_network_select = 0; wpa_s->auto_select = !!auto_select; wpa_printf(MSG_DEBUG, "Interworking: Start scan for network " "selection"); @@ -1493,3 +1768,84 @@ int interworking_select(struct wpa_supplicant *wpa_s, int auto_select) return 0; } + + +static void gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token, + enum gas_query_result result, + const struct wpabuf *adv_proto, + const struct wpabuf *resp, u16 status_code) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpa_msg(wpa_s, MSG_INFO, GAS_RESPONSE_INFO "addr=" MACSTR + " dialog_token=%d status_code=%d resp_len=%d", + MAC2STR(addr), dialog_token, status_code, + resp ? (int) wpabuf_len(resp) : -1); + if (!resp) + return; + + wpabuf_free(wpa_s->last_gas_resp); + wpa_s->last_gas_resp = wpabuf_dup(resp); + if (wpa_s->last_gas_resp == NULL) + return; + os_memcpy(wpa_s->last_gas_addr, addr, ETH_ALEN); + wpa_s->last_gas_dialog_token = dialog_token; +} + + +int gas_send_request(struct wpa_supplicant *wpa_s, const u8 *dst, + const struct wpabuf *adv_proto, + const struct wpabuf *query) +{ + struct wpabuf *buf; + int ret = 0; + int freq; + struct wpa_bss *bss; + int res; + size_t len; + u8 query_resp_len_limit = 0, pame_bi = 0; + + freq = wpa_s->assoc_freq; + bss = wpa_bss_get_bssid(wpa_s, dst); + if (bss) + freq = bss->freq; + if (freq <= 0) + return -1; + + wpa_printf(MSG_DEBUG, "GAS request to " MACSTR " (freq %d MHz)", + MAC2STR(dst), freq); + wpa_hexdump_buf(MSG_DEBUG, "Advertisement Protocol ID", adv_proto); + wpa_hexdump_buf(MSG_DEBUG, "GAS Query", query); + + len = 3 + wpabuf_len(adv_proto) + 2; + if (query) + len += wpabuf_len(query); + buf = gas_build_initial_req(0, len); + if (buf == NULL) + return -1; + + /* Advertisement Protocol IE */ + wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO); + wpabuf_put_u8(buf, 1 + wpabuf_len(adv_proto)); /* Length */ + wpabuf_put_u8(buf, (query_resp_len_limit & 0x7f) | + (pame_bi ? 0x80 : 0)); + wpabuf_put_buf(buf, adv_proto); + + /* GAS Query */ + if (query) { + wpabuf_put_le16(buf, wpabuf_len(query)); + wpabuf_put_buf(buf, query); + } else + wpabuf_put_le16(buf, 0); + + res = gas_query_req(wpa_s->gas, dst, freq, buf, gas_resp_cb, wpa_s); + if (res < 0) { + wpa_printf(MSG_DEBUG, "GAS: Failed to send Query Request"); + ret = -1; + } else + wpa_printf(MSG_DEBUG, "GAS: Query started with dialog token " + "%u", res); + + wpabuf_free(buf); + return ret; +} diff --git a/wpa_supplicant/interworking.h b/wpa_supplicant/interworking.h index 7dda8d82..cb8438e6 100644 --- a/wpa_supplicant/interworking.h +++ b/wpa_supplicant/interworking.h @@ -1,6 +1,6 @@ /* * Interworking (IEEE 802.11u) - * Copyright (c) 2011, Qualcomm Atheros + * Copyright (c) 2011-2012, Qualcomm Atheros * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -17,9 +17,13 @@ void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token, enum gas_query_result result, const struct wpabuf *adv_proto, const struct wpabuf *resp, u16 status_code); +int gas_send_request(struct wpa_supplicant *wpa_s, const u8 *dst, + const struct wpabuf *adv_proto, + const struct wpabuf *query); int interworking_fetch_anqp(struct wpa_supplicant *wpa_s); void interworking_stop_fetch_anqp(struct wpa_supplicant *wpa_s); int interworking_select(struct wpa_supplicant *wpa_s, int auto_select); int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss); +void interworking_start_fetch_anqp(struct wpa_supplicant *wpa_s); #endif /* INTERWORKING_H */ diff --git a/wpa_supplicant/main.c b/wpa_supplicant/main.c index 80aac77b..19f7ce66 100644 --- a/wpa_supplicant/main.c +++ b/wpa_supplicant/main.c @@ -101,20 +101,31 @@ static void license(void) } -static void wpa_supplicant_fd_workaround(void) +static void wpa_supplicant_fd_workaround(int start) { #ifdef __linux__ - int s, i; + static int fd[3] = { -1, -1, -1 }; + int i; /* When started from pcmcia-cs scripts, wpa_supplicant might start with * fd 0, 1, and 2 closed. This will cause some issues because many * places in wpa_supplicant are still printing out to stdout. As a * workaround, make sure that fd's 0, 1, and 2 are not used for other * sockets. */ - for (i = 0; i < 3; i++) { - s = open("/dev/null", O_RDWR); - if (s > 2) { - close(s); - break; + if (start) { + for (i = 0; i < 3; i++) { + fd[i] = open("/dev/null", O_RDWR); + if (fd[i] > 2) { + close(fd[i]); + fd[i] = -1; + break; + } + } + } else { + for (i = 0; i < 3; i++) { + if (fd[i] >= 0) { + close(fd[i]); + fd[i] = -1; + } } } #endif /* __linux__ */ @@ -140,7 +151,7 @@ int main(int argc, char *argv[]) return -1; iface_count = 1; - wpa_supplicant_fd_workaround(); + wpa_supplicant_fd_workaround(1); for (;;) { c = getopt(argc, argv, @@ -241,8 +252,8 @@ int main(int argc, char *argv[]) break; case 'N': iface_count++; - iface = os_realloc(ifaces, iface_count * - sizeof(struct wpa_interface)); + iface = os_realloc_array(ifaces, iface_count, + sizeof(struct wpa_interface)); if (iface == NULL) goto out; ifaces = iface; @@ -288,6 +299,7 @@ int main(int argc, char *argv[]) wpa_supplicant_deinit(global); out: + wpa_supplicant_fd_workaround(0); os_free(ifaces); os_free(params.pid_file); diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c index 55cb2439..cfe04332 100644 --- a/wpa_supplicant/p2p_supplicant.c +++ b/wpa_supplicant/p2p_supplicant.c @@ -61,6 +61,27 @@ #define P2P_MAX_INITIAL_CONN_WAIT 10 #endif /* P2P_MAX_INITIAL_CONN_WAIT */ +#ifndef P2P_CONCURRENT_SEARCH_DELAY +#ifdef ANDROID_P2P +#define P2P_CONCURRENT_SEARCH_DELAY 0 +#else +#define P2P_CONCURRENT_SEARCH_DELAY 500 +#endif +#endif /* P2P_CONCURRENT_SEARCH_DELAY */ + +enum p2p_group_removal_reason { + P2P_GROUP_REMOVAL_UNKNOWN, + P2P_GROUP_REMOVAL_SILENT, + P2P_GROUP_REMOVAL_FORMATION_FAILED, + P2P_GROUP_REMOVAL_REQUESTED, + P2P_GROUP_REMOVAL_IDLE_TIMEOUT, + P2P_GROUP_REMOVAL_UNAVAILABLE, + P2P_GROUP_REMOVAL_GO_ENDING_SESSION, +#ifdef ANDROID_P2P + P2P_GROUP_REMOVAL_FREQ_CONFLICT +#endif +}; + #ifdef ANDROID_P2P static int wpas_global_scan_in_progress(struct wpa_supplicant *wpa_s); #endif @@ -129,6 +150,7 @@ static int wpas_p2p_scan(void *ctx, enum p2p_scan_type type, int freq, const u8 *req_dev_types, const u8 *dev_id, u16 pw_id) { struct wpa_supplicant *wpa_s = ctx; + struct wpa_supplicant *ifs; struct wpa_driver_scan_params params; int ret; struct wpabuf *wps_ie, *ies; @@ -138,6 +160,18 @@ static int wpas_p2p_scan(void *ctx, enum p2p_scan_type type, int freq, if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; + for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) { + if (ifs->sta_scan_pending && + wpas_p2p_in_progress(wpa_s) == 2) { + wpa_printf(MSG_DEBUG, "Delaying P2P scan to allow " + "pending station mode scan to be " + "completed on interface %s", ifs->ifname); + wpa_s->p2p_cb_on_scan_complete = 1; + wpa_supplicant_req_scan(ifs, 0, 0); + return 1; + } + } + os_memset(¶ms, 0, sizeof(params)); /* P2P Wildcard SSID */ @@ -241,14 +275,19 @@ static struct wpa_supplicant * wpas_get_p2p_group(struct wpa_supplicant *wpa_s, } -static void wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, int silent) +static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, + enum p2p_group_removal_reason removal_reason) { struct wpa_ssid *ssid; char *gtype; const char *reason; ssid = wpa_s->current_ssid; +#ifdef ANDROID_P2P + if ((ssid == NULL) && (wpa_s->p2p_group_interface == NOT_P2P_GROUP_INTERFACE)) { +#else if (ssid == NULL) { +#endif /* * The current SSID was not known, but there may still be a * pending P2P group interface waiting for provisioning. @@ -261,6 +300,11 @@ static void wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, int silent) break; ssid = ssid->next; } + if (ssid == NULL) { + wpa_printf(MSG_ERROR, "P2P: P2P group interface " + "not found"); + return -1; + } } if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_GO) gtype = "GO"; @@ -279,10 +323,13 @@ static void wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, int silent) P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s", wpa_s->ifname, wpa_s->cross_connect_uplink); } - switch (wpa_s->removal_reason) { + switch (removal_reason) { case P2P_GROUP_REMOVAL_REQUESTED: reason = " reason=REQUESTED"; break; + case P2P_GROUP_REMOVAL_FORMATION_FAILED: + reason = " reason=FORMATION_FAILED"; + break; case P2P_GROUP_REMOVAL_IDLE_TIMEOUT: reason = " reason=IDLE"; break; @@ -301,7 +348,7 @@ static void wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, int silent) reason = ""; break; } - if (!silent) { + if (removal_reason != P2P_GROUP_REMOVAL_SILENT) { wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_REMOVED "%s %s%s", wpa_s->ifname, gtype, reason); @@ -310,7 +357,7 @@ static void wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, int silent) if (eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL) > 0) wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group idle timeout"); - if (!silent && ssid) + if (removal_reason != P2P_GROUP_REMOVAL_SILENT && ssid) wpas_notify_p2p_group_removed(wpa_s, ssid, gtype); if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) { @@ -327,7 +374,7 @@ static void wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, int silent) if (wpa_s && ifname) wpa_drv_if_remove(wpa_s, type, ifname); os_free(ifname); - return; + return 0; } wpa_printf(MSG_DEBUG, "P2P: Remove temporary group network"); @@ -360,6 +407,8 @@ static void wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, int silent) wpa_supplicant_ap_deinit(wpa_s); else wpa_drv_deinit_p2p_cli(wpa_s); + + return 0; } @@ -517,6 +566,7 @@ static void wpas_p2p_add_persistent_group_client(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, *s; u8 *n; size_t i; + int found = 0; ssid = wpa_s->current_ssid; if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GO || @@ -537,17 +587,40 @@ static void wpas_p2p_add_persistent_group_client(struct wpa_supplicant *wpa_s, for (i = 0; s->p2p_client_list && i < s->num_p2p_clients; i++) { if (os_memcmp(s->p2p_client_list + i * ETH_ALEN, addr, - ETH_ALEN) == 0) - return; /* already in list */ + ETH_ALEN) != 0) + continue; + + if (i == s->num_p2p_clients - 1) + return; /* already the most recent entry */ + + /* move the entry to mark it most recent */ + os_memmove(s->p2p_client_list + i * ETH_ALEN, + s->p2p_client_list + (i + 1) * ETH_ALEN, + (s->num_p2p_clients - i - 1) * ETH_ALEN); + os_memcpy(s->p2p_client_list + + (s->num_p2p_clients - 1) * ETH_ALEN, addr, ETH_ALEN); + found = 1; + break; } - n = os_realloc(s->p2p_client_list, - (s->num_p2p_clients + 1) * ETH_ALEN); - if (n == NULL) - return; - os_memcpy(n + s->num_p2p_clients * ETH_ALEN, addr, ETH_ALEN); - s->p2p_client_list = n; - s->num_p2p_clients++; + if (!found && s->num_p2p_clients < P2P_MAX_STORED_CLIENTS) { + n = os_realloc_array(s->p2p_client_list, + s->num_p2p_clients + 1, ETH_ALEN); + if (n == NULL) + return; + os_memcpy(n + s->num_p2p_clients * ETH_ALEN, addr, ETH_ALEN); + s->p2p_client_list = n; + s->num_p2p_clients++; + } else if (!found) { + /* Not enough room for an additional entry - drop the oldest + * entry */ + os_memmove(s->p2p_client_list, + s->p2p_client_list + ETH_ALEN, + (s->num_p2p_clients - 1) * ETH_ALEN); + os_memcpy(s->p2p_client_list + + (s->num_p2p_clients - 1) * ETH_ALEN, + addr, ETH_ALEN); + } #ifndef CONFIG_NO_CONFIG_WRITE if (wpa_s->parent->conf->update_config && @@ -580,7 +653,8 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s, if (!success) { wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_FORMATION_FAILURE); - wpas_p2p_group_delete(wpa_s, 0); + wpas_p2p_group_delete(wpa_s, + P2P_GROUP_REMOVAL_FORMATION_FAILED); return; } @@ -835,6 +909,7 @@ static void wpas_start_wps_go(struct wpa_supplicant *wpa_s, ssid->mode = group_formation ? WPAS_MODE_P2P_GROUP_FORMATION : WPAS_MODE_P2P_GO; ssid->frequency = params->freq; + ssid->ht40 = params->ht40; ssid->ssid = os_zalloc(params->ssid_len + 1); if (ssid->ssid) { os_memcpy(ssid->ssid, params->ssid, params->ssid_len); @@ -845,6 +920,7 @@ static void wpas_start_wps_go(struct wpa_supplicant *wpa_s, ssid->proto = WPA_PROTO_RSN; ssid->pairwise_cipher = WPA_CIPHER_CCMP; ssid->passphrase = os_strdup(params->passphrase); + ssid->ap_max_inactivity = wpa_s->parent->conf->p2p_go_max_inactivity; wpa_s->ap_configured_cb = p2p_go_configured; wpa_s->ap_configured_cb_ctx = wpa_s; @@ -1032,6 +1108,9 @@ void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res) return; } + if (wpa_s->p2p_go_ht40) + res->ht40 = 1; + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_SUCCESS); wpas_notify_p2p_go_neg_completed(wpa_s, res); @@ -1449,6 +1528,62 @@ static void wpas_sd_req_upnp(struct wpa_supplicant *wpa_s, } +#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 */ + + void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token, u16 update_indic, const u8 *tlvs, size_t tlvs_len) { @@ -1540,6 +1675,12 @@ void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token, 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 */ default: wpa_printf(MSG_DEBUG, "P2P: Unavailable service " "protocol %u", srv_proto); @@ -1657,6 +1798,88 @@ u64 wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst, } +#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->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) + return 0; + 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->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) @@ -2084,7 +2307,7 @@ static void wpas_invitation_received(void *ctx, const u8 *sa, const u8 *bssid, MAC2STR(sa), op_freq); if (s) { wpas_p2p_group_add_persistent( - wpa_s, s, s->mode == WPAS_MODE_P2P_GO, 0); + wpa_s, s, s->mode == WPAS_MODE_P2P_GO, 0, 0); } else if (bssid) { wpas_p2p_join(wpa_s, bssid, go_dev_addr, wpa_s->p2p_wps_method, 0); @@ -2151,7 +2374,7 @@ static void wpas_invitation_result(void *ctx, int status, const u8 *bssid) } wpas_p2p_group_add_persistent(wpa_s, ssid, - ssid->mode == WPAS_MODE_P2P_GO, 0); + ssid->mode == WPAS_MODE_P2P_GO, 0, 0); } @@ -2288,26 +2511,48 @@ struct p2p_oper_class_map { enum { BW20, BW40PLUS, BW40MINUS } bw; }; +static struct p2p_oper_class_map op_class[] = { + { HOSTAPD_MODE_IEEE80211G, 81, 1, 13, 1, BW20 }, + { HOSTAPD_MODE_IEEE80211G, 82, 14, 14, 1, BW20 }, +#if 0 /* Do not enable HT40 on 2 GHz for now */ + { HOSTAPD_MODE_IEEE80211G, 83, 1, 9, 1, BW40PLUS }, + { HOSTAPD_MODE_IEEE80211G, 84, 5, 13, 1, BW40MINUS }, +#endif + { HOSTAPD_MODE_IEEE80211A, 115, 36, 48, 4, BW20 }, + { HOSTAPD_MODE_IEEE80211A, 124, 149, 161, 4, BW20 }, + { HOSTAPD_MODE_IEEE80211A, 116, 36, 44, 8, BW40PLUS }, + { HOSTAPD_MODE_IEEE80211A, 117, 40, 48, 8, BW40MINUS }, + { HOSTAPD_MODE_IEEE80211A, 126, 149, 157, 8, BW40PLUS }, + { HOSTAPD_MODE_IEEE80211A, 127, 153, 161, 8, BW40MINUS }, + { -1, 0, 0, 0, 0, BW20 } +}; + + +static int wpas_p2p_verify_channel(struct wpa_supplicant *wpa_s, + struct hostapd_hw_modes *mode, + u8 channel, u8 bw) +{ + int flag; + + if (!has_channel(wpa_s->global, mode, channel, &flag)) + return -1; + if (bw == BW40MINUS && + (!(flag & HOSTAPD_CHAN_HT40MINUS) || + !has_channel(wpa_s->global, mode, channel - 4, NULL))) + return 0; + if (bw == BW40PLUS && + (!(flag & HOSTAPD_CHAN_HT40PLUS) || + !has_channel(wpa_s->global, mode, channel + 4, NULL))) + return 0; + return 1; +} + + static int wpas_p2p_setup_channels(struct wpa_supplicant *wpa_s, struct p2p_channels *chan) { struct hostapd_hw_modes *mode; int cla, op; - struct p2p_oper_class_map op_class[] = { - { HOSTAPD_MODE_IEEE80211G, 81, 1, 13, 1, BW20 }, - { HOSTAPD_MODE_IEEE80211G, 82, 14, 14, 1, BW20 }, -#if 0 /* Do not enable HT40 on 2 GHz for now */ - { HOSTAPD_MODE_IEEE80211G, 83, 1, 9, 1, BW40PLUS }, - { HOSTAPD_MODE_IEEE80211G, 84, 5, 13, 1, BW40MINUS }, -#endif - { HOSTAPD_MODE_IEEE80211A, 115, 36, 48, 4, BW20 }, - { HOSTAPD_MODE_IEEE80211A, 124, 149, 161, 4, BW20 }, - { HOSTAPD_MODE_IEEE80211A, 116, 36, 44, 8, BW40PLUS }, - { HOSTAPD_MODE_IEEE80211A, 117, 40, 48, 8, BW40MINUS }, - { HOSTAPD_MODE_IEEE80211A, 126, 149, 157, 8, BW40PLUS }, - { HOSTAPD_MODE_IEEE80211A, 127, 153, 161, 8, BW40MINUS }, - { -1, 0, 0, 0, 0, BW20 } - }; if (wpa_s->hw.modes == NULL) { wpa_printf(MSG_DEBUG, "P2P: Driver did not support fetching " @@ -2327,16 +2572,7 @@ static int wpas_p2p_setup_channels(struct wpa_supplicant *wpa_s, if (mode == NULL) continue; for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) { - int flag; - if (!has_channel(wpa_s->global, mode, ch, &flag)) - continue; - if (o->bw == BW40MINUS && - (!(flag & HOSTAPD_CHAN_HT40MINUS) || - !has_channel(wpa_s->global, mode, ch - 4, NULL))) - continue; - if (o->bw == BW40PLUS && - (!(flag & HOSTAPD_CHAN_HT40PLUS) || - !has_channel(wpa_s->global, mode, ch + 4, NULL))) + if (wpas_p2p_verify_channel(wpa_s, mode, ch, o->bw) < 1) continue; if (reg == NULL) { wpa_printf(MSG_DEBUG, "P2P: Add operating " @@ -2360,6 +2596,32 @@ static int wpas_p2p_setup_channels(struct wpa_supplicant *wpa_s, } +int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s, + struct hostapd_hw_modes *mode, u8 channel) +{ + int op, ret; + + for (op = 0; op_class[op].op_class; op++) { + 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) + continue; + ret = wpas_p2p_verify_channel(wpa_s, mode, ch, o->bw); + if (ret < 0) + continue; + else if (ret > 0) + return (o->bw == BW40MINUS) ? -1 : 1; + else + return 0; + } + } + return 0; +} + + static int wpas_get_noa(void *ctx, const u8 *interface_addr, u8 *buf, size_t buf_len) { @@ -2540,7 +2802,6 @@ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s) p2p.p2p_intra_bss = wpa_s->conf->p2p_intra_bss; - #ifdef ANDROID_P2P if(wpa_s->drv_flags & WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT) p2p.p2p_concurrency = P2P_MULTI_CHANNEL_CONCURRENT; @@ -2607,13 +2868,14 @@ void wpas_p2p_deinit_global(struct wpa_global *global) { struct wpa_supplicant *wpa_s, *tmp; + wpa_s = global->ifaces; + if (wpa_s) + wpas_p2p_service_flush(wpa_s); + if (global->p2p == NULL) return; /* Remove remaining P2P group interfaces */ - wpa_s = global->ifaces; - if (wpa_s) - wpas_p2p_service_flush(wpa_s); while (wpa_s && wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) wpa_s = wpa_s->next; while (wpa_s) { @@ -2677,6 +2939,13 @@ static int wpas_p2p_start_go_neg(struct wpa_supplicant *wpa_s, force_freq, persistent_group); } + /* + * Increase GO config timeout if HT40 is used since it takes some time + * to scan channels for coex purposes before the BSS can be started. + */ + p2p_set_config_timeout(wpa_s->global->p2p, + wpa_s->p2p_go_ht40 ? 255 : 100, 20); + return p2p_connect(wpa_s->global->p2p, peer_addr, wps_method, go_intent, own_interface_addr, force_freq, persistent_group, ssid ? ssid->ssid : NULL, @@ -2881,7 +3150,8 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, wpa_s->p2p_go_intent, wpa_s->p2p_connect_freq, wpa_s->p2p_persistent_id, - wpa_s->p2p_pd_before_go_neg); + wpa_s->p2p_pd_before_go_neg, + wpa_s->p2p_go_ht40); return; } @@ -3177,6 +3447,7 @@ static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s) * parameters or -1 to generate new values (SSID/passphrase) * @pd: Whether to send Provision Discovery prior to GO Negotiation as an * interoperability workaround when initiating group formation + * @ht40: Start GO with 40 MHz channel width * Returns: 0 or new PIN (if pin was %NULL) on success, -1 on unspecified * failure, -2 on failure due to channel not currently available, * -3 if forced channel is not supported @@ -3184,7 +3455,8 @@ static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s) int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, const char *pin, enum p2p_wps_method wps_method, int persistent_group, int auto_join, int join, int auth, - int go_intent, int freq, int persistent_id, int pd) + int go_intent, int freq, int persistent_id, int pd, + int ht40) { int force_freq = 0, oper_freq = 0; u8 bssid[ETH_ALEN]; @@ -3216,6 +3488,7 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, wpa_s->p2p_connect_freq = freq; wpa_s->p2p_fallback_to_go_neg = 0; wpa_s->p2p_pd_before_go_neg = !!pd; + wpa_s->p2p_go_ht40 = !!ht40; if (pin) os_strlcpy(wpa_s->p2p_pin, pin, sizeof(wpa_s->p2p_pin)); @@ -3447,13 +3720,14 @@ int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname) static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s, struct p2p_go_neg_results *params, - int freq) + int freq, int ht40) { u8 bssid[ETH_ALEN]; int res; os_memset(params, 0, sizeof(*params)); params->role_go = 1; + params->ht40 = ht40; if (freq) { wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on forced " "frequency %d MHz", freq); @@ -3565,7 +3839,7 @@ wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated, * i.e., without using Group Owner Negotiation. */ int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group, - int freq) + int freq, int ht40) { struct p2p_go_neg_results params; unsigned int r; @@ -3623,7 +3897,7 @@ int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group, return -1; } - if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq)) + if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq, ht40)) return -1; if (params.freq && !p2p_supported_freq(wpa_s->global->p2p, params.freq)) { @@ -3690,7 +3964,7 @@ static int wpas_start_p2p_client(struct wpa_supplicant *wpa_s, int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, int addr_allocated, - int freq) + int freq, int ht40) { struct p2p_go_neg_results params; int go = 0; @@ -3716,7 +3990,7 @@ int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, if (ssid->mode != WPAS_MODE_P2P_GO) return -1; - if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq)) + if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq, ht40)) return -1; params.role_go = 1; @@ -3955,7 +4229,7 @@ static void wpas_p2p_clear_pending_action_tx(struct wpa_supplicant *wpa_s) int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout, enum p2p_discovery_type type, unsigned int num_req_dev_types, const u8 *req_dev_types, - const u8 *dev_id) + const u8 *dev_id, unsigned int search_delay) { wpas_p2p_clear_pending_action_tx(wpa_s); wpa_s->p2p_long_listen = 0; @@ -3970,7 +4244,8 @@ int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout, wpa_supplicant_cancel_sched_scan(wpa_s); return p2p_find(wpa_s->global->p2p, timeout, type, - num_req_dev_types, req_dev_types, dev_id); + num_req_dev_types, req_dev_types, dev_id, + search_delay); } @@ -4263,7 +4538,7 @@ void wpas_p2p_completed(struct wpa_supplicant *wpa_s) } if (!wpa_s->show_group_started || !ssid) - return; + goto done; wpa_s->show_group_started = 0; @@ -4305,6 +4580,19 @@ void wpas_p2p_completed(struct wpa_supplicant *wpa_s) if (network_id < 0) network_id = ssid->id; wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 1); + +done: + if (wpa_s->p2p_cb_on_scan_complete && !wpa_s->global->p2p_disabled && + wpa_s->global->p2p != NULL) { + wpa_s->p2p_cb_on_scan_complete = 0; + if (p2p_other_scan_completed(wpa_s->global->p2p) == 1) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Pending P2P operation " + "continued after successful connection"); + p2p_increase_search_delay( + wpa_s->global->p2p, + wpas_p2p_search_delay(wpa_s)); + } + } } @@ -4360,8 +4648,7 @@ static void wpas_p2p_group_idle_timeout(void *eloop_ctx, void *timeout_ctx) wpa_printf(MSG_DEBUG, "P2P: Group idle timeout reached - terminate " "group"); - wpa_s->removal_reason = P2P_GROUP_REMOVAL_IDLE_TIMEOUT; - wpas_p2p_group_delete(wpa_s, 0); + wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_IDLE_TIMEOUT); } @@ -4441,8 +4728,8 @@ void wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid, wpa_s->current_ssid->mode == WPAS_MODE_INFRA) { wpa_printf(MSG_DEBUG, "P2P: GO indicated that the P2P Group " "session is ending"); - wpa_s->removal_reason = P2P_GROUP_REMOVAL_GO_ENDING_SESSION; - wpas_p2p_group_delete(wpa_s, 0); + wpas_p2p_group_delete(wpa_s, + P2P_GROUP_REMOVAL_GO_ENDING_SESSION); } } @@ -4810,7 +5097,8 @@ int wpas_p2p_cancel(struct wpa_supplicant *wpa_s) found = 1; eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->parent, NULL); - wpas_p2p_group_delete(wpa_s, 0); + wpas_p2p_group_delete(wpa_s, + P2P_GROUP_REMOVAL_REQUESTED); break; } } @@ -4831,8 +5119,7 @@ void wpas_p2p_interface_unavailable(struct wpa_supplicant *wpa_s) wpa_printf(MSG_DEBUG, "P2P: Remove group due to driver resource not " "being available anymore"); - wpa_s->removal_reason = P2P_GROUP_REMOVAL_UNAVAILABLE; - wpas_p2p_group_delete(wpa_s, 0); + wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_UNAVAILABLE); } @@ -4878,10 +5165,7 @@ int wpas_p2p_disconnect(struct wpa_supplicant *wpa_s) if (wpa_s == NULL) return -1; - wpa_s->removal_reason = P2P_GROUP_REMOVAL_REQUESTED; - wpas_p2p_group_delete(wpa_s, 0); - - return 0; + return wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_REQUESTED); } @@ -4984,13 +5268,14 @@ static void wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s, wpa_s = wpa_s->parent; offchannel_send_action_done(wpa_s); if (group_added) - wpas_p2p_group_delete(group, 1); + wpas_p2p_group_delete(group, P2P_GROUP_REMOVAL_SILENT); wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Fall back to GO Negotiation"); wpas_p2p_connect(wpa_s, wpa_s->pending_join_dev_addr, wpa_s->p2p_pin, wpa_s->p2p_wps_method, wpa_s->p2p_persistent_group, 0, 0, 0, wpa_s->p2p_go_intent, wpa_s->p2p_connect_freq, wpa_s->p2p_persistent_id, - wpa_s->p2p_pd_before_go_neg); + wpa_s->p2p_pd_before_go_neg, + wpa_s->p2p_go_ht40); } @@ -5010,6 +5295,44 @@ int wpas_p2p_scan_no_go_seen(struct wpa_supplicant *wpa_s) return 1; } + +unsigned int wpas_p2p_search_delay(struct wpa_supplicant *wpa_s) +{ + const char *rn, *rn2; + struct wpa_supplicant *ifs; + + if (wpa_s->wpa_state > WPA_SCANNING) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use %u ms search delay due to " + "concurrent operation", + P2P_CONCURRENT_SEARCH_DELAY); + return P2P_CONCURRENT_SEARCH_DELAY; + } + + if (!wpa_s->driver->get_radio_name) + return 0; + rn = wpa_s->driver->get_radio_name(wpa_s->drv_priv); + if (rn == NULL || rn[0] == '\0') + return 0; + + for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) { + if (ifs == wpa_s || !ifs->driver->get_radio_name) + continue; + + rn2 = ifs->driver->get_radio_name(ifs->drv_priv); + if (!rn2 || os_strcmp(rn, rn2) != 0) + continue; + if (ifs->wpa_state > WPA_SCANNING) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use %u ms search " + "delay due to concurrent operation on " + "interface %s", + P2P_CONCURRENT_SEARCH_DELAY, ifs->ifname); + return P2P_CONCURRENT_SEARCH_DELAY; + } + } + + return 0; +} + #ifdef ANDROID_P2P int wpas_p2p_handle_frequency_conflicts(struct wpa_supplicant *wpa_s, int freq) { @@ -5039,8 +5362,7 @@ int wpas_p2p_handle_frequency_conflicts(struct wpa_supplicant *wpa_s, int freq) * P2P connection. So remove the interface */ wpa_printf(MSG_DEBUG, "P2P: Removing P2P connection due to Single channel" "concurrent mode frequency conflict"); - iface->removal_reason = P2P_GROUP_REMOVAL_FREQ_CONFLICT; - wpas_p2p_group_delete(iface, 0); + wpas_p2p_group_delete(iface, P2P_GROUP_REMOVAL_FREQ_CONFLICT); } else { /* Existing connection has the priority. Disable the newly * selected network and let the application know about it. diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h index 5af323f6..bbdd83e7 100644 --- a/wpa_supplicant/p2p_supplicant.h +++ b/wpa_supplicant/p2p_supplicant.h @@ -21,7 +21,7 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, const char *pin, enum p2p_wps_method wps_method, int persistent_group, int auto_join, int join, int auth, int go_intent, int freq, int persistent_id, - int pd); + int pd, int ht40); void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s, unsigned int freq, unsigned int duration); void wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, @@ -32,10 +32,10 @@ int wpas_p2p_handle_frequency_conflicts(struct wpa_supplicant *wpa_s, #endif int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname); int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group, - int freq); + int freq, int ht40); int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, int addr_allocated, - int freq); + int freq, int ht40); struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr, @@ -57,7 +57,7 @@ enum p2p_discovery_type; int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout, enum p2p_discovery_type type, unsigned int num_req_dev_types, const u8 *req_dev_types, - const u8 *dev_id); + const u8 *dev_id, unsigned int search_delay); void wpas_p2p_stop_find(struct wpa_supplicant *wpa_s); int wpas_p2p_listen(struct wpa_supplicant *wpa_s, unsigned int timeout); int wpas_p2p_assoc_req_ie(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, @@ -90,6 +90,8 @@ u64 wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst, const struct wpabuf *tlvs); u64 wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst, u8 version, const char *query); +u64 wpas_p2p_sd_request_wifi_display(struct wpa_supplicant *wpa_s, + const u8 *dst, const char *role); int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, u64 req); void wpas_p2p_sd_response(struct wpa_supplicant *wpa_s, int freq, const u8 *dst, u8 dialog_token, @@ -149,5 +151,8 @@ struct wpa_ssid * wpas_p2p_get_persistent(struct wpa_supplicant *wpa_s, void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s, const u8 *addr); int wpas_p2p_scan_no_go_seen(struct wpa_supplicant *wpa_s); +int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s, + struct hostapd_hw_modes *mode, u8 channel); +unsigned int wpas_p2p_search_delay(struct wpa_supplicant *wpa_s); #endif /* P2P_SUPPLICANT_H */ diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c index 8df21d9c..d8db81fb 100644 --- a/wpa_supplicant/scan.c +++ b/wpa_supplicant/scan.c @@ -89,6 +89,9 @@ int wpa_supplicant_enabled_networks(struct wpa_supplicant *wpa_s) count++; ssid = ssid->next; } + if (wpa_s->conf->cred && wpa_s->conf->interworking && + wpa_s->conf->auto_interworking) + count++; return count; } @@ -138,7 +141,7 @@ static void int_array_concat(int **res, const int *a) reslen = int_array_len(*res); alen = int_array_len(a); - n = os_realloc(*res, (reslen + alen + 1) * sizeof(int)); + n = os_realloc_array(*res, reslen + alen + 1, sizeof(int)); if (n == NULL) { os_free(*res); *res = NULL; @@ -385,9 +388,7 @@ static void wpas_add_interworking_elements(struct wpa_supplicant *wpa_s, #endif /* CONFIG_INTERWORKING */ -static struct wpabuf * -wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s, - struct wpa_driver_scan_params *params) +static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s) { struct wpabuf *extra_ie = NULL; #ifdef CONFIG_WPS @@ -475,15 +476,18 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) #ifdef CONFIG_P2P if (wpas_p2p_in_progress(wpa_s)) { - if (wpa_s->wpa_state == WPA_SCANNING) { + if (wpa_s->sta_scan_pending && + wpas_p2p_in_progress(wpa_s) == 2 && + wpa_s->p2p_cb_on_scan_complete) { + wpa_dbg(wpa_s, MSG_DEBUG, "Process pending station " + "mode scan during P2P search"); + } else { wpa_dbg(wpa_s, MSG_DEBUG, "Delay station mode scan " "while P2P operation is in progress"); + wpa_s->sta_scan_pending = 1; wpa_supplicant_req_scan(wpa_s, 5, 0); - } else { - wpa_dbg(wpa_s, MSG_DEBUG, "Do not request scan while " - "P2P operation is in progress"); + return; } - return; } #endif /* CONFIG_P2P */ @@ -642,7 +646,7 @@ ssid_list_set: #endif /* CONFIG_P2P */ wpa_supplicant_optimize_freqs(wpa_s, ¶ms); - extra_ie = wpa_supplicant_extra_ies(wpa_s, ¶ms); + extra_ie = wpa_supplicant_extra_ies(wpa_s); #ifdef CONFIG_HS20 if (wpa_s->conf->hs20 && wpabuf_resize(&extra_ie, 6) == 0) @@ -771,7 +775,7 @@ int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s) struct wpa_driver_scan_params *scan_params; enum wpa_states prev_state; struct wpa_ssid *ssid = NULL; - struct wpabuf *wps_ie = NULL; + struct wpabuf *extra_ie = NULL; int ret; unsigned int max_sched_scan_ssids; int wildcard = 0; @@ -935,8 +939,11 @@ int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s) params.filter_ssids = NULL; } - if (wpa_s->wps) - wps_ie = wpa_supplicant_extra_ies(wpa_s, ¶ms); + extra_ie = wpa_supplicant_extra_ies(wpa_s); + if (extra_ie) { + params.extra_ies = wpabuf_head(extra_ie); + params.extra_ies_len = wpabuf_len(extra_ie); + } scan_params = ¶ms; @@ -953,7 +960,7 @@ scan: ret = wpa_supplicant_start_sched_scan(wpa_s, scan_params, wpa_s->sched_scan_interval); - wpabuf_free(wps_ie); + wpabuf_free(extra_ie); os_free(params.filter_ssids); if (ret) { wpa_msg(wpa_s, MSG_WARNING, "Failed to initiate sched scan"); diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index c7187e43..b09e5f18 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -127,6 +127,15 @@ void sme_authenticate(struct wpa_supplicant *wpa_s, "key management and encryption suites"); return; } + } else if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && + wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt)) { + /* + * Both WPA and non-WPA IEEE 802.1X enabled in configuration - + * use non-WPA since the scan results did not indicate that the + * AP is using WPA or WPA2. + */ + wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); + wpa_s->sme.assoc_req_ie_len = 0; } else if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) { wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie); if (wpa_supplicant_set_suites(wpa_s, NULL, ssid, @@ -802,7 +811,7 @@ static void wpa_setband_scan_freqs_list(struct wpa_supplicant *wpa_s, return; } - params->freqs = os_zalloc((mode->num_channels + 1) * sizeof(int)); + params->freqs = os_calloc(mode->num_channels + 1, sizeof(int)); if (params->freqs == NULL) return; for (count = 0, i = 0; i < mode->num_channels; i++) { @@ -843,6 +852,8 @@ void sme_sched_obss_scan(struct wpa_supplicant *wpa_s, int enable) const u8 *ie; struct wpa_bss *bss = wpa_s->current_bss; struct wpa_ssid *ssid = wpa_s->current_ssid; + struct hostapd_hw_modes *hw_mode = NULL; + int i; eloop_cancel_timeout(sme_obss_scan_timeout, wpa_s, NULL); wpa_s->sme.sched_obss_scan = 0; @@ -853,9 +864,20 @@ void sme_sched_obss_scan(struct wpa_supplicant *wpa_s, int enable) ssid->mode != IEEE80211_MODE_INFRA) return; /* Not using station SME in wpa_supplicant */ - if (!wpa_s->hw.modes || - !(wpa_s->hw.modes->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) - return; /* Driver does not support HT40 */ + if (!wpa_s->hw.modes) + return; + + /* only HT caps in 11g mode are relevant */ + for (i = 0; i < wpa_s->hw.num_modes; i++) { + hw_mode = &wpa_s->hw.modes[i]; + if (hw_mode->mode == HOSTAPD_MODE_IEEE80211G) + break; + } + + /* Driver does not support HT40 for 11g or doesn't have 11g. */ + if (i == wpa_s->hw.num_modes || !hw_mode || + !(hw_mode->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) + return; if (bss == NULL || bss->freq < 2400 || bss->freq > 2500) return; /* Not associated on 2.4 GHz band */ @@ -938,9 +960,9 @@ static void sme_sa_query_timer(void *eloop_ctx, void *timeout_ctx) sme_check_sa_query_timeout(wpa_s)) return; - nbuf = os_realloc(wpa_s->sme.sa_query_trans_id, - (wpa_s->sme.sa_query_count + 1) * - WLAN_SA_QUERY_TR_ID_LEN); + nbuf = os_realloc_array(wpa_s->sme.sa_query_trans_id, + wpa_s->sme.sa_query_count + 1, + WLAN_SA_QUERY_TR_ID_LEN); if (nbuf == NULL) return; if (wpa_s->sme.sa_query_count == 0) { diff --git a/wpa_supplicant/tests/test_eap_sim_common.c b/wpa_supplicant/tests/test_eap_sim_common.c index 44488588..f60b1821 100644 --- a/wpa_supplicant/tests/test_eap_sim_common.c +++ b/wpa_supplicant/tests/test_eap_sim_common.c @@ -28,7 +28,7 @@ static int test_eap_sim_prf(void) printf("Testing EAP-SIM PRF (FIPS 186-2 + change notice 1)\n"); eap_sim_prf(xkey, buf, sizeof(buf)); - if (memcmp(w, buf, sizeof(w) != 0)) { + if (memcmp(w, buf, sizeof(w)) != 0) { printf("eap_sim_prf failed\n"); return 1; } diff --git a/wpa_supplicant/wifi_display.c b/wpa_supplicant/wifi_display.c new file mode 100644 index 00000000..92ca5360 --- /dev/null +++ b/wpa_supplicant/wifi_display.c @@ -0,0 +1,251 @@ +/* + * wpa_supplicant - Wi-Fi Display + * Copyright (c) 2011, Atheros Communications, Inc. + * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "p2p/p2p.h" +#include "common/ieee802_11_defs.h" +#include "wpa_supplicant_i.h" +#include "wifi_display.h" + + +int wifi_display_init(struct wpa_global *global) +{ + global->wifi_display = 1; + return 0; +} + + +void wifi_display_deinit(struct wpa_global *global) +{ + int i; + for (i = 0; i < MAX_WFD_SUBELEMS; i++) { + wpabuf_free(global->wfd_subelem[i]); + global->wfd_subelem[i] = NULL; + } +} + + +static int wifi_display_update_wfd_ie(struct wpa_global *global) +{ + struct wpabuf *ie, *buf; + size_t len, plen; + + wpa_printf(MSG_DEBUG, "WFD: Update WFD IE"); + + if (!global->wifi_display) { + wpa_printf(MSG_DEBUG, "WFD: Wi-Fi Display disabled - do not " + "include WFD IE"); + p2p_set_wfd_ie_beacon(global->p2p, NULL); + p2p_set_wfd_ie_probe_req(global->p2p, NULL); + p2p_set_wfd_ie_probe_resp(global->p2p, NULL); + p2p_set_wfd_ie_assoc_req(global->p2p, NULL); + p2p_set_wfd_ie_invitation(global->p2p, NULL); + p2p_set_wfd_ie_prov_disc_req(global->p2p, NULL); + p2p_set_wfd_ie_prov_disc_resp(global->p2p, NULL); + p2p_set_wfd_ie_go_neg(global->p2p, NULL); + p2p_set_wfd_dev_info(global->p2p, NULL); + p2p_set_wfd_assoc_bssid(global->p2p, NULL); + p2p_set_wfd_coupled_sink_info(global->p2p, NULL); + return 0; + } + + p2p_set_wfd_dev_info(global->p2p, + global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]); + p2p_set_wfd_assoc_bssid( + global->p2p, + global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID]); + p2p_set_wfd_coupled_sink_info( + global->p2p, global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK]); + + /* + * WFD IE is included in number of management frames. Two different + * sets of subelements are included depending on the frame: + * + * Beacon, (Re)Association Request, GO Negotiation Req/Resp/Conf, + * Provision Discovery Req: + * WFD Device Info + * [Associated BSSID] + * [Coupled Sink Info] + * + * Probe Request: + * WFD Device Info + * [Associated BSSID] + * [Coupled Sink Info] + * [WFD Extended Capability] + * + * Probe Response: + * WFD Device Info + * [Associated BSSID] + * [Coupled Sink Info] + * [WFD Extended Capability] + * [WFD Session Info] + * + * (Re)Association Response, P2P Invitation Req/Resp, + * Provision Discovery Resp: + * WFD Device Info + * [Associated BSSID] + * [Coupled Sink Info] + * [WFD Session Info] + */ + len = 0; + if (global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]) + len += wpabuf_len(global->wfd_subelem[ + WFD_SUBELEM_DEVICE_INFO]); + if (global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID]) + len += wpabuf_len(global->wfd_subelem[ + WFD_SUBELEM_ASSOCIATED_BSSID]); + if (global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK]) + len += wpabuf_len(global->wfd_subelem[ + WFD_SUBELEM_COUPLED_SINK]); + if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO]) + len += wpabuf_len(global->wfd_subelem[ + WFD_SUBELEM_SESSION_INFO]); + if (global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB]) + len += wpabuf_len(global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB]); + buf = wpabuf_alloc(len); + if (buf == NULL) + return -1; + + if (global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]) + wpabuf_put_buf(buf, + global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]); + if (global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID]) + wpabuf_put_buf(buf, global->wfd_subelem[ + WFD_SUBELEM_ASSOCIATED_BSSID]); + if (global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK]) + wpabuf_put_buf(buf, + global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK]); + + ie = wifi_display_encaps(buf); + wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Beacon", ie); + p2p_set_wfd_ie_beacon(global->p2p, ie); + + ie = wifi_display_encaps(buf); + wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for (Re)Association Request", + ie); + p2p_set_wfd_ie_assoc_req(global->p2p, ie); + + ie = wifi_display_encaps(buf); + wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for GO Negotiation", ie); + p2p_set_wfd_ie_go_neg(global->p2p, ie); + + ie = wifi_display_encaps(buf); + wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Provision Discovery " + "Request", ie); + p2p_set_wfd_ie_prov_disc_req(global->p2p, ie); + + plen = buf->used; + if (global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB]) + wpabuf_put_buf(buf, + global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB]); + + ie = wifi_display_encaps(buf); + wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Probe Request", ie); + p2p_set_wfd_ie_probe_req(global->p2p, ie); + + if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO]) + wpabuf_put_buf(buf, + global->wfd_subelem[WFD_SUBELEM_SESSION_INFO]); + ie = wifi_display_encaps(buf); + wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Probe Response", ie); + p2p_set_wfd_ie_probe_resp(global->p2p, ie); + + /* Remove WFD Extended Capability from buffer */ + buf->used = plen; + if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO]) + wpabuf_put_buf(buf, + global->wfd_subelem[WFD_SUBELEM_SESSION_INFO]); + + ie = wifi_display_encaps(buf); + wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for P2P Invitation", ie); + p2p_set_wfd_ie_invitation(global->p2p, ie); + + ie = wifi_display_encaps(buf); + wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Provision Discovery " + "Response", ie); + p2p_set_wfd_ie_prov_disc_resp(global->p2p, ie); + + wpabuf_free(buf); + + return 0; +} + + +void wifi_display_enable(struct wpa_global *global, int enabled) +{ + wpa_printf(MSG_DEBUG, "WFD: Wi-Fi Display %s", + enabled ? "enabled" : "disabled"); + global->wifi_display = enabled; + wifi_display_update_wfd_ie(global); +} + + +int wifi_display_subelem_set(struct wpa_global *global, char *cmd) +{ + char *pos; + int subelem; + size_t len; + struct wpabuf *e; + + pos = os_strchr(cmd, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + subelem = atoi(cmd); + if (subelem < 0 || subelem >= MAX_WFD_SUBELEMS) + return -1; + + len = os_strlen(pos); + if (len & 1) + return -1; + len /= 2; + + if (len == 0) { + /* Clear subelement */ + e = NULL; + wpa_printf(MSG_DEBUG, "WFD: Clear subelement %d", subelem); + } else { + e = wpabuf_alloc(1 + len); + if (e == NULL) + return -1; + wpabuf_put_u8(e, subelem); + if (hexstr2bin(pos, wpabuf_put(e, len), len) < 0) { + wpabuf_free(e); + return -1; + } + wpa_printf(MSG_DEBUG, "WFD: Set subelement %d", subelem); + } + + wpabuf_free(global->wfd_subelem[subelem]); + global->wfd_subelem[subelem] = e; + wifi_display_update_wfd_ie(global); + + return 0; +} + + +int wifi_display_subelem_get(struct wpa_global *global, char *cmd, + char *buf, size_t buflen) +{ + int subelem; + + subelem = atoi(cmd); + if (subelem < 0 || subelem >= MAX_WFD_SUBELEMS) + return -1; + + if (global->wfd_subelem[subelem] == NULL) + return 0; + + return wpa_snprintf_hex(buf, buflen, + wpabuf_head_u8(global->wfd_subelem[subelem]) + + 1, + wpabuf_len(global->wfd_subelem[subelem]) - 1); +} diff --git a/wpa_supplicant/wifi_display.h b/wpa_supplicant/wifi_display.h new file mode 100644 index 00000000..b75d4f2d --- /dev/null +++ b/wpa_supplicant/wifi_display.h @@ -0,0 +1,20 @@ +/* + * wpa_supplicant - Wi-Fi Display + * Copyright (c) 2011, Atheros Communications, Inc. + * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WIFI_DISPLAY_H +#define WIFI_DISPLAY_H + +int wifi_display_init(struct wpa_global *global); +void wifi_display_deinit(struct wpa_global *global); +void wifi_display_enable(struct wpa_global *global, int enabled); +int wifi_display_subelem_set(struct wpa_global *global, char *cmd); +int wifi_display_subelem_get(struct wpa_global *global, char *cmd, + char *buf, size_t buflen); + +#endif /* WIFI_DISPLAY_H */ diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c new file mode 100644 index 00000000..98ce9661 --- /dev/null +++ b/wpa_supplicant/wnm_sta.c @@ -0,0 +1,248 @@ +/* + * wpa_supplicant - WNM + * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "rsn_supp/wpa.h" +#include "../wpa_supplicant/wpa_supplicant_i.h" +#include "../wpa_supplicant/driver_i.h" + +#define MAX_TFS_IE_LEN 1024 + +#ifdef CONFIG_IEEE80211V + +/* get the TFS IE from driver */ +static int ieee80211_11_get_tfs_ie(struct wpa_supplicant *wpa_s, u8 *buf, + u16 *buf_len, enum wnm_oper oper) +{ + wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper); + + return wpa_drv_wnm_oper(wpa_s, oper, wpa_s->bssid, buf, buf_len); +} + + +/* set the TFS IE to driver */ +static int ieee80211_11_set_tfs_ie(struct wpa_supplicant *wpa_s, + const u8 *addr, u8 *buf, u16 *buf_len, + enum wnm_oper oper) +{ + wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper); + + return wpa_drv_wnm_oper(wpa_s, oper, addr, buf, buf_len); +} + + +/* MLME-SLEEPMODE.request */ +int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s, + u8 action, u8 intval) +{ + struct ieee80211_mgmt *mgmt; + int res; + size_t len; + struct wnm_sleep_element *wnmsleep_ie; + u8 *wnmtfs_ie; + u8 wnmsleep_ie_len; + u16 wnmtfs_ie_len; /* possibly multiple IE(s) */ + enum wnm_oper tfs_oper = action == 0 ? WNM_SLEEP_TFS_REQ_IE_ADD : + WNM_SLEEP_TFS_REQ_IE_NONE; + + /* WNM-Sleep Mode IE */ + wnmsleep_ie_len = sizeof(struct wnm_sleep_element); + wnmsleep_ie = os_zalloc(sizeof(struct wnm_sleep_element)); + if (wnmsleep_ie == NULL) + return -1; + wnmsleep_ie->eid = WLAN_EID_WNMSLEEP; + wnmsleep_ie->len = wnmsleep_ie_len - 2; + wnmsleep_ie->action_type = action; + wnmsleep_ie->status = WNM_STATUS_SLEEP_ACCEPT; + wnmsleep_ie->intval = intval; + + /* TFS IE(s) */ + wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN); + if (wnmtfs_ie == NULL) { + os_free(wnmsleep_ie); + return -1; + } + if (ieee80211_11_get_tfs_ie(wpa_s, wnmtfs_ie, &wnmtfs_ie_len, + tfs_oper)) { + wnmtfs_ie_len = 0; + os_free(wnmtfs_ie); + wnmtfs_ie = NULL; + } + + mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + wnmtfs_ie_len); + if (mgmt == NULL) { + wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for " + "WNM-Sleep Request action frame"); + return -1; + } + + os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); + os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); + os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + mgmt->u.action.category = WLAN_ACTION_WNM; + mgmt->u.action.u.wnm_sleep_req.action = WNM_SLEEP_MODE_REQ; + os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable, wnmsleep_ie, + wnmsleep_ie_len); + /* copy TFS IE here */ + if (wnmtfs_ie_len > 0) { + os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable + + wnmsleep_ie_len, wnmtfs_ie, wnmtfs_ie_len); + } + + len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_req) + wnmsleep_ie_len + + wnmtfs_ie_len; + + res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, + wpa_s->own_addr, wpa_s->bssid, + &mgmt->u.action.category, len, 0); + if (res < 0) + wpa_printf(MSG_DEBUG, "Failed to send WNM-Sleep Request " + "(action=%d, intval=%d)", action, intval); + + os_free(wnmsleep_ie); + os_free(wnmtfs_ie); + os_free(mgmt); + + return res; +} + + +static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s, + const u8 *frm, int len) +{ + /* + * Action [1] | Diaglog Token [1] | Key Data Len [2] | Key Data | + * WNM-Sleep Mode IE | TFS Response IE + */ + u8 *pos = (u8 *) frm; /* point to action field */ + u16 key_len_total = le_to_host16(*((u16 *)(frm+2))); + u8 gtk_len; +#ifdef CONFIG_IEEE80211W + u8 igtk_len; +#endif /* CONFIG_IEEE80211W */ + struct wnm_sleep_element *wnmsleep_ie = NULL; + /* multiple TFS Resp IE (assuming consecutive) */ + u8 *tfsresp_ie_start = NULL; + u8 *tfsresp_ie_end = NULL; + u16 tfsresp_ie_len = 0; + + wpa_printf(MSG_DEBUG, "action=%d token = %d key_len_total = %d", + frm[0], frm[1], key_len_total); + pos += 4 + key_len_total; + while (pos - frm < len) { + u8 ie_len = *(pos + 1); + if (*pos == WLAN_EID_WNMSLEEP) + wnmsleep_ie = (struct wnm_sleep_element *) pos; + else if (*pos == WLAN_EID_TFS_RESP) { + if (!tfsresp_ie_start) + tfsresp_ie_start = pos; + tfsresp_ie_end = pos; + } else + wpa_printf(MSG_DEBUG, "EID %d not recognized", *pos); + pos += ie_len + 2; + } + + if (!wnmsleep_ie) { + wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found"); + return; + } + + if (wnmsleep_ie->status == WNM_STATUS_SLEEP_ACCEPT) { + wpa_printf(MSG_DEBUG, "Successfully recv WNM-Sleep Response " + "frame (action=%d, intval=%d)", + wnmsleep_ie->action_type, wnmsleep_ie->intval); + if (wnmsleep_ie->action_type == 0) { + wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_CONFIRM, + wpa_s->bssid, NULL, NULL); + /* remove GTK/IGTK ?? */ + + /* set the TFS Resp IE(s) */ + if (tfsresp_ie_start && tfsresp_ie_end && + tfsresp_ie_end - tfsresp_ie_start >= 0) { + tfsresp_ie_len = (tfsresp_ie_end + + tfsresp_ie_end[1] + 2) - + tfsresp_ie_start; + wpa_printf(MSG_DEBUG, "TFS Resp IE(s) found"); + /* + * pass the TFS Resp IE(s) to driver for + * processing + */ + if (ieee80211_11_set_tfs_ie( + wpa_s, wpa_s->bssid, + tfsresp_ie_start, + &tfsresp_ie_len, + WNM_SLEEP_TFS_RESP_IE_SET)) + wpa_printf(MSG_DEBUG, "Fail to set " + "TFS Resp IE"); + } + } else if (wnmsleep_ie->action_type == 1) { + wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_CONFIRM, + wpa_s->bssid, NULL, NULL); + /* Install GTK/IGTK */ + do { + /* point to key data field */ + u8 *ptr = (u8 *) frm + 1 + 1 + 2; + while (ptr < (u8 *) frm + 4 + key_len_total) { + if (*ptr == WNM_SLEEP_SUBELEM_GTK) { + gtk_len = *(ptr + 4); + wpa_wnmsleep_install_key( + wpa_s->wpa, + WNM_SLEEP_SUBELEM_GTK, + ptr); + ptr += 13 + gtk_len; +#ifdef CONFIG_IEEE80211W + } else if (*ptr == + WNM_SLEEP_SUBELEM_IGTK) { + igtk_len = WPA_IGTK_LEN; + wpa_wnmsleep_install_key( + wpa_s->wpa, + WNM_SLEEP_SUBELEM_IGTK, + ptr); + ptr += 10 + WPA_IGTK_LEN; +#endif /* CONFIG_IEEE80211W */ + } else + break; /* skip the loop */ + } + } while(0); + } + } else { + wpa_printf(MSG_DEBUG, "Reject recv WNM-Sleep Response frame " + "(action=%d, intval=%d)", + wnmsleep_ie->action_type, wnmsleep_ie->intval); + if (wnmsleep_ie->action_type == 0) + wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_FAIL, + wpa_s->bssid, NULL, NULL); + else if (wnmsleep_ie->action_type == 1) + wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_FAIL, + wpa_s->bssid, NULL, NULL); + } +} + + +void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s, + struct rx_action *action) +{ + u8 *pos = (u8 *) action->data; /* point to action field */ + u8 act = *pos++; + /* u8 dialog_token = *pos++; */ + + switch (act) { + case WNM_SLEEP_MODE_RESP: + ieee802_11_rx_wnmsleep_resp(wpa_s, action->data, action->len); + break; + default: + break; + } +} + +#endif /* CONFIG_IEEE80211V */ diff --git a/wpa_supplicant/wnm_sta.h b/wpa_supplicant/wnm_sta.h new file mode 100644 index 00000000..ba2535b0 --- /dev/null +++ b/wpa_supplicant/wnm_sta.h @@ -0,0 +1,21 @@ +/* + * IEEE 802.11v WNM related functions and structures + * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WNM_STA_H +#define WNM_STA_H + +struct rx_action; +struct wpa_supplicant; + +int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s, + u8 action, u8 intval); + +void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s, + struct rx_action *action); + +#endif /* WNM_STA_H */ diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c index bed13dd9..3986b9be 100644 --- a/wpa_supplicant/wpa_cli.c +++ b/wpa_supplicant/wpa_cli.c @@ -95,8 +95,11 @@ static DEFINE_DL_LIST(p2p_peers); /* struct cli_txt_entry */ static DEFINE_DL_LIST(p2p_groups); /* struct cli_txt_entry */ -static void print_help(void); +static void print_help(const char *cmd); static void wpa_cli_mon_receive(int sock, void *eloop_ctx, void *sock_ctx); +static void wpa_cli_close_connection(void); +static char * wpa_cli_get_default_ifname(void); +static char ** wpa_list_cmd_list(void); static void usage(void) @@ -113,7 +116,7 @@ static void usage(void) " -B = run a daemon in the background\n" " default path: " CONFIG_CTRL_IFACE_DIR "\n" " default interface: first interface found in socket path\n"); - print_help(); + print_help(NULL); } @@ -241,7 +244,7 @@ static char ** cli_txt_list_array(struct dl_list *txt_list) char **res; struct cli_txt_entry *e; - res = os_zalloc((count + 1) * sizeof(char *)); + res = os_calloc(count + 1, sizeof(char *)); if (res == NULL) return NULL; @@ -364,6 +367,7 @@ static int wpa_cli_open_connection(const char *ifname, int attach) } else { printf("Warning: Failed to attach to " "wpa_supplicant.\n"); + wpa_cli_close_connection(); return -1; } } @@ -456,6 +460,58 @@ static int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd) } +static int write_cmd(char *buf, size_t buflen, const char *cmd, int argc, + char *argv[]) +{ + int i, res; + char *pos, *end; + + pos = buf; + end = buf + buflen; + + res = os_snprintf(pos, end - pos, "%s", cmd); + if (res < 0 || res >= end - pos) + goto fail; + pos += res; + + for (i = 0; i < argc; i++) { + res = os_snprintf(pos, end - pos, " %s", argv[i]); + if (res < 0 || res >= end - pos) + goto fail; + pos += res; + } + + buf[buflen - 1] = '\0'; + return 0; + +fail: + printf("Too long command\n"); + return -1; +} + + +static int wpa_cli_cmd(struct wpa_ctrl *ctrl, const char *cmd, int min_args, + int argc, char *argv[]) +{ + char buf[256]; + if (argc < min_args) { + printf("Invalid %s command - at least %d argument%s " + "required.\n", cmd, min_args, + min_args > 1 ? "s are" : " is"); + return -1; + } + if (write_cmd(buf, sizeof(buf), cmd, argc, argv) < 0) + return -1; + return wpa_ctrl_command(ctrl, buf); +} + + +static int wpa_cli_cmd_ifname(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "IFNAME"); +} + + static int wpa_cli_cmd_status(struct wpa_ctrl *ctrl, int argc, char *argv[]) { if (argc > 0 && os_strcmp(argv[0], "verbose") == 0) @@ -480,14 +536,7 @@ static int wpa_cli_cmd_relog(struct wpa_ctrl *ctrl, int argc, char *argv[]) static int wpa_cli_cmd_note(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int ret; - if (argc == 0) - return -1; - ret = os_snprintf(cmd, sizeof(cmd), "NOTE %s", argv[0]); - if (ret < 0 || (size_t) ret >= sizeof(cmd)) - return -1; - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "NOTE", 1, argc, argv); } @@ -505,11 +554,26 @@ static int wpa_cli_cmd_pmksa(struct wpa_ctrl *ctrl, int argc, char *argv[]) static int wpa_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - print_help(); + print_help(argc > 0 ? argv[0] : NULL); return 0; } +static char ** wpa_cli_complete_help(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + char **res = NULL; + + switch (arg) { + case 1: + res = wpa_list_cmd_list(); + break; + } + + return res; +} + + static int wpa_cli_cmd_license(struct wpa_ctrl *ctrl, int argc, char *argv[]) { printf("%s\n\n%s\n", wpa_cli_version, wpa_cli_full_license); @@ -577,21 +641,7 @@ static int wpa_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[]) static int wpa_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - - if (argc != 1) { - printf("Invalid GET command: need one argument (variable " - "name)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "GET %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long GET command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "GET", 1, argc, argv); } @@ -617,100 +667,34 @@ static int wpa_cli_cmd_reassociate(struct wpa_ctrl *ctrl, int argc, static int wpa_cli_cmd_preauthenticate(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - - if (argc != 1) { - printf("Invalid PREAUTH command: needs one argument " - "(BSSID)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "PREAUTH %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long PREAUTH command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "PREAUTH", 1, argc, argv); } static int wpa_cli_cmd_ap_scan(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - - if (argc != 1) { - printf("Invalid AP_SCAN command: needs one argument (ap_scan " - "value)\n"); - return -1; - } - res = os_snprintf(cmd, sizeof(cmd), "AP_SCAN %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long AP_SCAN command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "AP_SCAN", 1, argc, argv); } static int wpa_cli_cmd_scan_interval(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - - if (argc != 1) { - printf("Invalid SCAN_INTERVAL command: needs one argument " - "scan_interval value)\n"); - return -1; - } - res = os_snprintf(cmd, sizeof(cmd), "SCAN_INTERVAL %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long SCAN_INTERVAL command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "SCAN_INTERVAL", 1, argc, argv); } static int wpa_cli_cmd_bss_expire_age(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - - if (argc != 1) { - printf("Invalid BSS_EXPIRE_AGE command: needs one argument " - "(bss_expire_age value)\n"); - return -1; - } - res = os_snprintf(cmd, sizeof(cmd), "BSS_EXPIRE_AGE %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long BSS_EXPIRE_AGE command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "BSS_EXPIRE_AGE", 1, argc, argv); } static int wpa_cli_cmd_bss_expire_count(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - - if (argc != 1) { - printf("Invalid BSS_EXPIRE_COUNT command: needs one argument " - "(bss_expire_count value)\n"); - return -1; - } - res = os_snprintf(cmd, sizeof(cmd), "BSS_EXPIRE_COUNT %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long BSS_EXPIRE_COUNT command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "BSS_EXPIRE_COUNT", 1, argc, argv); } @@ -734,69 +718,24 @@ static int wpa_cli_cmd_bss_flush(struct wpa_ctrl *ctrl, int argc, char *argv[]) static int wpa_cli_cmd_stkstart(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - - if (argc != 1) { - printf("Invalid STKSTART command: needs one argument " - "(Peer STA MAC address)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "STKSTART %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long STKSTART command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "STKSTART", 1, argc, argv); } static int wpa_cli_cmd_ft_ds(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - - if (argc != 1) { - printf("Invalid FT_DS command: needs one argument " - "(Target AP MAC address)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "FT_DS %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long FT_DS command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "FT_DS", 1, argc, argv); } static int wpa_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - - if (argc == 0) { - /* Any BSSID */ - return wpa_ctrl_command(ctrl, "WPS_PBC"); - } - - /* Specific BSSID */ - res = os_snprintf(cmd, sizeof(cmd), "WPS_PBC %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long WPS_PBC command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "WPS_PBC", 0, argc, argv); } static int wpa_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - if (argc == 0) { printf("Invalid WPS_PIN command: need one or two arguments:\n" "- BSSID: use 'any' to select any\n" @@ -805,49 +744,14 @@ static int wpa_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc, char *argv[]) return -1; } - if (argc == 1) { - /* Use dynamically generated PIN (returned as reply) */ - res = os_snprintf(cmd, sizeof(cmd), "WPS_PIN %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long WPS_PIN command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); - } - - /* Use hardcoded PIN from a label */ - res = os_snprintf(cmd, sizeof(cmd), "WPS_PIN %s %s", argv[0], argv[1]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long WPS_PIN command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "WPS_PIN", 1, argc, argv); } static int wpa_cli_cmd_wps_check_pin(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - - if (argc != 1 && argc != 2) { - printf("Invalid WPS_CHECK_PIN command: needs one argument:\n" - "- PIN to be verified\n"); - return -1; - } - - if (argc == 2) - res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s %s", - argv[0], argv[1]); - else - res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s", - argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long WPS_CHECK_PIN command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "WPS_CHECK_PIN", 1, argc, argv); } @@ -861,9 +765,6 @@ static int wpa_cli_cmd_wps_cancel(struct wpa_ctrl *ctrl, int argc, #ifdef CONFIG_WPS_OOB static int wpa_cli_cmd_wps_oob(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - if (argc != 3 && argc != 4) { printf("Invalid WPS_OOB command: need three or four " "arguments:\n" @@ -876,17 +777,7 @@ static int wpa_cli_cmd_wps_oob(struct wpa_ctrl *ctrl, int argc, char *argv[]) return -1; } - if (argc == 3) - res = os_snprintf(cmd, sizeof(cmd), "WPS_OOB %s %s %s", - argv[0], argv[1], argv[2]); - else - res = os_snprintf(cmd, sizeof(cmd), "WPS_OOB %s %s %s %s", - argv[0], argv[1], argv[2], argv[3]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long WPS_OOB command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "WPS_OOB", 3, argc, argv); } #endif /* CONFIG_WPS_OOB */ @@ -895,43 +786,14 @@ static int wpa_cli_cmd_wps_oob(struct wpa_ctrl *ctrl, int argc, char *argv[]) static int wpa_cli_cmd_wps_nfc(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - - if (argc >= 1) - res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC %s", - argv[0]); - else - res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC"); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long WPS_NFC command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "WPS_NFC", 0, argc, argv); } static int wpa_cli_cmd_wps_nfc_token(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - - if (argc != 1) { - printf("Invalid WPS_NFC_TOKEN command: need one argument:\n" - "format: WPS or NDEF\n"); - return -1; - } - if (argc >= 1) - res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_TOKEN %s", - argv[0]); - else - res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_TOKEN"); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long WPS_NFC_TOKEN command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "WPS_NFC_TOKEN", 1, argc, argv); } @@ -1023,41 +885,14 @@ static int wpa_cli_cmd_wps_reg(struct wpa_ctrl *ctrl, int argc, char *argv[]) static int wpa_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - - if (argc < 1) { - printf("Invalid WPS_AP_PIN command: needs at least one " - "argument\n"); - return -1; - } - - if (argc > 2) - res = os_snprintf(cmd, sizeof(cmd), "WPS_AP_PIN %s %s %s", - argv[0], argv[1], argv[2]); - else if (argc > 1) - res = os_snprintf(cmd, sizeof(cmd), "WPS_AP_PIN %s %s", - argv[0], argv[1]); - else - res = os_snprintf(cmd, sizeof(cmd), "WPS_AP_PIN %s", - argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long WPS_AP_PIN command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "WPS_AP_PIN", 1, argc, argv); } static int wpa_cli_cmd_wps_er_start(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[100]; - if (argc > 0) { - os_snprintf(cmd, sizeof(cmd), "WPS_ER_START %s", argv[0]); - return wpa_ctrl_command(ctrl, cmd); - } - return wpa_ctrl_command(ctrl, "WPS_ER_START"); + return wpa_cli_cmd(ctrl, "WPS_ER_START", 0, argc, argv); } @@ -1072,9 +907,6 @@ static int wpa_cli_cmd_wps_er_stop(struct wpa_ctrl *ctrl, int argc, static int wpa_cli_cmd_wps_er_pin(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - if (argc < 2) { printf("Invalid WPS_ER_PIN command: need at least two " "arguments:\n" @@ -1084,48 +916,20 @@ static int wpa_cli_cmd_wps_er_pin(struct wpa_ctrl *ctrl, int argc, return -1; } - if (argc > 2) - res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s %s", - argv[0], argv[1], argv[2]); - else - res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s", - argv[0], argv[1]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long WPS_ER_PIN command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "WPS_ER_PIN", 2, argc, argv); } static int wpa_cli_cmd_wps_er_pbc(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - - if (argc != 1) { - printf("Invalid WPS_ER_PBC command: need one argument:\n" - "- UUID: Specify the Enrollee\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_PBC %s", - argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long WPS_ER_PBC command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "WPS_ER_PBC", 1, argc, argv); } static int wpa_cli_cmd_wps_er_learn(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - if (argc != 2) { printf("Invalid WPS_ER_LEARN command: need two arguments:\n" "- UUID: specify which AP to use\n" @@ -1133,22 +937,13 @@ static int wpa_cli_cmd_wps_er_learn(struct wpa_ctrl *ctrl, int argc, return -1; } - res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_LEARN %s %s", - argv[0], argv[1]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long WPS_ER_LEARN command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "WPS_ER_LEARN", 2, argc, argv); } static int wpa_cli_cmd_wps_er_set_config(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - if (argc != 2) { printf("Invalid WPS_ER_SET_CONFIG command: need two " "arguments:\n" @@ -1157,13 +952,7 @@ static int wpa_cli_cmd_wps_er_set_config(struct wpa_ctrl *ctrl, int argc, return -1; } - res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_SET_CONFIG %s %s", - argv[0], argv[1]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long WPS_ER_SET_CONFIG command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "WPS_ER_SET_CONFIG", 2, argc, argv); } @@ -1222,9 +1011,6 @@ static int wpa_cli_cmd_wps_er_config(struct wpa_ctrl *ctrl, int argc, static int wpa_cli_cmd_wps_er_nfc_config_token(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - if (argc != 2) { printf("Invalid WPS_ER_NFC_CONFIG_TOKEN command: need two " "arguments:\n" @@ -1233,53 +1019,20 @@ static int wpa_cli_cmd_wps_er_nfc_config_token(struct wpa_ctrl *ctrl, int argc, return -1; } - res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_NFC_CONFIG_TOKEN %s %s", - argv[0], argv[1]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long WPS_ER_NFC_CONFIG_TOKEN command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "WPS_ER_NFC_CONFIG_TOKEN", 2, argc, argv); } #endif /* CONFIG_WPS_NFC */ static int wpa_cli_cmd_ibss_rsn(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - - if (argc != 1) { - printf("Invalid IBSS_RSN command: needs one argument " - "(Peer STA MAC address)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "IBSS_RSN %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long IBSS_RSN command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "IBSS_RSN", 1, argc, argv); } static int wpa_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - - if (argc != 1) { - printf("Invalid LEVEL command: needs one argument (debug " - "level)\n"); - return -1; - } - res = os_snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long LEVEL command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "LEVEL", 1, argc, argv); } @@ -1484,85 +1237,25 @@ static int wpa_cli_cmd_passphrase(struct wpa_ctrl *ctrl, int argc, static int wpa_cli_cmd_bssid(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256], *pos, *end; - int i, ret; - if (argc < 2) { printf("Invalid BSSID command: needs two arguments (network " "id and BSSID)\n"); return -1; } - end = cmd + sizeof(cmd); - pos = cmd; - ret = os_snprintf(pos, end - pos, "BSSID"); - if (ret < 0 || ret >= end - pos) { - printf("Too long BSSID command.\n"); - return -1; - } - pos += ret; - for (i = 0; i < argc; i++) { - ret = os_snprintf(pos, end - pos, " %s", argv[i]); - if (ret < 0 || ret >= end - pos) { - printf("Too long BSSID command.\n"); - return -1; - } - pos += ret; - } - - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "BSSID", 2, argc, argv); } static int wpa_cli_cmd_blacklist(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256], *pos, *end; - int i, ret; - - end = cmd + sizeof(cmd); - pos = cmd; - ret = os_snprintf(pos, end - pos, "BLACKLIST"); - if (ret < 0 || ret >= end - pos) { - printf("Too long BLACKLIST command.\n"); - return -1; - } - pos += ret; - for (i = 0; i < argc; i++) { - ret = os_snprintf(pos, end - pos, " %s", argv[i]); - if (ret < 0 || ret >= end - pos) { - printf("Too long BLACKLIST command.\n"); - return -1; - } - pos += ret; - } - - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "BLACKLIST", 0, argc, argv); } static int wpa_cli_cmd_log_level(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256], *pos, *end; - int i, ret; - - end = cmd + sizeof(cmd); - pos = cmd; - ret = os_snprintf(pos, end - pos, "LOG_LEVEL"); - if (ret < 0 || ret >= end - pos) { - printf("Too long LOG_LEVEL command.\n"); - return -1; - } - pos += ret; - for (i = 0; i < argc; i++) { - ret = os_snprintf(pos, end - pos, " %s", argv[i]); - if (ret < 0 || ret >= end - pos) { - printf("Too long LOG_LEVEL command.\n"); - return -1; - } - pos += ret; - } - - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "LOG_LEVEL", 0, argc, argv); } @@ -1576,68 +1269,21 @@ static int wpa_cli_cmd_list_networks(struct wpa_ctrl *ctrl, int argc, static int wpa_cli_cmd_select_network(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[32]; - int res; - - if (argc < 1) { - printf("Invalid SELECT_NETWORK command: needs one argument " - "(network id)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "SELECT_NETWORK %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "SELECT_NETWORK", 1, argc, argv); } static int wpa_cli_cmd_enable_network(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[32]; - int res; - - if (argc < 1) { - printf("Invalid ENABLE_NETWORK command: needs one argument " - "(network id)\n"); - return -1; - } - - if (argc > 1) - res = os_snprintf(cmd, sizeof(cmd), "ENABLE_NETWORK %s %s", - argv[0], argv[1]); - else - res = os_snprintf(cmd, sizeof(cmd), "ENABLE_NETWORK %s", - argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "ENABLE_NETWORK", 1, argc, argv); } static int wpa_cli_cmd_disable_network(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[32]; - int res; - - if (argc < 1) { - printf("Invalid DISABLE_NETWORK command: needs one argument " - "(network id)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "DISABLE_NETWORK %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "DISABLE_NETWORK", 1, argc, argv); } @@ -1651,21 +1297,7 @@ static int wpa_cli_cmd_add_network(struct wpa_ctrl *ctrl, int argc, static int wpa_cli_cmd_remove_network(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[32]; - int res; - - if (argc < 1) { - printf("Invalid REMOVE_NETWORK command: needs one argument " - "(network id)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "REMOVE_NETWORK %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "REMOVE_NETWORK", 1, argc, argv); } @@ -1693,9 +1325,6 @@ static void wpa_cli_show_network_variables(void) static int wpa_cli_cmd_set_network(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - if (argc == 0) { wpa_cli_show_network_variables(); return 0; @@ -1707,22 +1336,13 @@ static int wpa_cli_cmd_set_network(struct wpa_ctrl *ctrl, int argc, return -1; } - res = os_snprintf(cmd, sizeof(cmd), "SET_NETWORK %s %s %s", - argv[0], argv[1], argv[2]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long SET_NETWORK command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "SET_NETWORK", 3, argc, argv); } static int wpa_cli_cmd_get_network(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - if (argc == 0) { wpa_cli_show_network_variables(); return 0; @@ -1734,13 +1354,7 @@ static int wpa_cli_cmd_get_network(struct wpa_ctrl *ctrl, int argc, return -1; } - res = os_snprintf(cmd, sizeof(cmd), "GET_NETWORK %s %s", - argv[0], argv[1]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long GET_NETWORK command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "GET_NETWORK", 2, argc, argv); } @@ -1760,42 +1374,19 @@ static int wpa_cli_cmd_add_cred(struct wpa_ctrl *ctrl, int argc, char *argv[]) static int wpa_cli_cmd_remove_cred(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[32]; - int res; - - if (argc < 1) { - printf("Invalid REMOVE_CRED command: needs one argument " - "(cred id)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "REMOVE_CRED %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "REMOVE_CRED", 1, argc, argv); } static int wpa_cli_cmd_set_cred(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - if (argc != 3) { printf("Invalid SET_CRED command: needs three arguments\n" "(cred id, variable name, and value)\n"); return -1; } - res = os_snprintf(cmd, sizeof(cmd), "SET_CRED %s %s %s", - argv[0], argv[1], argv[2]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long SET_CRED command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "SET_CRED", 3, argc, argv); } @@ -1835,24 +1426,7 @@ static int wpa_cli_cmd_scan_results(struct wpa_ctrl *ctrl, int argc, static int wpa_cli_cmd_bss(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[64]; - int res; - - if (argc < 1) { - printf("Invalid BSS command: need at least one argument" - "(index or BSSID)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "BSS %s%s%s%s%s", argv[0], - argc > 1 ? " " : "", argc > 1 ? argv[1] : "", - argc > 2 ? " " : "", argc > 2 ? argv[2] : ""); - - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "BSS", 1, argc, argv); } @@ -1874,9 +1448,6 @@ static char ** wpa_cli_complete_bss(const char *str, int pos) static int wpa_cli_cmd_get_capability(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[64]; - int res; - if (argc < 1 || argc > 2) { printf("Invalid GET_CAPABILITY command: need either one or " "two arguments\n"); @@ -1889,13 +1460,7 @@ static int wpa_cli_cmd_get_capability(struct wpa_ctrl *ctrl, int argc, return -1; } - res = os_snprintf(cmd, sizeof(cmd), "GET_CAPABILITY %s%s", argv[0], - (argc == 2) ? " strict" : ""); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "GET_CAPABILITY", 1, argc, argv); } @@ -1975,20 +1540,7 @@ static int wpa_cli_cmd_interface_add(struct wpa_ctrl *ctrl, int argc, static int wpa_cli_cmd_interface_remove(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[128]; - int res; - - if (argc != 1) { - printf("Invalid INTERFACE_REMOVE command: needs one argument " - "(interface name)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "INTERFACE_REMOVE %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "INTERFACE_REMOVE", 1, argc, argv); } @@ -2002,14 +1554,7 @@ static int wpa_cli_cmd_interface_list(struct wpa_ctrl *ctrl, int argc, #ifdef CONFIG_AP static int wpa_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char buf[64]; - if (argc != 1) { - printf("Invalid 'sta' command - exactly one argument, STA " - "address, is required.\n"); - return -1; - } - os_snprintf(buf, sizeof(buf), "STA %s", argv[0]); - return wpa_ctrl_command(ctrl, buf); + return wpa_cli_cmd(ctrl, "STA", 1, argc, argv); } @@ -2066,36 +1611,14 @@ static int wpa_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc, char *argv[]) static int wpa_cli_cmd_deauthenticate(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char buf[64]; - if (argc < 1) { - printf("Invalid 'deauthenticate' command - exactly one " - "argument, STA address, is required.\n"); - return -1; - } - if (argc > 1) - os_snprintf(buf, sizeof(buf), "DEAUTHENTICATE %s %s", - argv[0], argv[1]); - else - os_snprintf(buf, sizeof(buf), "DEAUTHENTICATE %s", argv[0]); - return wpa_ctrl_command(ctrl, buf); + return wpa_cli_cmd(ctrl, "DEAUTHENTICATE", 1, argc, argv); } static int wpa_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char buf[64]; - if (argc < 1) { - printf("Invalid 'disassociate' command - exactly one " - "argument, STA address, is required.\n"); - return -1; - } - if (argc > 1) - os_snprintf(buf, sizeof(buf), "DISASSOCIATE %s %s", - argv[0], argv[1]); - else - os_snprintf(buf, sizeof(buf), "DISASSOCIATE %s", argv[0]); - return wpa_ctrl_command(ctrl, buf); + return wpa_cli_cmd(ctrl, "DISASSOCIATE", 1, argc, argv); } #endif /* CONFIG_AP */ @@ -2120,21 +1643,7 @@ static int wpa_cli_cmd_drop_sa(struct wpa_ctrl *ctrl, int argc, char *argv[]) static int wpa_cli_cmd_roam(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[128]; - int res; - - if (argc != 1) { - printf("Invalid ROAM command: needs one argument " - "(target AP's BSSID)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "ROAM %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long ROAM command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "ROAM", 1, argc, argv); } @@ -2142,24 +1651,36 @@ static int wpa_cli_cmd_roam(struct wpa_ctrl *ctrl, int argc, char *argv[]) static int wpa_cli_cmd_p2p_find(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[128]; - int res; + return wpa_cli_cmd(ctrl, "P2P_FIND", 0, argc, argv); +} - if (argc == 0) - return wpa_ctrl_command(ctrl, "P2P_FIND"); - if (argc > 2) - res = os_snprintf(cmd, sizeof(cmd), "P2P_FIND %s %s %s", - argv[0], argv[1], argv[2]); - else if (argc > 1) - res = os_snprintf(cmd, sizeof(cmd), "P2P_FIND %s %s", - argv[0], argv[1]); - else - res = os_snprintf(cmd, sizeof(cmd), "P2P_FIND %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - return wpa_ctrl_command(ctrl, cmd); +static char ** wpa_cli_complete_p2p_find(const char *str, int pos) +{ + char **res = NULL; + int arg = get_cmd_arg_num(str, pos); + + res = os_calloc(6, sizeof(char *)); + if (res == NULL) + return NULL; + res[0] = os_strdup("type=social"); + if (res[0] == NULL) { + os_free(res); + return NULL; + } + res[1] = os_strdup("type=progressive"); + if (res[1] == NULL) + return res; + res[2] = os_strdup("delay="); + if (res[2] == NULL) + return res; + res[3] = os_strdup("dev_id="); + if (res[3] == NULL) + return res; + if (arg == 1) + res[4] = os_strdup("[timeout]"); + + return res; } @@ -2173,40 +1694,7 @@ static int wpa_cli_cmd_p2p_stop_find(struct wpa_ctrl *ctrl, int argc, static int wpa_cli_cmd_p2p_connect(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[128]; - int res; - - if (argc < 2) { - printf("Invalid P2P_CONNECT command: needs at least two " - "arguments (address and pbc/PIN)\n"); - return -1; - } -#ifdef ANDROID_P2P - if (argc > 5) - res = os_snprintf(cmd, sizeof(cmd), - "P2P_CONNECT %s %s %s %s %s %s", - argv[0], argv[1], argv[2], argv[3], - argv[4], argv[5]); - else -#endif - if (argc > 4) - res = os_snprintf(cmd, sizeof(cmd), - "P2P_CONNECT %s %s %s %s %s", - argv[0], argv[1], argv[2], argv[3], - argv[4]); - else if (argc > 3) - res = os_snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s %s %s", - argv[0], argv[1], argv[2], argv[3]); - else if (argc > 2) - res = os_snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s %s", - argv[0], argv[1], argv[2]); - else - res = os_snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s", - argv[0], argv[1]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "P2P_CONNECT", 2, argc, argv); } @@ -2228,37 +1716,14 @@ static char ** wpa_cli_complete_p2p_connect(const char *str, int pos) static int wpa_cli_cmd_p2p_listen(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[128]; - int res; - - if (argc == 0) - return wpa_ctrl_command(ctrl, "P2P_LISTEN"); - - res = os_snprintf(cmd, sizeof(cmd), "P2P_LISTEN %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "P2P_LISTEN", 0, argc, argv); } static int wpa_cli_cmd_p2p_group_remove(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[128]; - int res; - - if (argc != 1) { - printf("Invalid P2P_GROUP_REMOVE command: needs one argument " - "(interface name)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "P2P_GROUP_REMOVE %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "P2P_GROUP_REMOVE", 1, argc, argv); } @@ -2280,31 +1745,13 @@ static char ** wpa_cli_complete_p2p_group_remove(const char *str, int pos) static int wpa_cli_cmd_p2p_group_add(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[128]; - int res; - - if (argc == 0) - return wpa_ctrl_command(ctrl, "P2P_GROUP_ADD"); - - if (argc > 1) - res = os_snprintf(cmd, sizeof(cmd), "P2P_GROUP_ADD %s %s", - argv[0], argv[1]); - else - res = os_snprintf(cmd, sizeof(cmd), "P2P_GROUP_ADD %s", - argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "P2P_GROUP_ADD", 0, argc, argv); } static int wpa_cli_cmd_p2p_prov_disc(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[128]; - int res; - if (argc != 2 && argc != 3) { printf("Invalid P2P_PROV_DISC command: needs at least " "two arguments, address and config method\n" @@ -2312,16 +1759,7 @@ static int wpa_cli_cmd_p2p_prov_disc(struct wpa_ctrl *ctrl, int argc, return -1; } - if (argc == 3) - res = os_snprintf(cmd, sizeof(cmd), "P2P_PROV_DISC %s %s %s", - argv[0], argv[1], argv[2]); - else - res = os_snprintf(cmd, sizeof(cmd), "P2P_PROV_DISC %s %s", - argv[0], argv[1]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "P2P_PROV_DISC", 2, argc, argv); } @@ -2336,7 +1774,6 @@ static int wpa_cli_cmd_p2p_serv_disc_req(struct wpa_ctrl *ctrl, int argc, char *argv[]) { char cmd[4096]; - int res; if (argc != 2 && argc != 4) { printf("Invalid P2P_SERV_DISC_REQ command: needs two " @@ -2346,16 +1783,8 @@ static int wpa_cli_cmd_p2p_serv_disc_req(struct wpa_ctrl *ctrl, int argc, return -1; } - if (argc == 4) - res = os_snprintf(cmd, sizeof(cmd), - "P2P_SERV_DISC_REQ %s %s %s %s", - argv[0], argv[1], argv[2], argv[3]); - else - res = os_snprintf(cmd, sizeof(cmd), "P2P_SERV_DISC_REQ %s %s", - argv[0], argv[1]); - if (res < 0 || (size_t) res >= sizeof(cmd)) + if (write_cmd(cmd, sizeof(cmd), "P2P_SERV_DISC_REQ", argc, argv) < 0) return -1; - cmd[sizeof(cmd) - 1] = '\0'; return wpa_ctrl_command(ctrl, cmd); } @@ -2363,21 +1792,7 @@ static int wpa_cli_cmd_p2p_serv_disc_req(struct wpa_ctrl *ctrl, int argc, static int wpa_cli_cmd_p2p_serv_disc_cancel_req(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[128]; - int res; - - if (argc != 1) { - printf("Invalid P2P_SERV_DISC_CANCEL_REQ command: needs one " - "argument (pending request identifier)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "P2P_SERV_DISC_CANCEL_REQ %s", - argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "P2P_SERV_DISC_CANCEL_REQ", 1, argc, argv); } @@ -2412,21 +1827,7 @@ static int wpa_cli_cmd_p2p_service_update(struct wpa_ctrl *ctrl, int argc, static int wpa_cli_cmd_p2p_serv_disc_external(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[128]; - int res; - - if (argc != 1) { - printf("Invalid P2P_SERV_DISC_EXTERNAL command: needs one " - "argument (external processing: 0/1)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "P2P_SERV_DISC_EXTERNAL %s", - argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "P2P_SERV_DISC_EXTERNAL", 1, argc, argv); } @@ -2494,60 +1895,20 @@ static int wpa_cli_cmd_p2p_service_del(struct wpa_ctrl *ctrl, int argc, static int wpa_cli_cmd_p2p_reject(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[128]; - int res; - - if (argc != 1) { - printf("Invalid P2P_REJECT command: needs one argument " - "(peer address)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "P2P_REJECT %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "P2P_REJECT", 1, argc, argv); } static int wpa_cli_cmd_p2p_invite(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[128]; - int res; - - if (argc < 1) { - printf("Invalid P2P_INVITE command: needs at least one " - "argument\n"); - return -1; - } - - if (argc > 2) - res = os_snprintf(cmd, sizeof(cmd), "P2P_INVITE %s %s %s", - argv[0], argv[1], argv[2]); - else if (argc > 1) - res = os_snprintf(cmd, sizeof(cmd), "P2P_INVITE %s %s", - argv[0], argv[1]); - else - res = os_snprintf(cmd, sizeof(cmd), "P2P_INVITE %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "P2P_INVITE", 1, argc, argv); } static int wpa_cli_cmd_p2p_peer(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char buf[64]; - if (argc != 1) { - printf("Invalid 'p2p_peer' command - exactly one argument, " - "P2P peer device address, is required.\n"); - return -1; - } - os_snprintf(buf, sizeof(buf), "P2P_PEER %s", argv[0]); - return wpa_ctrl_command(ctrl, buf); + return wpa_cli_cmd(ctrl, "P2P_PEER", 1, argc, argv); } @@ -2623,20 +1984,7 @@ static int wpa_cli_cmd_p2p_peers(struct wpa_ctrl *ctrl, int argc, char *argv[]) static int wpa_cli_cmd_p2p_set(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[100]; - int res; - - if (argc != 2) { - printf("Invalid P2P_SET command: needs two arguments (field, " - "value)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "P2P_SET %s %s", argv[0], argv[1]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "P2P_SET", 2, argc, argv); } @@ -2656,48 +2004,58 @@ static int wpa_cli_cmd_p2p_cancel(struct wpa_ctrl *ctrl, int argc, static int wpa_cli_cmd_p2p_unauthorize(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[100]; - int res; + return wpa_cli_cmd(ctrl, "P2P_UNAUTHORIZE", 1, argc, argv); +} - if (argc != 1) { - printf("Invalid P2P_UNAUTHORIZE command: needs one argument " - "(peer address)\n"); + +static int wpa_cli_cmd_p2p_presence_req(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + if (argc != 0 && argc != 2 && argc != 4) { + printf("Invalid P2P_PRESENCE_REQ command: needs two arguments " + "(preferred duration, interval; in microsecods).\n" + "Optional second pair can be used to provide " + "acceptable values.\n"); return -1; } - res = os_snprintf(cmd, sizeof(cmd), "P2P_UNAUTHORIZE %s", argv[0]); + return wpa_cli_cmd(ctrl, "P2P_PRESENCE_REQ", 0, argc, argv); +} - if (res < 0 || (size_t) res >= sizeof(cmd)) + +static int wpa_cli_cmd_p2p_ext_listen(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + if (argc != 0 && argc != 2) { + printf("Invalid P2P_EXT_LISTEN command: needs two arguments " + "(availability period, availability interval; in " + "millisecods).\n" + "Extended Listen Timing can be cancelled with this " + "command when used without parameters.\n"); return -1; + } - cmd[sizeof(cmd) - 1] = '\0'; - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "P2P_EXT_LISTEN", 0, argc, argv); } +#endif /* CONFIG_P2P */ -static int wpa_cli_cmd_p2p_presence_req(struct wpa_ctrl *ctrl, int argc, - char *argv[]) +#ifdef CONFIG_WIFI_DISPLAY + +static int wpa_cli_cmd_wfd_subelem_set(struct wpa_ctrl *ctrl, int argc, + char *argv[]) { char cmd[100]; int res; - if (argc != 0 && argc != 2 && argc != 4) { - printf("Invalid P2P_PRESENCE_REQ command: needs two arguments " - "(preferred duration, interval; in microsecods).\n" - "Optional second pair can be used to provide " - "acceptable values.\n"); + if (argc != 1 && argc != 2) { + printf("Invalid WFD_SUBELEM_SET command: needs one or two " + "arguments (subelem, hexdump)\n"); return -1; } - if (argc == 4) - res = os_snprintf(cmd, sizeof(cmd), - "P2P_PRESENCE_REQ %s %s %s %s", - argv[0], argv[1], argv[2], argv[3]); - else if (argc == 2) - res = os_snprintf(cmd, sizeof(cmd), "P2P_PRESENCE_REQ %s %s", - argv[0], argv[1]); - else - res = os_snprintf(cmd, sizeof(cmd), "P2P_PRESENCE_REQ"); + res = os_snprintf(cmd, sizeof(cmd), "WFD_SUBELEM_SET %s %s", + argv[0], argc > 1 ? argv[1] : ""); if (res < 0 || (size_t) res >= sizeof(cmd)) return -1; cmd[sizeof(cmd) - 1] = '\0'; @@ -2705,33 +2063,26 @@ static int wpa_cli_cmd_p2p_presence_req(struct wpa_ctrl *ctrl, int argc, } -static int wpa_cli_cmd_p2p_ext_listen(struct wpa_ctrl *ctrl, int argc, - char *argv[]) +static int wpa_cli_cmd_wfd_subelem_get(struct wpa_ctrl *ctrl, int argc, + char *argv[]) { char cmd[100]; int res; - if (argc != 0 && argc != 2) { - printf("Invalid P2P_EXT_LISTEN command: needs two arguments " - "(availability period, availability interval; in " - "millisecods).\n" - "Extended Listen Timing can be cancelled with this " - "command when used without parameters.\n"); + if (argc != 1) { + printf("Invalid WFD_SUBELEM_GET command: needs one " + "argument (subelem)\n"); return -1; } - if (argc == 2) - res = os_snprintf(cmd, sizeof(cmd), "P2P_EXT_LISTEN %s %s", - argv[0], argv[1]); - else - res = os_snprintf(cmd, sizeof(cmd), "P2P_EXT_LISTEN"); + res = os_snprintf(cmd, sizeof(cmd), "WFD_SUBELEM_GET %s", + argv[0]); if (res < 0 || (size_t) res >= sizeof(cmd)) return -1; cmd[sizeof(cmd) - 1] = '\0'; return wpa_ctrl_command(ctrl, cmd); } - -#endif /* CONFIG_P2P */ +#endif /* CONFIG_WIFI_DISPLAY */ #ifdef CONFIG_INTERWORKING @@ -2752,58 +2103,34 @@ static int wpa_cli_cmd_stop_fetch_anqp(struct wpa_ctrl *ctrl, int argc, static int wpa_cli_cmd_interworking_select(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[100]; - int res; - - if (argc == 0) - return wpa_ctrl_command(ctrl, "INTERWORKING_SELECT"); - - res = os_snprintf(cmd, sizeof(cmd), "INTERWORKING_SELECT %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "INTERWORKING_SELECT", 0, argc, argv); } static int wpa_cli_cmd_interworking_connect(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[100]; - int res; + return wpa_cli_cmd(ctrl, "INTERWORKING_CONNECT", 1, argc, argv); +} - if (argc != 1) { - printf("Invalid INTERWORKING_CONNECT commands: needs one " - "argument (BSSID)\n"); - return -1; - } - res = os_snprintf(cmd, sizeof(cmd), "INTERWORKING_CONNECT %s", - argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - return wpa_ctrl_command(ctrl, cmd); +static int wpa_cli_cmd_anqp_get(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_cli_cmd(ctrl, "ANQP_GET", 2, argc, argv); } -static int wpa_cli_cmd_anqp_get(struct wpa_ctrl *ctrl, int argc, char *argv[]) +static int wpa_cli_cmd_gas_request(struct wpa_ctrl *ctrl, int argc, + char *argv[]) { - char cmd[100]; - int res; + return wpa_cli_cmd(ctrl, "GAS_REQUEST", 2, argc, argv); +} - if (argc != 2) { - printf("Invalid ANQP_GET command: needs two arguments " - "(addr and info id list)\n"); - return -1; - } - res = os_snprintf(cmd, sizeof(cmd), "ANQP_GET %s %s", - argv[0], argv[1]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - return wpa_ctrl_command(ctrl, cmd); +static int wpa_cli_cmd_gas_response_get(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "GAS_RESPONSE_GET", 2, argc, argv); } #endif /* CONFIG_INTERWORKING */ @@ -2813,21 +2140,7 @@ static int wpa_cli_cmd_anqp_get(struct wpa_ctrl *ctrl, int argc, char *argv[]) static int wpa_cli_cmd_hs20_anqp_get(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[100]; - int res; - - if (argc != 2) { - printf("Invalid HS20_ANQP_GET command: needs two arguments " - "(addr and subtype list)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "HS20_ANQP_GET %s %s", - argv[0], argv[1]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "HS20_ANQP_GET", 2, argc, argv); } @@ -2835,7 +2148,6 @@ static int wpa_cli_cmd_get_nai_home_realm_list(struct wpa_ctrl *ctrl, int argc, char *argv[]) { char cmd[512]; - int res; if (argc == 0) { printf("Command needs one or two arguments (dst mac addr and " @@ -2843,18 +2155,9 @@ static int wpa_cli_cmd_get_nai_home_realm_list(struct wpa_ctrl *ctrl, int argc, return -1; } - if (argc == 1) - res = os_snprintf(cmd, sizeof(cmd), - "HS20_GET_NAI_HOME_REALM_LIST %s", - argv[0]); - else - res = os_snprintf(cmd, sizeof(cmd), - "HS20_GET_NAI_HOME_REALM_LIST %s %s", - argv[0], argv[1]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long command.\n"); + if (write_cmd(cmd, sizeof(cmd), "HS20_GET_NAI_HOME_REALM_LIST", + argc, argv) < 0) return -1; - } return wpa_ctrl_command(ctrl, cmd); } @@ -2865,83 +2168,28 @@ static int wpa_cli_cmd_get_nai_home_realm_list(struct wpa_ctrl *ctrl, int argc, static int wpa_cli_cmd_sta_autoconnect(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - - if (argc != 1) { - printf("Invalid STA_AUTOCONNECT command: needs one argument " - "(0/1 = disable/enable automatic reconnection)\n"); - return -1; - } - res = os_snprintf(cmd, sizeof(cmd), "STA_AUTOCONNECT %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long STA_AUTOCONNECT command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "STA_AUTOCONNECT", 1, argc, argv); } static int wpa_cli_cmd_tdls_discover(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - - if (argc != 1) { - printf("Invalid TDLS_DISCOVER command: needs one argument " - "(Peer STA MAC address)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "TDLS_DISCOVER %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long TDLS_DISCOVER command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "TDLS_DISCOVER", 1, argc, argv); } static int wpa_cli_cmd_tdls_setup(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - - if (argc != 1) { - printf("Invalid TDLS_SETUP command: needs one argument " - "(Peer STA MAC address)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "TDLS_SETUP %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long TDLS_SETUP command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "TDLS_SETUP", 1, argc, argv); } static int wpa_cli_cmd_tdls_teardown(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - - if (argc != 1) { - printf("Invalid TDLS_TEARDOWN command: needs one argument " - "(Peer STA MAC address)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "TDLS_TEARDOWN %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long TDLS_TEARDOWN command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "TDLS_TEARDOWN", 1, argc, argv); } @@ -2970,24 +2218,23 @@ static int wpa_cli_cmd_reauthenticate(struct wpa_ctrl *ctrl, int argc, static int wpa_cli_cmd_autoscan(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - if (argc == 0) return wpa_ctrl_command(ctrl, "AUTOSCAN "); - res = os_snprintf(cmd, sizeof(cmd), "AUTOSCAN %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long AUTOSCAN command.\n"); - return -1; - } - - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "AUTOSCAN", 0, argc, argv); } #endif /* CONFIG_AUTOSCAN */ +static int wpa_cli_cmd_raw(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + if (argc == 0) + return -1; + return wpa_cli_cmd(ctrl, argv[0], 0, argc - 1, &argv[1]); +} + + #ifdef ANDROID static int wpa_cli_cmd_driver(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -3009,6 +2256,7 @@ static int wpa_cli_cmd_driver(struct wpa_ctrl *ctrl, int argc, char *argv[]) } #endif + enum wpa_cli_cmd_flags { cli_cmd_flag_none = 0x00, cli_cmd_flag_sensitive = 0x01 @@ -3017,401 +2265,433 @@ enum wpa_cli_cmd_flags { struct wpa_cli_cmd { const char *cmd; int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]); + char ** (*completion)(const char *str, int pos); enum wpa_cli_cmd_flags flags; const char *usage; }; static struct wpa_cli_cmd wpa_cli_commands[] = { - { "status", wpa_cli_cmd_status, + { "status", wpa_cli_cmd_status, NULL, cli_cmd_flag_none, "[verbose] = get current WPA/EAPOL/EAP status" }, - { "ping", wpa_cli_cmd_ping, + { "ifname", wpa_cli_cmd_ifname, NULL, + cli_cmd_flag_none, + "= get current interface name" }, + { "ping", wpa_cli_cmd_ping, NULL, cli_cmd_flag_none, "= pings wpa_supplicant" }, - { "relog", wpa_cli_cmd_relog, + { "relog", wpa_cli_cmd_relog, NULL, cli_cmd_flag_none, "= re-open log-file (allow rolling logs)" }, - { "note", wpa_cli_cmd_note, + { "note", wpa_cli_cmd_note, NULL, cli_cmd_flag_none, "<text> = add a note to wpa_supplicant debug log" }, - { "mib", wpa_cli_cmd_mib, + { "mib", wpa_cli_cmd_mib, NULL, cli_cmd_flag_none, "= get MIB variables (dot1x, dot11)" }, - { "help", wpa_cli_cmd_help, + { "help", wpa_cli_cmd_help, wpa_cli_complete_help, cli_cmd_flag_none, - "= show this usage help" }, - { "interface", wpa_cli_cmd_interface, + "[command] = show usage help" }, + { "interface", wpa_cli_cmd_interface, NULL, cli_cmd_flag_none, "[ifname] = show interfaces/select interface" }, - { "level", wpa_cli_cmd_level, + { "level", wpa_cli_cmd_level, NULL, cli_cmd_flag_none, "<debug level> = change debug level" }, - { "license", wpa_cli_cmd_license, + { "license", wpa_cli_cmd_license, NULL, cli_cmd_flag_none, "= show full wpa_cli license" }, - { "quit", wpa_cli_cmd_quit, + { "quit", wpa_cli_cmd_quit, NULL, cli_cmd_flag_none, "= exit wpa_cli" }, - { "set", wpa_cli_cmd_set, + { "set", wpa_cli_cmd_set, NULL, cli_cmd_flag_none, "= set variables (shows list of variables when run without " "arguments)" }, - { "get", wpa_cli_cmd_get, + { "get", wpa_cli_cmd_get, NULL, cli_cmd_flag_none, "<name> = get information" }, - { "logon", wpa_cli_cmd_logon, + { "logon", wpa_cli_cmd_logon, NULL, cli_cmd_flag_none, "= IEEE 802.1X EAPOL state machine logon" }, - { "logoff", wpa_cli_cmd_logoff, + { "logoff", wpa_cli_cmd_logoff, NULL, cli_cmd_flag_none, "= IEEE 802.1X EAPOL state machine logoff" }, - { "pmksa", wpa_cli_cmd_pmksa, + { "pmksa", wpa_cli_cmd_pmksa, NULL, cli_cmd_flag_none, "= show PMKSA cache" }, - { "reassociate", wpa_cli_cmd_reassociate, + { "reassociate", wpa_cli_cmd_reassociate, NULL, cli_cmd_flag_none, "= force reassociation" }, - { "preauthenticate", wpa_cli_cmd_preauthenticate, + { "preauthenticate", wpa_cli_cmd_preauthenticate, wpa_cli_complete_bss, cli_cmd_flag_none, "<BSSID> = force preauthentication" }, - { "identity", wpa_cli_cmd_identity, + { "identity", wpa_cli_cmd_identity, NULL, cli_cmd_flag_none, "<network id> <identity> = configure identity for an SSID" }, - { "password", wpa_cli_cmd_password, + { "password", wpa_cli_cmd_password, NULL, cli_cmd_flag_sensitive, "<network id> <password> = configure password for an SSID" }, - { "new_password", wpa_cli_cmd_new_password, + { "new_password", wpa_cli_cmd_new_password, NULL, cli_cmd_flag_sensitive, "<network id> <password> = change password for an SSID" }, - { "pin", wpa_cli_cmd_pin, + { "pin", wpa_cli_cmd_pin, NULL, cli_cmd_flag_sensitive, "<network id> <pin> = configure pin for an SSID" }, - { "otp", wpa_cli_cmd_otp, + { "otp", wpa_cli_cmd_otp, NULL, cli_cmd_flag_sensitive, "<network id> <password> = configure one-time-password for an SSID" }, - { "passphrase", wpa_cli_cmd_passphrase, + { "passphrase", wpa_cli_cmd_passphrase, NULL, cli_cmd_flag_sensitive, "<network id> <passphrase> = configure private key passphrase\n" " for an SSID" }, - { "bssid", wpa_cli_cmd_bssid, + { "bssid", wpa_cli_cmd_bssid, NULL, cli_cmd_flag_none, "<network id> <BSSID> = set preferred BSSID for an SSID" }, - { "blacklist", wpa_cli_cmd_blacklist, + { "blacklist", wpa_cli_cmd_blacklist, wpa_cli_complete_bss, cli_cmd_flag_none, "<BSSID> = add a BSSID to the blacklist\n" "blacklist clear = clear the blacklist\n" "blacklist = display the blacklist" }, - { "log_level", wpa_cli_cmd_log_level, + { "log_level", wpa_cli_cmd_log_level, NULL, cli_cmd_flag_none, "<level> [<timestamp>] = update the log level/timestamp\n" "log_level = display the current log level and log options" }, - { "list_networks", wpa_cli_cmd_list_networks, + { "list_networks", wpa_cli_cmd_list_networks, NULL, cli_cmd_flag_none, "= list configured networks" }, - { "select_network", wpa_cli_cmd_select_network, + { "select_network", wpa_cli_cmd_select_network, NULL, cli_cmd_flag_none, "<network id> = select a network (disable others)" }, - { "enable_network", wpa_cli_cmd_enable_network, + { "enable_network", wpa_cli_cmd_enable_network, NULL, cli_cmd_flag_none, "<network id> = enable a network" }, - { "disable_network", wpa_cli_cmd_disable_network, + { "disable_network", wpa_cli_cmd_disable_network, NULL, cli_cmd_flag_none, "<network id> = disable a network" }, - { "add_network", wpa_cli_cmd_add_network, + { "add_network", wpa_cli_cmd_add_network, NULL, cli_cmd_flag_none, "= add a network" }, - { "remove_network", wpa_cli_cmd_remove_network, + { "remove_network", wpa_cli_cmd_remove_network, NULL, cli_cmd_flag_none, "<network id> = remove a network" }, - { "set_network", wpa_cli_cmd_set_network, + { "set_network", wpa_cli_cmd_set_network, NULL, cli_cmd_flag_sensitive, "<network id> <variable> <value> = set network variables (shows\n" " list of variables when run without arguments)" }, - { "get_network", wpa_cli_cmd_get_network, + { "get_network", wpa_cli_cmd_get_network, NULL, cli_cmd_flag_none, "<network id> <variable> = get network variables" }, - { "list_creds", wpa_cli_cmd_list_creds, + { "list_creds", wpa_cli_cmd_list_creds, NULL, cli_cmd_flag_none, "= list configured credentials" }, - { "add_cred", wpa_cli_cmd_add_cred, + { "add_cred", wpa_cli_cmd_add_cred, NULL, cli_cmd_flag_none, "= add a credential" }, - { "remove_cred", wpa_cli_cmd_remove_cred, + { "remove_cred", wpa_cli_cmd_remove_cred, NULL, cli_cmd_flag_none, "<cred id> = remove a credential" }, - { "set_cred", wpa_cli_cmd_set_cred, + { "set_cred", wpa_cli_cmd_set_cred, NULL, cli_cmd_flag_sensitive, "<cred id> <variable> <value> = set credential variables" }, - { "save_config", wpa_cli_cmd_save_config, + { "save_config", wpa_cli_cmd_save_config, NULL, cli_cmd_flag_none, "= save the current configuration" }, - { "disconnect", wpa_cli_cmd_disconnect, + { "disconnect", wpa_cli_cmd_disconnect, NULL, cli_cmd_flag_none, "= disconnect and wait for reassociate/reconnect command before\n" " connecting" }, - { "reconnect", wpa_cli_cmd_reconnect, + { "reconnect", wpa_cli_cmd_reconnect, NULL, cli_cmd_flag_none, "= like reassociate, but only takes effect if already disconnected" }, - { "scan", wpa_cli_cmd_scan, + { "scan", wpa_cli_cmd_scan, NULL, cli_cmd_flag_none, "= request new BSS scan" }, - { "scan_results", wpa_cli_cmd_scan_results, + { "scan_results", wpa_cli_cmd_scan_results, NULL, cli_cmd_flag_none, "= get latest scan results" }, - { "bss", wpa_cli_cmd_bss, + { "bss", wpa_cli_cmd_bss, wpa_cli_complete_bss, cli_cmd_flag_none, "<<idx> | <bssid>> = get detailed scan result info" }, - { "get_capability", wpa_cli_cmd_get_capability, + { "get_capability", wpa_cli_cmd_get_capability, NULL, cli_cmd_flag_none, - "<eap/pairwise/group/key_mgmt/proto/auth_alg/channels>\n" - " = get capabilities" }, - { "reconfigure", wpa_cli_cmd_reconfigure, + "<eap/pairwise/group/key_mgmt/proto/auth_alg/channels> " + "= get capabilies" }, + { "reconfigure", wpa_cli_cmd_reconfigure, NULL, cli_cmd_flag_none, "= force wpa_supplicant to re-read its configuration file" }, - { "terminate", wpa_cli_cmd_terminate, + { "terminate", wpa_cli_cmd_terminate, NULL, cli_cmd_flag_none, "= terminate wpa_supplicant" }, - { "interface_add", wpa_cli_cmd_interface_add, + { "interface_add", wpa_cli_cmd_interface_add, NULL, cli_cmd_flag_none, "<ifname> <confname> <driver> <ctrl_interface> <driver_param>\n" " <bridge_name> = adds new interface, all parameters but <ifname>\n" " are optional" }, - { "interface_remove", wpa_cli_cmd_interface_remove, + { "interface_remove", wpa_cli_cmd_interface_remove, NULL, cli_cmd_flag_none, "<ifname> = removes the interface" }, - { "interface_list", wpa_cli_cmd_interface_list, + { "interface_list", wpa_cli_cmd_interface_list, NULL, cli_cmd_flag_none, "= list available interfaces" }, - { "ap_scan", wpa_cli_cmd_ap_scan, + { "ap_scan", wpa_cli_cmd_ap_scan, NULL, cli_cmd_flag_none, "<value> = set ap_scan parameter" }, - { "scan_interval", wpa_cli_cmd_scan_interval, + { "scan_interval", wpa_cli_cmd_scan_interval, NULL, cli_cmd_flag_none, "<value> = set scan_interval parameter (in seconds)" }, - { "bss_expire_age", wpa_cli_cmd_bss_expire_age, + { "bss_expire_age", wpa_cli_cmd_bss_expire_age, NULL, cli_cmd_flag_none, "<value> = set BSS expiration age parameter" }, - { "bss_expire_count", wpa_cli_cmd_bss_expire_count, + { "bss_expire_count", wpa_cli_cmd_bss_expire_count, NULL, cli_cmd_flag_none, "<value> = set BSS expiration scan count parameter" }, - { "bss_flush", wpa_cli_cmd_bss_flush, + { "bss_flush", wpa_cli_cmd_bss_flush, NULL, cli_cmd_flag_none, "<value> = set BSS flush age (0 by default)" }, - { "stkstart", wpa_cli_cmd_stkstart, + { "stkstart", wpa_cli_cmd_stkstart, NULL, cli_cmd_flag_none, "<addr> = request STK negotiation with <addr>" }, - { "ft_ds", wpa_cli_cmd_ft_ds, + { "ft_ds", wpa_cli_cmd_ft_ds, wpa_cli_complete_bss, cli_cmd_flag_none, "<addr> = request over-the-DS FT with <addr>" }, - { "wps_pbc", wpa_cli_cmd_wps_pbc, + { "wps_pbc", wpa_cli_cmd_wps_pbc, wpa_cli_complete_bss, cli_cmd_flag_none, "[BSSID] = start Wi-Fi Protected Setup: Push Button Configuration" }, - { "wps_pin", wpa_cli_cmd_wps_pin, + { "wps_pin", wpa_cli_cmd_wps_pin, wpa_cli_complete_bss, cli_cmd_flag_sensitive, "<BSSID> [PIN] = start WPS PIN method (returns PIN, if not " "hardcoded)" }, - { "wps_check_pin", wpa_cli_cmd_wps_check_pin, + { "wps_check_pin", wpa_cli_cmd_wps_check_pin, NULL, cli_cmd_flag_sensitive, "<PIN> = verify PIN checksum" }, - { "wps_cancel", wpa_cli_cmd_wps_cancel, cli_cmd_flag_none, + { "wps_cancel", wpa_cli_cmd_wps_cancel, NULL, cli_cmd_flag_none, "Cancels the pending WPS operation" }, #ifdef CONFIG_WPS_OOB - { "wps_oob", wpa_cli_cmd_wps_oob, + { "wps_oob", wpa_cli_cmd_wps_oob, NULL, cli_cmd_flag_sensitive, "<DEV_TYPE> <PATH> <METHOD> [DEV_NAME] = start WPS OOB" }, #endif /* CONFIG_WPS_OOB */ #ifdef CONFIG_WPS_NFC - { "wps_nfc", wpa_cli_cmd_wps_nfc, + { "wps_nfc", wpa_cli_cmd_wps_nfc, wpa_cli_complete_bss, cli_cmd_flag_none, "[BSSID] = start Wi-Fi Protected Setup: NFC" }, - { "wps_nfc_token", wpa_cli_cmd_wps_nfc_token, + { "wps_nfc_token", wpa_cli_cmd_wps_nfc_token, NULL, cli_cmd_flag_none, "<WPS|NDEF> = create password token" }, - { "wps_nfc_tag_read", wpa_cli_cmd_wps_nfc_tag_read, + { "wps_nfc_tag_read", wpa_cli_cmd_wps_nfc_tag_read, NULL, cli_cmd_flag_sensitive, "<hexdump of payload> = report read NFC tag with WPS data" }, #endif /* CONFIG_WPS_NFC */ - { "wps_reg", wpa_cli_cmd_wps_reg, + { "wps_reg", wpa_cli_cmd_wps_reg, wpa_cli_complete_bss, cli_cmd_flag_sensitive, "<BSSID> <AP PIN> = start WPS Registrar to configure an AP" }, - { "wps_ap_pin", wpa_cli_cmd_wps_ap_pin, + { "wps_ap_pin", wpa_cli_cmd_wps_ap_pin, NULL, cli_cmd_flag_sensitive, "[params..] = enable/disable AP PIN" }, - { "wps_er_start", wpa_cli_cmd_wps_er_start, + { "wps_er_start", wpa_cli_cmd_wps_er_start, NULL, cli_cmd_flag_none, "[IP address] = start Wi-Fi Protected Setup External Registrar" }, - { "wps_er_stop", wpa_cli_cmd_wps_er_stop, + { "wps_er_stop", wpa_cli_cmd_wps_er_stop, NULL, cli_cmd_flag_none, "= stop Wi-Fi Protected Setup External Registrar" }, - { "wps_er_pin", wpa_cli_cmd_wps_er_pin, + { "wps_er_pin", wpa_cli_cmd_wps_er_pin, NULL, cli_cmd_flag_sensitive, "<UUID> <PIN> = add an Enrollee PIN to External Registrar" }, - { "wps_er_pbc", wpa_cli_cmd_wps_er_pbc, + { "wps_er_pbc", wpa_cli_cmd_wps_er_pbc, NULL, cli_cmd_flag_none, "<UUID> = accept an Enrollee PBC using External Registrar" }, - { "wps_er_learn", wpa_cli_cmd_wps_er_learn, + { "wps_er_learn", wpa_cli_cmd_wps_er_learn, NULL, cli_cmd_flag_sensitive, "<UUID> <PIN> = learn AP configuration" }, - { "wps_er_set_config", wpa_cli_cmd_wps_er_set_config, + { "wps_er_set_config", wpa_cli_cmd_wps_er_set_config, NULL, cli_cmd_flag_none, "<UUID> <network id> = set AP configuration for enrolling" }, - { "wps_er_config", wpa_cli_cmd_wps_er_config, + { "wps_er_config", wpa_cli_cmd_wps_er_config, NULL, cli_cmd_flag_sensitive, "<UUID> <PIN> <SSID> <auth> <encr> <key> = configure AP" }, #ifdef CONFIG_WPS_NFC - { "wps_er_nfc_config_token", wpa_cli_cmd_wps_er_nfc_config_token, + { "wps_er_nfc_config_token", wpa_cli_cmd_wps_er_nfc_config_token, NULL, cli_cmd_flag_none, "<WPS/NDEF> <UUID> = build NFC configuration token" }, #endif /* CONFIG_WPS_NFC */ - { "ibss_rsn", wpa_cli_cmd_ibss_rsn, + { "ibss_rsn", wpa_cli_cmd_ibss_rsn, NULL, cli_cmd_flag_none, "<addr> = request RSN authentication with <addr> in IBSS" }, #ifdef CONFIG_AP - { "sta", wpa_cli_cmd_sta, + { "sta", wpa_cli_cmd_sta, NULL, cli_cmd_flag_none, "<addr> = get information about an associated station (AP)" }, - { "all_sta", wpa_cli_cmd_all_sta, + { "all_sta", wpa_cli_cmd_all_sta, NULL, cli_cmd_flag_none, "= get information about all associated stations (AP)" }, - { "deauthenticate", wpa_cli_cmd_deauthenticate, + { "deauthenticate", wpa_cli_cmd_deauthenticate, NULL, cli_cmd_flag_none, "<addr> = deauthenticate a station" }, - { "disassociate", wpa_cli_cmd_disassociate, + { "disassociate", wpa_cli_cmd_disassociate, NULL, cli_cmd_flag_none, "<addr> = disassociate a station" }, #endif /* CONFIG_AP */ - { "suspend", wpa_cli_cmd_suspend, cli_cmd_flag_none, + { "suspend", wpa_cli_cmd_suspend, NULL, cli_cmd_flag_none, "= notification of suspend/hibernate" }, - { "resume", wpa_cli_cmd_resume, cli_cmd_flag_none, + { "resume", wpa_cli_cmd_resume, NULL, cli_cmd_flag_none, "= notification of resume/thaw" }, - { "drop_sa", wpa_cli_cmd_drop_sa, cli_cmd_flag_none, + { "drop_sa", wpa_cli_cmd_drop_sa, NULL, cli_cmd_flag_none, "= drop SA without deauth/disassoc (test command)" }, - { "roam", wpa_cli_cmd_roam, + { "roam", wpa_cli_cmd_roam, wpa_cli_complete_bss, cli_cmd_flag_none, "<addr> = roam to the specified BSS" }, #ifdef CONFIG_P2P - { "p2p_find", wpa_cli_cmd_p2p_find, cli_cmd_flag_none, + { "p2p_find", wpa_cli_cmd_p2p_find, wpa_cli_complete_p2p_find, + cli_cmd_flag_none, "[timeout] [type=*] = find P2P Devices for up-to timeout seconds" }, - { "p2p_stop_find", wpa_cli_cmd_p2p_stop_find, cli_cmd_flag_none, + { "p2p_stop_find", wpa_cli_cmd_p2p_stop_find, NULL, cli_cmd_flag_none, "= stop P2P Devices search" }, - { "p2p_connect", wpa_cli_cmd_p2p_connect, cli_cmd_flag_none, - "<addr> <\"pbc\"|PIN> = connect to a P2P Devices" }, - { "p2p_listen", wpa_cli_cmd_p2p_listen, cli_cmd_flag_none, + { "p2p_connect", wpa_cli_cmd_p2p_connect, wpa_cli_complete_p2p_connect, + cli_cmd_flag_none, + "<addr> <\"pbc\"|PIN> [ht40] = connect to a P2P Device" }, + { "p2p_listen", wpa_cli_cmd_p2p_listen, NULL, cli_cmd_flag_none, "[timeout] = listen for P2P Devices for up-to timeout seconds" }, - { "p2p_group_remove", wpa_cli_cmd_p2p_group_remove, cli_cmd_flag_none, + { "p2p_group_remove", wpa_cli_cmd_p2p_group_remove, + wpa_cli_complete_p2p_group_remove, cli_cmd_flag_none, "<ifname> = remove P2P group interface (terminate group if GO)" }, - { "p2p_group_add", wpa_cli_cmd_p2p_group_add, cli_cmd_flag_none, - "= add a new P2P group (local end as GO)" }, - { "p2p_prov_disc", wpa_cli_cmd_p2p_prov_disc, cli_cmd_flag_none, + { "p2p_group_add", wpa_cli_cmd_p2p_group_add, NULL, cli_cmd_flag_none, + "[ht40] = add a new P2P group (local end as GO)" }, + { "p2p_prov_disc", wpa_cli_cmd_p2p_prov_disc, + wpa_cli_complete_p2p_peer, cli_cmd_flag_none, "<addr> <method> = request provisioning discovery" }, - { "p2p_get_passphrase", wpa_cli_cmd_p2p_get_passphrase, + { "p2p_get_passphrase", wpa_cli_cmd_p2p_get_passphrase, NULL, cli_cmd_flag_none, "= get the passphrase for a group (GO only)" }, { "p2p_serv_disc_req", wpa_cli_cmd_p2p_serv_disc_req, - cli_cmd_flag_none, + wpa_cli_complete_p2p_peer, cli_cmd_flag_none, "<addr> <TLVs> = schedule service discovery request" }, { "p2p_serv_disc_cancel_req", wpa_cli_cmd_p2p_serv_disc_cancel_req, - cli_cmd_flag_none, + NULL, cli_cmd_flag_none, "<id> = cancel pending service discovery request" }, - { "p2p_serv_disc_resp", wpa_cli_cmd_p2p_serv_disc_resp, + { "p2p_serv_disc_resp", wpa_cli_cmd_p2p_serv_disc_resp, NULL, cli_cmd_flag_none, "<freq> <addr> <dialog token> <TLVs> = service discovery response" }, - { "p2p_service_update", wpa_cli_cmd_p2p_service_update, + { "p2p_service_update", wpa_cli_cmd_p2p_service_update, NULL, cli_cmd_flag_none, "= indicate change in local services" }, - { "p2p_serv_disc_external", wpa_cli_cmd_p2p_serv_disc_external, + { "p2p_serv_disc_external", wpa_cli_cmd_p2p_serv_disc_external, NULL, cli_cmd_flag_none, "<external> = set external processing of service discovery" }, - { "p2p_service_flush", wpa_cli_cmd_p2p_service_flush, + { "p2p_service_flush", wpa_cli_cmd_p2p_service_flush, NULL, cli_cmd_flag_none, "= remove all stored service entries" }, - { "p2p_service_add", wpa_cli_cmd_p2p_service_add, + { "p2p_service_add", wpa_cli_cmd_p2p_service_add, NULL, cli_cmd_flag_none, "<bonjour|upnp> <query|version> <response|service> = add a local " "service" }, - { "p2p_service_del", wpa_cli_cmd_p2p_service_del, + { "p2p_service_del", wpa_cli_cmd_p2p_service_del, NULL, cli_cmd_flag_none, "<bonjour|upnp> <query|version> [|service] = remove a local " "service" }, - { "p2p_reject", wpa_cli_cmd_p2p_reject, + { "p2p_reject", wpa_cli_cmd_p2p_reject, wpa_cli_complete_p2p_peer, cli_cmd_flag_none, "<addr> = reject connection attempts from a specific peer" }, - { "p2p_invite", wpa_cli_cmd_p2p_invite, + { "p2p_invite", wpa_cli_cmd_p2p_invite, NULL, cli_cmd_flag_none, "<cmd> [peer=addr] = invite peer" }, - { "p2p_peers", wpa_cli_cmd_p2p_peers, cli_cmd_flag_none, + { "p2p_peers", wpa_cli_cmd_p2p_peers, NULL, cli_cmd_flag_none, "[discovered] = list known (optionally, only fully discovered) P2P " "peers" }, - { "p2p_peer", wpa_cli_cmd_p2p_peer, cli_cmd_flag_none, + { "p2p_peer", wpa_cli_cmd_p2p_peer, wpa_cli_complete_p2p_peer, + cli_cmd_flag_none, "<address> = show information about known P2P peer" }, - { "p2p_set", wpa_cli_cmd_p2p_set, cli_cmd_flag_none, + { "p2p_set", wpa_cli_cmd_p2p_set, NULL, cli_cmd_flag_none, "<field> <value> = set a P2P parameter" }, - { "p2p_flush", wpa_cli_cmd_p2p_flush, cli_cmd_flag_none, + { "p2p_flush", wpa_cli_cmd_p2p_flush, NULL, cli_cmd_flag_none, "= flush P2P state" }, - { "p2p_cancel", wpa_cli_cmd_p2p_cancel, cli_cmd_flag_none, + { "p2p_cancel", wpa_cli_cmd_p2p_cancel, NULL, cli_cmd_flag_none, "= cancel P2P group formation" }, - { "p2p_unauthorize", wpa_cli_cmd_p2p_unauthorize, cli_cmd_flag_none, + { "p2p_unauthorize", wpa_cli_cmd_p2p_unauthorize, + wpa_cli_complete_p2p_peer, cli_cmd_flag_none, "<address> = unauthorize a peer" }, - { "p2p_presence_req", wpa_cli_cmd_p2p_presence_req, cli_cmd_flag_none, + { "p2p_presence_req", wpa_cli_cmd_p2p_presence_req, NULL, + cli_cmd_flag_none, "[<duration> <interval>] [<duration> <interval>] = request GO " "presence" }, - { "p2p_ext_listen", wpa_cli_cmd_p2p_ext_listen, cli_cmd_flag_none, + { "p2p_ext_listen", wpa_cli_cmd_p2p_ext_listen, NULL, + cli_cmd_flag_none, "[<period> <interval>] = set extended listen timing" }, #endif /* CONFIG_P2P */ - +#ifdef CONFIG_WIFI_DISPLAY + { "wfd_subelem_set", wpa_cli_cmd_wfd_subelem_set, NULL, + cli_cmd_flag_none, + "<subelem> [contents] = set Wi-Fi Display subelement" }, + { "wfd_subelem_get", wpa_cli_cmd_wfd_subelem_get, NULL, + cli_cmd_flag_none, + "<subelem> = get Wi-Fi Display subelement" }, +#endif /* CONFIG_WIFI_DISPLAY */ #ifdef CONFIG_INTERWORKING - { "fetch_anqp", wpa_cli_cmd_fetch_anqp, cli_cmd_flag_none, + { "fetch_anqp", wpa_cli_cmd_fetch_anqp, NULL, cli_cmd_flag_none, "= fetch ANQP information for all APs" }, - { "stop_fetch_anqp", wpa_cli_cmd_stop_fetch_anqp, cli_cmd_flag_none, + { "stop_fetch_anqp", wpa_cli_cmd_stop_fetch_anqp, NULL, + cli_cmd_flag_none, "= stop fetch_anqp operation" }, - { "interworking_select", wpa_cli_cmd_interworking_select, + { "interworking_select", wpa_cli_cmd_interworking_select, NULL, cli_cmd_flag_none, "[auto] = perform Interworking network selection" }, { "interworking_connect", wpa_cli_cmd_interworking_connect, - cli_cmd_flag_none, + wpa_cli_complete_bss, cli_cmd_flag_none, "<BSSID> = connect using Interworking credentials" }, - { "anqp_get", wpa_cli_cmd_anqp_get, cli_cmd_flag_none, + { "anqp_get", wpa_cli_cmd_anqp_get, wpa_cli_complete_bss, + cli_cmd_flag_none, "<addr> <info id>[,<info id>]... = request ANQP information" }, + { "gas_request", wpa_cli_cmd_gas_request, wpa_cli_complete_bss, + cli_cmd_flag_none, + "<addr> <AdvProtoID> [QueryReq] = GAS request" }, + { "gas_response_get", wpa_cli_cmd_gas_response_get, + wpa_cli_complete_bss, cli_cmd_flag_none, + "<addr> <dialog token> [start,len] = Fetch last GAS response" }, #endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_HS20 - { "hs20_anqp_get", wpa_cli_cmd_hs20_anqp_get, cli_cmd_flag_none, + { "hs20_anqp_get", wpa_cli_cmd_hs20_anqp_get, wpa_cli_complete_bss, + cli_cmd_flag_none, "<addr> <subtype>[,<subtype>]... = request HS 2.0 ANQP information" }, { "nai_home_realm_list", wpa_cli_cmd_get_nai_home_realm_list, - cli_cmd_flag_none, + wpa_cli_complete_bss, cli_cmd_flag_none, "<addr> <home realm> = get HS20 nai home realm list" }, #endif /* CONFIG_HS20 */ - { "sta_autoconnect", wpa_cli_cmd_sta_autoconnect, cli_cmd_flag_none, + { "sta_autoconnect", wpa_cli_cmd_sta_autoconnect, NULL, + cli_cmd_flag_none, "<0/1> = disable/enable automatic reconnection" }, - { "tdls_discover", wpa_cli_cmd_tdls_discover, + { "tdls_discover", wpa_cli_cmd_tdls_discover, NULL, cli_cmd_flag_none, "<addr> = request TDLS discovery with <addr>" }, - { "tdls_setup", wpa_cli_cmd_tdls_setup, + { "tdls_setup", wpa_cli_cmd_tdls_setup, NULL, cli_cmd_flag_none, "<addr> = request TDLS setup with <addr>" }, - { "tdls_teardown", wpa_cli_cmd_tdls_teardown, + { "tdls_teardown", wpa_cli_cmd_tdls_teardown, NULL, cli_cmd_flag_none, "<addr> = tear down TDLS with <addr>" }, - { "signal_poll", wpa_cli_cmd_signal_poll, + { "signal_poll", wpa_cli_cmd_signal_poll, NULL, cli_cmd_flag_none, "= get signal parameters" }, - { "pktcnt_poll", wpa_cli_cmd_pktcnt_poll, + { "pktcnt_poll", wpa_cli_cmd_pktcnt_poll, NULL, cli_cmd_flag_none, "= get TX/RX packet counters" }, - { "reauthenticate", wpa_cli_cmd_reauthenticate, cli_cmd_flag_none, + { "reauthenticate", wpa_cli_cmd_reauthenticate, NULL, + cli_cmd_flag_none, "= trigger IEEE 802.1X/EAPOL reauthentication" }, #ifdef CONFIG_AUTOSCAN - { "autoscan", wpa_cli_cmd_autoscan, cli_cmd_flag_none, + { "autoscan", wpa_cli_cmd_autoscan, NULL, cli_cmd_flag_none, "[params] = Set or unset (if none) autoscan parameters" }, #endif /* CONFIG_AUTOSCAN */ + { "raw", wpa_cli_cmd_raw, NULL, cli_cmd_flag_sensitive, + "<params..> = Sent unprocessed command" }, #ifdef ANDROID - { "driver", wpa_cli_cmd_driver, + { "driver", wpa_cli_cmd_driver, NULL, cli_cmd_flag_none, "<command> = driver private commands" }, #endif - { NULL, NULL, cli_cmd_flag_none, NULL } + { NULL, NULL, NULL, cli_cmd_flag_none, NULL } }; @@ -3433,12 +2713,14 @@ static void print_cmd_help(struct wpa_cli_cmd *cmd, const char *pad) } -static void print_help(void) +static void print_help(const char *cmd) { int n; printf("commands:\n"); - for (n = 0; wpa_cli_commands[n].cmd; n++) - print_cmd_help(&wpa_cli_commands[n], " "); + for (n = 0; wpa_cli_commands[n].cmd; n++) { + if (cmd == NULL || str_starts(wpa_cli_commands[n].cmd, cmd)) + print_cmd_help(&wpa_cli_commands[n], " "); + } } @@ -3469,7 +2751,7 @@ static char ** wpa_list_cmd_list(void) int i, count; count = sizeof(wpa_cli_commands) / sizeof(wpa_cli_commands[0]); - res = os_zalloc(count * sizeof(char *)); + res = os_calloc(count, sizeof(char *)); if (res == NULL) return NULL; @@ -3488,19 +2770,11 @@ static char ** wpa_cli_cmd_completion(const char *cmd, const char *str, { int i; - if (os_strcasecmp(cmd, "bss") == 0) - return wpa_cli_complete_bss(str, pos); -#ifdef CONFIG_P2P - if (os_strcasecmp(cmd, "p2p_connect") == 0) - return wpa_cli_complete_p2p_connect(str, pos); - if (os_strcasecmp(cmd, "p2p_peer") == 0) - return wpa_cli_complete_p2p_peer(str, pos); - if (os_strcasecmp(cmd, "p2p_group_remove") == 0) - return wpa_cli_complete_p2p_group_remove(str, pos); -#endif /* CONFIG_P2P */ - for (i = 0; wpa_cli_commands[i].cmd; i++) { if (os_strcasecmp(wpa_cli_commands[i].cmd, cmd) == 0) { + if (wpa_cli_commands[i].completion) + return wpa_cli_commands[i].completion(str, + pos); edit_clear_line(); printf("\r%s\n", wpa_cli_commands[i].usage); edit_redraw(); @@ -3711,7 +2985,14 @@ static void wpa_cli_action_cb(char *msg, size_t len) static void wpa_cli_reconnect(void) { wpa_cli_close_connection(); - wpa_cli_open_connection(ctrl_ifname, 1); + if (wpa_cli_open_connection(ctrl_ifname, 1) < 0) + return; + + if (interactive) { + edit_clear_line(); + printf("\rConnection to wpa_supplicant re-established\n"); + edit_redraw(); + } } @@ -3783,6 +3064,33 @@ static void cli_event(const char *str) } +static int check_terminating(const char *msg) +{ + const char *pos = msg; + + if (*pos == '<') { + /* skip priority */ + pos = os_strchr(pos, '>'); + if (pos) + pos++; + else + pos = msg; + } + + if (str_match(pos, WPA_EVENT_TERMINATING) && ctrl_conn) { + edit_clear_line(); + printf("\rConnection to wpa_supplicant lost - trying to " + "reconnect\n"); + edit_redraw(); + wpa_cli_attached = 0; + wpa_cli_close_connection(); + return 1; + } + + return 0; +} + + static void wpa_cli_recv_pending(struct wpa_ctrl *ctrl, int action_monitor) { if (ctrl_conn == NULL) { @@ -3803,6 +3111,9 @@ static void wpa_cli_recv_pending(struct wpa_ctrl *ctrl, int action_monitor) printf("\r%s\n", buf); edit_redraw(); } + + if (interactive && check_terminating(buf) > 0) + return; } } else { printf("Could not read pending message.\n"); @@ -3862,12 +3173,6 @@ static void wpa_cli_ping(void *eloop_ctx, void *timeout_ctx) } -static void wpa_cli_eloop_terminate(int sig, void *signal_ctx) -{ - eloop_terminate(); -} - - static void wpa_cli_mon_receive(int sock, void *eloop_ctx, void *sock_ctx) { wpa_cli_recv_pending(mon_conn, 0); @@ -3890,11 +3195,18 @@ static void wpa_cli_edit_eof_cb(void *ctx) } -static void wpa_cli_interactive(void) +static int warning_displayed = 0; +static char *hfile = NULL; +static int edit_started = 0; + +static void start_edit(void) { - char *home, *hfile = NULL; + char *home; + char *ps = NULL; - printf("\nInteractive mode\n\n"); +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + ps = wpa_ctrl_get_remote_ifname(ctrl_conn); +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ home = getenv("HOME"); if (home) { @@ -3905,17 +3217,52 @@ static void wpa_cli_interactive(void) os_snprintf(hfile, hfile_len, "%s/%s", home, fname); } - eloop_register_signal_terminate(wpa_cli_eloop_terminate, NULL); - edit_init(wpa_cli_edit_cmd_cb, wpa_cli_edit_eof_cb, - wpa_cli_edit_completion_cb, NULL, hfile); + if (edit_init(wpa_cli_edit_cmd_cb, wpa_cli_edit_eof_cb, + wpa_cli_edit_completion_cb, NULL, hfile, ps) < 0) { + eloop_terminate(); + return; + } + + edit_started = 1; eloop_register_timeout(ping_interval, 0, wpa_cli_ping, NULL, NULL); +} + + +static void try_connection(void *eloop_ctx, void *timeout_ctx) +{ + if (ctrl_ifname == NULL) + ctrl_ifname = wpa_cli_get_default_ifname(); + + if (!wpa_cli_open_connection(ctrl_ifname, 1) == 0) { + if (!warning_displayed) { + printf("Could not connect to wpa_supplicant: " + "%s - re-trying\n", ctrl_ifname); + warning_displayed = 1; + } + eloop_register_timeout(1, 0, try_connection, NULL, NULL); + return; + } + + if (warning_displayed) + printf("Connection established.\n"); + start_edit(); +} + + +static void wpa_cli_interactive(void) +{ + printf("\nInteractive mode\n\n"); + + eloop_register_timeout(0, 0, try_connection, NULL, NULL); eloop_run(); + eloop_cancel_timeout(try_connection, NULL, NULL); cli_txt_list_flush(&p2p_peers); cli_txt_list_flush(&p2p_groups); cli_txt_list_flush(&bsses); - edit_deinit(hfile, wpa_cli_edit_filter_history_cb); + if (edit_started) + edit_deinit(hfile, wpa_cli_edit_filter_history_cb); os_free(hfile); eloop_cancel_timeout(wpa_cli_ping, NULL, NULL); wpa_cli_close_connection(); @@ -3974,10 +3321,10 @@ static void wpa_cli_cleanup(void) os_program_deinit(); } -static void wpa_cli_terminate(int sig) + +static void wpa_cli_terminate(int sig, void *ctx) { - wpa_cli_cleanup(); - exit(0); + eloop_terminate(); } @@ -4051,7 +3398,6 @@ static char * wpa_cli_get_default_ifname(void) int main(int argc, char *argv[]) { - int warning_displayed = 0; int c; int daemonize = 0; int ret = 0; @@ -4121,30 +3467,13 @@ int main(int argc, char *argv[]) } } -#ifndef _WIN32_WCE - signal(SIGINT, wpa_cli_terminate); - signal(SIGTERM, wpa_cli_terminate); -#endif /* _WIN32_WCE */ + eloop_register_signal_terminate(wpa_cli_terminate, NULL); if (ctrl_ifname == NULL) ctrl_ifname = wpa_cli_get_default_ifname(); if (interactive) { - for (; !global;) { - if (wpa_cli_open_connection(ctrl_ifname, 1) == 0) { - if (warning_displayed) - printf("Connection established.\n"); - break; - } - - if (!warning_displayed) { - printf("Could not connect to wpa_supplicant: " - "%s - re-trying\n", ctrl_ifname); - warning_displayed = 1; - } - os_sleep(1, 0); - continue; - } + wpa_cli_interactive(); } else { if (!global && wpa_cli_open_connection(ctrl_ifname, 0) < 0) { @@ -4163,17 +3492,16 @@ int main(int argc, char *argv[]) return -1; } } - } - if (daemonize && os_daemonize(pid_file)) - return -1; + if (daemonize && os_daemonize(pid_file)) + return -1; - if (interactive) - wpa_cli_interactive(); - else if (action_file) - wpa_cli_action(ctrl_conn); - else - ret = wpa_request(ctrl_conn, argc - optind, &argv[optind]); + if (action_file) + wpa_cli_action(ctrl_conn); + else + ret = wpa_request(ctrl_conn, argc - optind, + &argv[optind]); + } os_free(ctrl_ifname); eloop_destroy(); diff --git a/wpa_supplicant/wpa_passphrase.c b/wpa_supplicant/wpa_passphrase.c index a9ec2361..9b568f0f 100644 --- a/wpa_supplicant/wpa_passphrase.c +++ b/wpa_supplicant/wpa_passphrase.c @@ -52,7 +52,7 @@ int main(int argc, char *argv[]) return 1; } - pbkdf2_sha1(passphrase, ssid, os_strlen(ssid), 4096, psk, 32); + pbkdf2_sha1(passphrase, (u8 *) ssid, os_strlen(ssid), 4096, psk, 32); printf("network={\n"); printf("\tssid=\"%s\"\n", ssid); diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index 47abd9aa..284a91d9 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -21,6 +21,7 @@ #include "rsn_supp/wpa.h" #include "eloop.h" #include "config.h" +#include "utils/ext_password.h" #include "l2_packet/l2_packet.h" #include "wpa_supplicant_i.h" #include "driver_i.h" @@ -40,6 +41,7 @@ #include "gas_query.h" #include "ap.h" #include "p2p_supplicant.h" +#include "wifi_display.h" #include "notify.h" #include "bgscan.h" #include "autoscan.h" @@ -153,6 +155,11 @@ static int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s, keylen = 16; alg = WPA_ALG_CCMP; break; + case WPA_CIPHER_GCMP: + os_memcpy(key, ssid->psk, 16); + keylen = 16; + alg = WPA_ALG_GCMP; + break; case WPA_CIPHER_TKIP: /* WPA-None uses the same Michael MIC key for both TX and RX */ os_memcpy(key, ssid->psk, 16 + 8); @@ -191,6 +198,17 @@ static void wpa_supplicant_timeout(void *eloop_ctx, void *timeout_ctx) * So, wait a second until scanning again. */ wpa_supplicant_req_scan(wpa_s, 1, 0); + +#ifdef CONFIG_P2P + if (wpa_s->p2p_cb_on_scan_complete && !wpa_s->global->p2p_disabled && + wpa_s->global->p2p != NULL) { + wpa_s->p2p_cb_on_scan_complete = 0; + if (p2p_other_scan_completed(wpa_s->global->p2p) == 1) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Pending P2P operation " + "continued after timed out authentication"); + } + } +#endif /* CONFIG_P2P */ } @@ -453,6 +471,11 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) wpa_s->bssid_filter = NULL; wnm_bss_keep_alive_deinit(wpa_s); + + ext_password_deinit(wpa_s->ext_pw); + wpa_s->ext_pw = NULL; + + wpabuf_free(wpa_s->last_gas_resp); } @@ -554,8 +577,16 @@ static void wpa_supplicant_start_bgscan(struct wpa_supplicant *wpa_s) * optimization, so the initial connection is not * affected. */ - } else + } else { + struct wpa_scan_results *scan_res; wpa_s->bgscan_ssid = wpa_s->current_ssid; + scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, + 0); + if (scan_res) { + bgscan_notify_scan(wpa_s, scan_res); + wpa_scan_results_free(scan_res); + } + } } else wpa_s->bgscan_ssid = NULL; } @@ -631,6 +662,7 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, ssid ? ssid->id : -1, ssid && ssid->id_str ? ssid->id_str : ""); #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ + wpas_clear_temp_disabled(wpa_s, ssid, 1); wpa_s->new_connection = 0; wpa_s->reassociated_connection = 1; wpa_drv_set_operstate(wpa_s, 1); @@ -823,6 +855,8 @@ enum wpa_cipher cipher_suite2driver(int cipher) return CIPHER_WEP104; case WPA_CIPHER_CCMP: return CIPHER_CCMP; + case WPA_CIPHER_GCMP: + return CIPHER_GCMP; case WPA_CIPHER_TKIP: default: return CIPHER_TKIP; @@ -999,6 +1033,9 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, if (sel & WPA_CIPHER_CCMP) { wpa_s->group_cipher = WPA_CIPHER_CCMP; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK CCMP"); + } else if (sel & WPA_CIPHER_GCMP) { + wpa_s->group_cipher = WPA_CIPHER_GCMP; + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK GCMP"); } else if (sel & WPA_CIPHER_TKIP) { wpa_s->group_cipher = WPA_CIPHER_TKIP; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK TKIP"); @@ -1018,6 +1055,9 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, if (sel & WPA_CIPHER_CCMP) { wpa_s->pairwise_cipher = WPA_CIPHER_CCMP; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK CCMP"); + } else if (sel & WPA_CIPHER_GCMP) { + wpa_s->pairwise_cipher = WPA_CIPHER_GCMP; + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK GCMP"); } else if (sel & WPA_CIPHER_TKIP) { wpa_s->pairwise_cipher = WPA_CIPHER_TKIP; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK TKIP"); @@ -1099,13 +1139,70 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, if (bss && ssid->bssid_set && ssid->ssid_len == 0 && ssid->passphrase) { u8 psk[PMK_LEN]; - pbkdf2_sha1(ssid->passphrase, (char *) bss->ssid, - bss->ssid_len, 4096, psk, PMK_LEN); + pbkdf2_sha1(ssid->passphrase, bss->ssid, bss->ssid_len, + 4096, psk, PMK_LEN); wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)", psk, PMK_LEN); wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN); } #endif /* CONFIG_NO_PBKDF2 */ +#ifdef CONFIG_EXT_PASSWORD + if (ssid->ext_psk) { + struct wpabuf *pw = ext_password_get(wpa_s->ext_pw, + ssid->ext_psk); + char pw_str[64 + 1]; + u8 psk[PMK_LEN]; + + if (pw == NULL) { + wpa_msg(wpa_s, MSG_INFO, "EXT PW: No PSK " + "found from external storage"); + return -1; + } + + if (wpabuf_len(pw) < 8 || wpabuf_len(pw) > 64) { + wpa_msg(wpa_s, MSG_INFO, "EXT PW: Unexpected " + "PSK length %d in external storage", + (int) wpabuf_len(pw)); + ext_password_free(pw); + return -1; + } + + os_memcpy(pw_str, wpabuf_head(pw), wpabuf_len(pw)); + pw_str[wpabuf_len(pw)] = '\0'; + +#ifndef CONFIG_NO_PBKDF2 + if (wpabuf_len(pw) >= 8 && wpabuf_len(pw) < 64 && bss) + { + pbkdf2_sha1(pw_str, bss->ssid, bss->ssid_len, + 4096, psk, PMK_LEN); + os_memset(pw_str, 0, sizeof(pw_str)); + wpa_hexdump_key(MSG_MSGDUMP, "PSK (from " + "external passphrase)", + psk, PMK_LEN); + wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN); + } else +#endif /* CONFIG_NO_PBKDF2 */ + if (wpabuf_len(pw) == 2 * PMK_LEN) { + if (hexstr2bin(pw_str, psk, PMK_LEN) < 0) { + wpa_msg(wpa_s, MSG_INFO, "EXT PW: " + "Invalid PSK hex string"); + os_memset(pw_str, 0, sizeof(pw_str)); + ext_password_free(pw); + return -1; + } + wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN); + } else { + wpa_msg(wpa_s, MSG_INFO, "EXT PW: No suitable " + "PSK available"); + os_memset(pw_str, 0, sizeof(pw_str)); + ext_password_free(pw); + return -1; + } + + os_memset(pw_str, 0, sizeof(pw_str)); + ext_password_free(pw); + } +#endif /* CONFIG_EXT_PASSWORD */ } else wpa_sm_set_pmk_from_pmksa(wpa_s->wpa); @@ -1257,6 +1354,16 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, "key management and encryption suites"); return; } + } else if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && bss && + wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt)) { + /* + * Both WPA and non-WPA IEEE 802.1X enabled in configuration - + * use non-WPA since the scan results did not indicate that the + * AP is using WPA or WPA2. + */ + wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); + wpa_ie_len = 0; + wpa_s->wpa_proto = 0; } else if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) { wpa_ie_len = sizeof(wpa_ie); if (wpa_supplicant_set_suites(wpa_s, NULL, ssid, @@ -1570,10 +1677,8 @@ static void wpa_supplicant_clear_connection(struct wpa_supplicant *wpa_s, struct wpa_ssid *old_ssid; wpa_clear_keys(wpa_s, addr); - wpa_supplicant_mark_disassoc(wpa_s); old_ssid = wpa_s->current_ssid; - wpa_s->current_ssid = NULL; - wpa_s->current_bss = NULL; + wpa_supplicant_mark_disassoc(wpa_s); wpa_sm_set_config(wpa_s->wpa, NULL); eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); if (old_ssid != wpa_s->current_ssid) @@ -1662,6 +1767,8 @@ void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s, was_disabled = other_ssid->disabled; other_ssid->disabled = 0; + if (was_disabled) + wpas_clear_temp_disabled(wpa_s, other_ssid, 0); if (was_disabled != other_ssid->disabled) wpas_notify_network_enabled_changed( @@ -1682,6 +1789,7 @@ void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s, was_disabled = ssid->disabled; ssid->disabled = 0; + wpas_clear_temp_disabled(wpa_s, ssid, 1); if (was_disabled != ssid->disabled) wpas_notify_network_enabled_changed(wpa_s, ssid); @@ -1752,6 +1860,9 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s, disconnected = 1; } + if (ssid) + wpas_clear_temp_disabled(wpa_s, ssid, 1); + /* * Mark all other networks disabled or mark all networks enabled if no * network specified. @@ -1763,6 +1874,8 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s, continue; /* do not change persistent P2P group data */ other_ssid->disabled = ssid ? (ssid->id != other_ssid->id) : 0; + if (was_disabled && !other_ssid->disabled) + wpas_clear_temp_disabled(wpa_s, other_ssid, 0); if (was_disabled != other_ssid->disabled) wpas_notify_network_enabled_changed(wpa_s, other_ssid); @@ -2529,6 +2642,38 @@ static int pcsc_reader_init(struct wpa_supplicant *wpa_s) } +int wpas_init_ext_pw(struct wpa_supplicant *wpa_s) +{ + char *val, *pos; + + ext_password_deinit(wpa_s->ext_pw); + wpa_s->ext_pw = NULL; + eapol_sm_set_ext_pw_ctx(wpa_s->eapol, NULL); + + if (!wpa_s->conf->ext_password_backend) + return 0; + + val = os_strdup(wpa_s->conf->ext_password_backend); + if (val == NULL) + return -1; + pos = os_strchr(val, ':'); + if (pos) + *pos++ = '\0'; + + wpa_printf(MSG_DEBUG, "EXT PW: Initialize backend '%s'", val); + + wpa_s->ext_pw = ext_password_init(val, pos); + os_free(val); + if (wpa_s->ext_pw == NULL) { + wpa_printf(MSG_DEBUG, "EXT PW: Failed to initialize backend"); + return -1; + } + eapol_sm_set_ext_pw_ctx(wpa_s->eapol, wpa_s->ext_pw); + + return 0; +} + + static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, struct wpa_interface *iface) { @@ -2754,6 +2899,9 @@ next_driver: if (pcsc_reader_init(wpa_s) < 0) return -1; + if (wpas_init_ext_pw(wpa_s) < 0) + return -1; + return 0; } @@ -3072,6 +3220,14 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params) return NULL; } +#ifdef CONFIG_WIFI_DISPLAY + if (wifi_display_init(global) < 0) { + wpa_printf(MSG_ERROR, "Failed to initialize Wi-Fi Display"); + wpa_supplicant_deinit(global); + return NULL; + } +#endif /* CONFIG_WIFI_DISPLAY */ + return global; } @@ -3123,6 +3279,9 @@ void wpa_supplicant_deinit(struct wpa_global *global) if (global == NULL) return; +#ifdef CONFIG_WIFI_DISPLAY + wifi_display_deinit(global); +#endif /* CONFIG_WIFI_DISPLAY */ #ifdef CONFIG_P2P wpas_p2p_deinit_global(global); #endif /* CONFIG_P2P */ @@ -3182,6 +3341,9 @@ void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s) } } + if (wpa_s->conf->changed_parameters & CFG_CHANGED_EXT_PW_BACKEND) + wpas_init_ext_pw(wpa_s); + #ifdef CONFIG_WPS wpas_wps_update_config(wpa_s); #endif /* CONFIG_WPS */ @@ -3304,6 +3466,17 @@ void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid) */ wpa_supplicant_req_scan(wpa_s, timeout / 1000, 1000 * (timeout % 1000)); + +#ifdef CONFIG_P2P + if (wpa_s->p2p_cb_on_scan_complete && !wpa_s->global->p2p_disabled && + wpa_s->global->p2p != NULL) { + wpa_s->p2p_cb_on_scan_complete = 0; + if (p2p_other_scan_completed(wpa_s->global->p2p) == 1) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Pending P2P operation " + "continued after failed association"); + } + } +#endif /* CONFIG_P2P */ } @@ -3417,6 +3590,10 @@ int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) return 1; /* invalid WEP key */ } + if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && !ssid->psk_set && + !ssid->ext_psk) + return 1; + return 0; } @@ -3429,3 +3606,63 @@ int wpas_is_p2p_prioritized(struct wpa_supplicant *wpa_s) return 0; return -1; } + + +void wpas_auth_failed(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid = wpa_s->current_ssid; + int dur; + struct os_time now; + + if (ssid == NULL) { + wpa_printf(MSG_DEBUG, "Authentication failure but no known " + "SSID block"); + return; + } + + if (ssid->key_mgmt == WPA_KEY_MGMT_WPS) + return; + + ssid->auth_failures++; + if (ssid->auth_failures > 50) + dur = 300; + else if (ssid->auth_failures > 20) + dur = 120; + else if (ssid->auth_failures > 10) + dur = 60; + else if (ssid->auth_failures > 5) + dur = 30; + else if (ssid->auth_failures > 1) + dur = 20; + else + dur = 10; + + os_get_time(&now); + if (now.sec + dur <= ssid->disabled_until.sec) + return; + + ssid->disabled_until.sec = now.sec + dur; + + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_TEMP_DISABLED + "id=%d ssid=\"%s\" auth_failures=%u duration=%d", + ssid->id, wpa_ssid_txt(ssid->ssid, ssid->ssid_len), + ssid->auth_failures, dur); +} + + +void wpas_clear_temp_disabled(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, int clear_failures) +{ + if (ssid == NULL) + return; + + if (ssid->disabled_until.sec) { + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_REENABLED + "id=%d ssid=\"%s\"", + ssid->id, wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); + } + ssid->disabled_until.sec = 0; + ssid->disabled_until.usec = 0; + if (clear_failures) + ssid->auth_failures = 0; +} diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf index 3ebbcc26..2c07fd0a 100644 --- a/wpa_supplicant/wpa_supplicant.conf +++ b/wpa_supplicant/wpa_supplicant.conf @@ -241,20 +241,30 @@ fast_reauth=1 # This is an optional set of parameters for automatic scanning # within an interface in following format: #autoscan=<autoscan module name>:<module parameters> -# autoscan is like bgscan but on disconnected or inactive state. -# For instance, on exponential module parameters would be <base>:<limit> +#Â autoscan is like bgscan but on disconnected or inactive state. +#Â For instance, on exponential module parameters would be <base>:<limit> #autoscan=exponential:3:300 # Which means a delay between scans on a base exponential of 3, -# up to the limit of 300 seconds (3, 9, 27 ... 300) -# For periodic module, parameters would be <fixed interval> +#Â up to the limit of 300 seconds (3, 9, 27 ... 300) +#Â For periodic module, parameters would be <fixed interval> #autoscan=periodic:30 -# So a delay of 30 seconds will be applied between each scan +#Â So a delay of 30 seconds will be applied between each scan # filter_ssids - SSID-based scan result filtering # 0 = do not filter scan results (default) # 1 = only include configured SSIDs in scan results/BSS table #filter_ssids=0 +# Password (and passphrase, etc.) backend for external storage +# format: <backend name>[:<optional backend parameters>] +#ext_password_backend=test:pw1=password|pw2=testing + +# Timeout in seconds to detect STA inactivity (default: 300 seconds) +# +# This timeout value is used in P2P GO mode to clean up +# inactive stations. +#p2p_go_max_inactivity=300 + # Interworking (IEEE 802.11u) @@ -267,6 +277,14 @@ fast_reauth=1 # is enabled. # hessid=00:11:22:33:44:55 +# Automatic network selection behavior +# 0 = do not automatically go through Interworking network selection +# (i.e., require explicit interworking_select command for this; default) +# 1 = perform Interworking network selection if one or more +# credentials have been configured and scan did not find a +# matching network block +#auto_interworking=0 + # credential block # # Each credential used for automatic network selection is configured as a set @@ -336,6 +354,26 @@ fast_reauth=1 # This is used to compare against the Domain Name List to figure out # whether the AP is operated by the Home SP. # +# roaming_consortium: Roaming Consortium OI +# If roaming_consortium_len is non-zero, this field contains the +# Roaming Consortium OI that can be used to determine which access +# points support authentication with this credential. This is an +# alternative to the use of the realm parameter. When using Roaming +# Consortium to match the network, the EAP parameters need to be +# pre-configured with the credential since the NAI Realm information +# may not be available or fetched. +# +# eap: Pre-configured EAP method +# This optional field can be used to specify which EAP method will be +# used with this credential. If not set, the EAP method is selected +# automatically based on ANQP information (e.g., NAI Realm). +# +# phase1: Pre-configure Phase 1 (outer authentication) parameters +# This optional field is used with like the 'eap' parameter. +# +# phase2: Pre-configure Phase 2 (inner authentication) parameters +# This optional field is used with like the 'eap' parameter. +# # for example: # #cred={ @@ -350,6 +388,17 @@ fast_reauth=1 # imsi="310026-000000000" # milenage="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82" #} +# +#cred={ +# realm="example.com" +# username="user" +# password="password" +# ca_cert="/etc/wpa_supplicant/ca.pem" +# domain="example.com" +# roaming_consortium=223344 +# eap=TTLS +# phase2="auth=MSCHAPV2" +#} # Hotspot 2.0 # hs20=1 @@ -371,8 +420,10 @@ fast_reauth=1 # to external action script through wpa_cli as WPA_ID_STR environment # variable to make it easier to do network specific configuration. # -# ssid: SSID (mandatory); either as an ASCII string with double quotation or -# as hex string; network name +# ssid: SSID (mandatory); network name in one of the optional formats: +# - an ASCII string with double quotation +# - a hex string (two characters per octet of SSID) +# - a printf-escaped ASCII string P"<escaped string>" # # scan_ssid: # 0 = do not scan this SSID with specific Probe Request frames (default) @@ -473,7 +524,8 @@ fast_reauth=1 # The key used in WPA-PSK mode can be entered either as 64 hex-digits, i.e., # 32 bytes or as an ASCII passphrase (in which case, the real PSK will be # generated using the passphrase and SSID). ASCII passphrase must be between -# 8 and 63 characters (inclusive). +# 8 and 63 characters (inclusive). ext:<name of external PSK field> format can +# be used to indicate that the PSK/passphrase is stored in external storage. # This field is not needed, if WPA-EAP is used. # Note: Separate tool, wpa_passphrase, can be used to generate 256-bit keys # from ASCII passphrase. This process uses lot of CPU and wpa_supplicant @@ -542,7 +594,8 @@ fast_reauth=1 # MSCHAP (EAP-MSCHAPv2, EAP-TTLS/MSCHAPv2, EAP-TTLS/MSCHAP, LEAP). # EAP-PSK (128-bit PSK), EAP-PAX (128-bit PSK), and EAP-SAKE (256-bit # PSK) is also configured using this field. For EAP-GPSK, this is a -# variable length PSK. +# variable length PSK. ext:<name of external password field> format can +# be used to indicate that the password is stored in external storage. # ca_cert: File path to CA certificate file (PEM/DER). This file can have one # or more trusted CA certificates. If ca_cert and ca_path are not # included, server certificate will not be verified. This is insecure and @@ -645,6 +698,25 @@ fast_reauth=1 # phase2: Phase2 (inner authentication with TLS tunnel) parameters # (string with field-value pairs, e.g., "auth=MSCHAPV2" for EAP-PEAP or # "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS) +# +# TLS-based methods can use the following parameters to control TLS behavior +# (these are normally in the phase1 parameter, but can be used also in the +# phase2 parameter when EAP-TLS is used within the inner tunnel): +# tls_allow_md5=1 - allow MD5-based certificate signatures (depending on the +# TLS library, these may be disabled by default to enforce stronger +# security) +# tls_disable_time_checks=1 - ignore certificate validity time (this requests +# the TLS library to accept certificates even if they are not currently +# valid, i.e., have expired or have not yet become valid; this should be +# used only for testing purposes) +# tls_disable_session_ticket=1 - disable TLS Session Ticket extension +# 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. +# For EAP-FAST, this must be set to 0 (or left unconfigured for the +# default value to be used automatically). +# # Following certificate/private key fields are used in inner Phase2 # authentication when using EAP-TTLS or EAP-PEAP. # ca_cert2: File path to CA certificate file. This file can have one or more diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 3f6669dc..523b50df 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -1,6 +1,6 @@ /* * wpa_supplicant - Internal definitions - * Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -239,6 +239,12 @@ struct wpa_global { WPA_CONC_PREF_STA, WPA_CONC_PREF_P2P } conc_pref; + +#ifdef CONFIG_WIFI_DISPLAY + int wifi_display; +#define MAX_WFD_SUBELEMS 10 + struct wpabuf *wfd_subelem[MAX_WFD_SUBELEMS]; +#endif /* CONFIG_WIFI_DISPLAY */ }; @@ -249,6 +255,17 @@ enum offchannel_send_action_result { OFFCHANNEL_SEND_ACTION_FAILED /* Frame was not sent due to a failure */ }; +struct wps_ap_info { + u8 bssid[ETH_ALEN]; + enum wps_ap_info_type { + WPS_AP_NOT_SEL_REG, + WPS_AP_SEL_REG, + WPS_AP_SEL_REG_OUR + } type; + unsigned int tries; + struct os_time last_attempt; +}; + /** * struct wpa_supplicant - Internal data for wpa_supplicant interface * @@ -514,23 +531,14 @@ struct wpa_supplicant { */ char cross_connect_uplink[100]; - enum { - P2P_GROUP_REMOVAL_UNKNOWN, - P2P_GROUP_REMOVAL_REQUESTED, - P2P_GROUP_REMOVAL_IDLE_TIMEOUT, - P2P_GROUP_REMOVAL_UNAVAILABLE, - P2P_GROUP_REMOVAL_GO_ENDING_SESSION, -#ifdef ANDROID_P2P - P2P_GROUP_REMOVAL_FREQ_CONFLICT -#endif - } removal_reason; - unsigned int p2p_cb_on_scan_complete:1; + unsigned int sta_scan_pending:1; unsigned int p2p_auto_join:1; unsigned int p2p_auto_pd:1; unsigned int p2p_persistent_group:1; unsigned int p2p_fallback_to_go_neg:1; unsigned int p2p_pd_before_go_neg:1; + unsigned int p2p_go_ht40:1; int p2p_persistent_id; int p2p_go_intent; int p2p_connect_freq; @@ -547,6 +555,10 @@ struct wpa_supplicant { struct wpa_ssid *connect_without_scan; + struct wps_ap_info *wps_ap; + size_t num_wps_ap; + int wps_ap_iter; + int after_wps; int known_wps_freq; unsigned int wps_freq; @@ -564,6 +576,7 @@ struct wpa_supplicant { unsigned int fetch_anqp_in_progress:1; unsigned int network_select:1; unsigned int auto_select:1; + unsigned int auto_network_select:1; #endif /* CONFIG_INTERWORKING */ unsigned int drv_capa_known; @@ -577,6 +590,12 @@ struct wpa_supplicant { /* WLAN_REASON_* reason codes. Negative if locally generated. */ int disconnect_reason; + + struct ext_password_data *ext_pw; + + struct wpabuf *last_gas_resp; + u8 last_gas_addr[ETH_ALEN]; + u8 last_gas_dialog_token; }; @@ -659,6 +678,9 @@ void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s); void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid); int wpas_driver_bss_selection(struct wpa_supplicant *wpa_s); int wpas_is_p2p_prioritized(struct wpa_supplicant *wpa_s); +void wpas_auth_failed(struct wpa_supplicant *wpa_s); +void wpas_clear_temp_disabled(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, int clear_failures); void wpa_supplicant_proc_40mhz_intolerant(struct wpa_supplicant *wpa_s); /** @@ -700,4 +722,6 @@ static inline int network_is_persistent_group(struct wpa_ssid *ssid) int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); +int wpas_init_ext_pw(struct wpa_supplicant *wpa_s); + #endif /* WPA_SUPPLICANT_I_H */ diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c index 0873b1a8..23966b86 100644 --- a/wpa_supplicant/wps_supplicant.c +++ b/wpa_supplicant/wps_supplicant.c @@ -43,6 +43,15 @@ static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx); static void wpas_clear_wps(struct wpa_supplicant *wpa_s); +static void wpas_wps_clear_ap_info(struct wpa_supplicant *wpa_s) +{ + os_free(wpa_s->wps_ap); + wpa_s->wps_ap = NULL; + wpa_s->num_wps_ap = 0; + wpa_s->wps_ap_iter = 0; +} + + int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s) { if (!wpa_s->wps_success && @@ -66,6 +75,7 @@ int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s) return 1; } + wpas_wps_clear_ap_info(wpa_s); eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL); if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && !wpa_s->wps_success) wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_FAIL); @@ -263,9 +273,13 @@ static int wpa_supplicant_wps_cred(void *ctx, ssid->eap.phase1 = NULL; os_free(ssid->eap.eap_methods); ssid->eap.eap_methods = NULL; - if (!ssid->p2p_group) + if (!ssid->p2p_group) { ssid->temporary = 0; - ssid->bssid_set = 0; + ssid->bssid_set = 0; + } + ssid->disabled_until.sec = 0; + ssid->disabled_until.usec = 0; + ssid->auth_failures = 0; } else { wpa_printf(MSG_DEBUG, "WPS: Create a new network based on the " "received credential"); @@ -353,16 +367,6 @@ static int wpa_supplicant_wps_cred(void *ctx, ssid->key_mgmt = WPA_KEY_MGMT_PSK; ssid->proto = WPA_PROTO_WPA; break; - case WPS_AUTH_WPA: - ssid->auth_alg = WPA_AUTH_ALG_OPEN; - ssid->key_mgmt = WPA_KEY_MGMT_IEEE8021X; - ssid->proto = WPA_PROTO_WPA; - break; - case WPS_AUTH_WPA2: - ssid->auth_alg = WPA_AUTH_ALG_OPEN; - ssid->key_mgmt = WPA_KEY_MGMT_IEEE8021X; - ssid->proto = WPA_PROTO_RSN; - break; case WPS_AUTH_WPA2PSK: ssid->auth_alg = WPA_AUTH_ALG_OPEN; ssid->key_mgmt = WPA_KEY_MGMT_PSK; @@ -712,6 +716,8 @@ static void wpas_clear_wps(struct wpa_supplicant *wpa_s) wpa_config_remove_network(wpa_s->conf, id); } } + + wpas_wps_clear_ap_info(wpa_s); } @@ -907,6 +913,7 @@ int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, ssid->eap.fragment_size = wpa_s->wps_fragment_size; eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout, wpa_s, NULL); + wpa_s->wps_ap_iter = 1; wpas_wps_reassoc(wpa_s, ssid, bssid); return rpin; } @@ -933,7 +940,8 @@ int wpas_wps_cancel(struct wpa_supplicant *wpa_s) wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); wpas_clear_wps(wpa_s); - } + } else + wpas_wps_clear_ap_info(wpa_s); return 0; } @@ -1239,6 +1247,7 @@ int wpas_wps_init(struct wpa_supplicant *wpa_s) void wpas_wps_deinit(struct wpa_supplicant *wpa_s) { eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL); + wpas_wps_clear_ap_info(wpa_s); if (wpa_s->wps == NULL) return; @@ -1920,3 +1929,130 @@ int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_WPS_NFC */ + + +extern int wpa_debug_level; + +static void wpas_wps_dump_ap_info(struct wpa_supplicant *wpa_s) +{ + size_t i; + struct os_time now; + + if (wpa_debug_level > MSG_DEBUG) + return; + + if (wpa_s->wps_ap == NULL) + return; + + os_get_time(&now); + + for (i = 0; i < wpa_s->num_wps_ap; i++) { + struct wps_ap_info *ap = &wpa_s->wps_ap[i]; + struct wpa_blacklist *e = wpa_blacklist_get(wpa_s, ap->bssid); + + wpa_printf(MSG_DEBUG, "WPS: AP[%d] " MACSTR " type=%d " + "tries=%d last_attempt=%d sec ago blacklist=%d", + (int) i, MAC2STR(ap->bssid), ap->type, ap->tries, + ap->last_attempt.sec > 0 ? + (int) now.sec - (int) ap->last_attempt.sec : -1, + e ? e->count : 0); + } +} + + +static struct wps_ap_info * wpas_wps_get_ap_info(struct wpa_supplicant *wpa_s, + const u8 *bssid) +{ + size_t i; + + if (wpa_s->wps_ap == NULL) + return NULL; + + for (i = 0; i < wpa_s->num_wps_ap; i++) { + struct wps_ap_info *ap = &wpa_s->wps_ap[i]; + if (os_memcmp(ap->bssid, bssid, ETH_ALEN) == 0) + return ap; + } + + return NULL; +} + + +static void wpas_wps_update_ap_info_bss(struct wpa_supplicant *wpa_s, + struct wpa_scan_res *res) +{ + struct wpabuf *wps; + enum wps_ap_info_type type; + struct wps_ap_info *ap; + int r; + + if (wpa_scan_get_vendor_ie(res, WPS_IE_VENDOR_TYPE) == NULL) + return; + + wps = wpa_scan_get_vendor_ie_multi(res, WPS_IE_VENDOR_TYPE); + if (wps == NULL) + return; + + r = wps_is_addr_authorized(wps, wpa_s->own_addr, 1); + if (r == 2) + type = WPS_AP_SEL_REG_OUR; + else if (r == 1) + type = WPS_AP_SEL_REG; + else + type = WPS_AP_NOT_SEL_REG; + + wpabuf_free(wps); + + ap = wpas_wps_get_ap_info(wpa_s, res->bssid); + if (ap) { + if (ap->type != type) { + wpa_printf(MSG_DEBUG, "WPS: AP " MACSTR + " changed type %d -> %d", + MAC2STR(res->bssid), ap->type, type); + ap->type = type; + if (type != WPS_AP_NOT_SEL_REG) + wpa_blacklist_del(wpa_s, ap->bssid); + } + return; + } + + ap = os_realloc_array(wpa_s->wps_ap, wpa_s->num_wps_ap + 1, + sizeof(struct wps_ap_info)); + if (ap == NULL) + return; + + wpa_s->wps_ap = ap; + ap = &wpa_s->wps_ap[wpa_s->num_wps_ap]; + wpa_s->num_wps_ap++; + + os_memset(ap, 0, sizeof(*ap)); + os_memcpy(ap->bssid, res->bssid, ETH_ALEN); + ap->type = type; + wpa_printf(MSG_DEBUG, "WPS: AP " MACSTR " type %d added", + MAC2STR(ap->bssid), ap->type); +} + + +void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res) +{ + size_t i; + + for (i = 0; i < scan_res->num; i++) + wpas_wps_update_ap_info_bss(wpa_s, scan_res->res[i]); + + wpas_wps_dump_ap_info(wpa_s); +} + + +void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid) +{ + struct wps_ap_info *ap; + if (!wpa_s->wps_ap_iter) + return; + ap = wpas_wps_get_ap_info(wpa_s, bssid); + if (ap == NULL) + return; + ap->tries++; + os_get_time(&ap->last_attempt); +} diff --git a/wpa_supplicant/wps_supplicant.h b/wpa_supplicant/wps_supplicant.h index 5a49a8f2..36f1e021 100644 --- a/wpa_supplicant/wps_supplicant.h +++ b/wpa_supplicant/wps_supplicant.h @@ -10,6 +10,7 @@ #define WPS_SUPPLICANT_H struct wpa_scan_res; +struct wpa_scan_results; #ifdef CONFIG_WPS @@ -68,6 +69,9 @@ struct wpabuf * wpas_wps_nfc_token(struct wpa_supplicant *wpa_s, int ndef); int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *bssid); int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s, const struct wpabuf *data); +void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res); +void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid); #else /* CONFIG_WPS */ @@ -120,6 +124,16 @@ static inline int wpas_wps_searching(struct wpa_supplicant *wpa_s) return 0; } +static inline void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res) +{ +} + +static inline void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, + const u8 *bssid) +{ +} + #endif /* CONFIG_WPS */ #endif /* WPS_SUPPLICANT_H */ |
