diff options
63 files changed, 4217 insertions, 449 deletions
diff --git a/hostapd/Android.mk b/hostapd/Android.mk index c8ef46b9..6fcefdd1 100644 --- a/hostapd/Android.mk +++ b/hostapd/Android.mk @@ -181,8 +181,6 @@ OBJS += ctrl_iface.c OBJS += src/ap/ctrl_iface_ap.c endif -OBJS += src/crypto/md5.c - L_CFLAGS += -DCONFIG_CTRL_IFACE -DCONFIG_CTRL_IFACE_UNIX ifdef CONFIG_IAPP @@ -677,7 +675,9 @@ ifdef CONFIG_INTERNAL_AES AESOBJS += src/crypto/aes-internal.c src/crypto/aes-internal-enc.c endif +ifneq ($(CONFIG_TLS), openssl) AESOBJS += src/crypto/aes-wrap.c +endif ifdef NEED_AES_EAX AESOBJS += src/crypto/aes-eax.c NEED_AES_CTR=y @@ -692,9 +692,11 @@ ifdef NEED_AES_OMAC1 AESOBJS += src/crypto/aes-omac1.c endif ifdef NEED_AES_UNWRAP +ifneq ($(CONFIG_TLS), openssl) NEED_AES_DEC=y AESOBJS += src/crypto/aes-unwrap.c endif +endif ifdef NEED_AES_CBC NEED_AES_DEC=y AESOBJS += src/crypto/aes-cbc.c @@ -735,6 +737,10 @@ ifdef NEED_SHA1 OBJS += $(SHA1OBJS) endif +ifneq ($(CONFIG_TLS), openssl) +OBJS += src/crypto/md5.c +endif + ifdef NEED_MD5 ifdef CONFIG_INTERNAL_MD5 OBJS += src/crypto/md5-internal.c diff --git a/hostapd/Makefile b/hostapd/Makefile index 894b6527..eace68cd 100644 --- a/hostapd/Makefile +++ b/hostapd/Makefile @@ -6,6 +6,7 @@ ifndef CFLAGS CFLAGS = -MMD -O2 -Wall -g endif +CFLAGS += $(EXTRA_CFLAGS) CFLAGS += -I$(abspath ../src) CFLAGS += -I$(abspath ../src/utils) @@ -170,8 +171,6 @@ OBJS += ctrl_iface.o OBJS += ../src/ap/ctrl_iface_ap.o endif -OBJS += ../src/crypto/md5.o - CFLAGS += -DCONFIG_CTRL_IFACE -DCONFIG_CTRL_IFACE_UNIX ifdef CONFIG_IAPP @@ -671,7 +670,9 @@ ifdef CONFIG_INTERNAL_AES AESOBJS += ../src/crypto/aes-internal.o ../src/crypto/aes-internal-enc.o endif +ifneq ($(CONFIG_TLS), openssl) AESOBJS += ../src/crypto/aes-wrap.o +endif ifdef NEED_AES_EAX AESOBJS += ../src/crypto/aes-eax.o NEED_AES_CTR=y @@ -686,9 +687,11 @@ ifdef NEED_AES_OMAC1 AESOBJS += ../src/crypto/aes-omac1.o endif ifdef NEED_AES_UNWRAP +ifneq ($(CONFIG_TLS), openssl) NEED_AES_DEC=y AESOBJS += ../src/crypto/aes-unwrap.o endif +endif ifdef NEED_AES_CBC NEED_AES_DEC=y AESOBJS += ../src/crypto/aes-cbc.o @@ -728,6 +731,10 @@ ifdef NEED_SHA1 OBJS += $(SHA1OBJS) endif +ifneq ($(CONFIG_TLS), openssl) +OBJS += ../src/crypto/md5.o +endif + ifdef NEED_MD5 ifdef CONFIG_INTERNAL_MD5 OBJS += ../src/crypto/md5-internal.o @@ -954,7 +961,7 @@ hostapd_cli: $(OBJS_c) $(Q)$(CC) $(LDFLAGS) -o hostapd_cli $(OBJS_c) $(LIBS_c) @$(E) " LD " $@ -NOBJS = nt_password_hash.o ../src/crypto/ms_funcs.o $(SHA1OBJS) ../src/crypto/md5.o +NOBJS = nt_password_hash.o ../src/crypto/ms_funcs.o $(SHA1OBJS) NOBJS += ../src/utils/common.o ifdef NEED_RC4 ifdef CONFIG_INTERNAL_RC4 diff --git a/hostapd/config_file.c b/hostapd/config_file.c index 7cbb46b4..e3cad7c9 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -216,7 +216,7 @@ static int hostapd_config_read_eap_user(const char *fname, FILE *f; char buf[512], *pos, *start, *pos2; int line = 0, ret = 0, num_methods; - struct hostapd_eap_user *user = NULL, *tail = NULL; + struct hostapd_eap_user *user = NULL, *tail = NULL, *new_user = NULL; if (!fname) return 0; @@ -494,7 +494,7 @@ static int hostapd_config_read_eap_user(const char *fname, done: if (tail == NULL) { - tail = conf->eap_user = user; + tail = new_user = user; } else { tail->next = user; tail = user; @@ -510,6 +510,18 @@ static int hostapd_config_read_eap_user(const char *fname, fclose(f); + if (ret == 0) { + user = conf->eap_user; + while (user) { + struct hostapd_eap_user *prev; + + prev = user; + user = user->next; + hostapd_config_free_eap_user(prev); + } + conf->eap_user = new_user; + } + return ret; } #endif /* EAP_SERVER */ diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index b370f21f..1e569598 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -494,9 +494,9 @@ wmm_ac_vo_acm=0 # If set non-zero, require stations to perform scans of overlapping # channels to test for stations which would be affected by 40 MHz traffic. -# This parameter sets the interval in seconds between these scans. This -# is useful only for testing that stations properly set the OBSS interval, -# since the other parameters in the OBSS scan parameters IE are set to 0. +# This parameter sets the interval in seconds between these scans. Setting this +# to non-zero allows 2.4 GHz band AP to move dynamically to a 40 MHz channel if +# no co-existence issues with neighboring devices are found. #obss_interval=0 ##### IEEE 802.11ac related configuration ##################################### diff --git a/hs20/client/Android.mk b/hs20/client/Android.mk index 63cbc6f2..2457e138 100644 --- a/hs20/client/Android.mk +++ b/hs20/client/Android.mk @@ -8,9 +8,12 @@ INCLUDES += external/openssl/include INCLUDES += external/libxml2/include INCLUDES += external/curl/include INCLUDES += external/webkit/Source/WebKit/gtk -ifneq ($(wildcard external/icu),) -INCLUDES += external/icu/icu4c/source/common -else + +# We try to keep this compiling against older platform versions. +# The new icu location (external/icu) exports its own headers, but +# the older versions in external/icu4c don't, and we need to add those +# headers to the include path by hand. +ifeq ($(wildcard external/icu),) INCLUDES += external/icu4c/common endif diff --git a/hs20/server/spp_server.c b/hs20/server/spp_server.c index 4d77d0e8..8a2abf1f 100644 --- a/hs20/server/spp_server.c +++ b/hs20/server/spp_server.c @@ -103,6 +103,28 @@ static void db_update_session_password(struct hs20_svc *ctx, const char *user, } +static void db_update_session_machine_managed(struct hs20_svc *ctx, + const char *user, + const char *realm, + const char *sessionid, + const int pw_mm) +{ + char *sql; + + sql = sqlite3_mprintf("UPDATE sessions SET machine_managed=%Q WHERE id=%Q AND user=%Q AND realm=%Q", + pw_mm ? "1" : "0", sessionid, user, realm); + if (sql == NULL) + return; + debug_print(ctx, 1, "DB: %s", sql); + if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) { + debug_print(ctx, 1, + "Failed to update session machine_managed: %s", + sqlite3_errmsg(ctx->db)); + } + sqlite3_free(sql); +} + + static void db_add_session_pps(struct hs20_svc *ctx, const char *user, const char *realm, const char *sessionid, xml_node_t *node) @@ -1378,6 +1400,11 @@ static xml_node_t * hs20_user_input_registration(struct hs20_svc *ctx, debug_print(ctx, 1, "Request DB subscription registration on success " "notification"); + if (machine_managed) { + db_update_session_password(ctx, user, realm, session_id, pw); + db_update_session_machine_managed(ctx, user, realm, session_id, + machine_managed); + } db_add_session_pps(ctx, user, realm, session_id, pps); hs20_eventlog_node(ctx, user, realm, session_id, diff --git a/src/ap/ap_list.c b/src/ap/ap_list.c index 287d520a..04a56a95 100644 --- a/src/ap/ap_list.c +++ b/src/ap/ap_list.c @@ -111,8 +111,8 @@ static void ap_ap_hash_del(struct hostapd_iface *iface, struct ap_info *ap) if (s->hnext != NULL) s->hnext = s->hnext->hnext; else - printf("AP: could not remove AP " MACSTR " from hash table\n", - MAC2STR(ap->addr)); + wpa_printf(MSG_INFO, "AP: could not remove AP " MACSTR + " from hash table", MAC2STR(ap->addr)); } @@ -182,7 +182,8 @@ void ap_list_process_beacon(struct hostapd_iface *iface, if (!ap) { ap = ap_ap_add(iface, mgmt->bssid); if (!ap) { - printf("Failed to allocate AP information entry\n"); + wpa_printf(MSG_INFO, + "Failed to allocate AP information entry"); return; } new_ap = 1; diff --git a/src/ap/dhcp_snoop.c b/src/ap/dhcp_snoop.c index a7060246..3a77225f 100644 --- a/src/ap/dhcp_snoop.c +++ b/src/ap/dhcp_snoop.c @@ -42,6 +42,17 @@ struct bootp_pkt { static const u8 ic_bootp_cookie[] = { 99, 130, 83, 99 }; +static const char * ipaddr_str(u32 addr) +{ + static char buf[17]; + + os_snprintf(buf, sizeof(buf), "%u.%u.%u.%u", + (addr >> 24) & 0xff, (addr >> 16) & 0xff, + (addr >> 8) & 0xff, addr & 0xff); + return buf; +} + + static void handle_dhcp(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) { @@ -109,16 +120,17 @@ static void handle_dhcp(void *ctx, const u8 *src_addr, const u8 *buf, return; wpa_printf(MSG_DEBUG, "dhcp_snoop: Found DHCPACK for " MACSTR - " @ IPv4 address %X/%d", - MAC2STR(sta->addr), ntohl(b->your_ip), prefixlen); + " @ IPv4 address %s/%d", + MAC2STR(sta->addr), ipaddr_str(ntohl(b->your_ip)), + prefixlen); if (sta->ipaddr == b->your_ip) return; if (sta->ipaddr != 0) { wpa_printf(MSG_DEBUG, - "dhcp_snoop: Removing IPv4 address %X from the ip neigh table", - sta->ipaddr); + "dhcp_snoop: Removing IPv4 address %s from the ip neigh table", + ipaddr_str(be_to_host32(sta->ipaddr))); hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr); } diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c index 3f299f3e..4b0653de 100644 --- a/src/ap/ieee802_11_ht.c +++ b/src/ap/ieee802_11_ht.c @@ -292,7 +292,8 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd, iface->conf->secondary_channel = 0; ieee802_11_set_beacons(iface); } - if (!iface->num_sta_ht40_intolerant) { + if (!iface->num_sta_ht40_intolerant && + iface->conf->obss_interval) { unsigned int delay_time; delay_time = OVERLAPPING_BSS_TRANS_DELAY_FACTOR * iface->conf->obss_interval; diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c index bb432189..eebaa3ce 100644 --- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c @@ -781,13 +781,6 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, if (sta->vlan_id == old_vlanid) return 0; - /* - * During 1x reauth, if the vlan id changes, then remove the old id and - * proceed furthur to add the new one. - */ - if (old_vlanid > 0) - vlan_remove_dynamic(hapd, old_vlanid); - iface = hapd->conf->iface; if (sta->ssid->vlan[0]) iface = sta->ssid->vlan; @@ -815,7 +808,8 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, HOSTAPD_LEVEL_DEBUG, "could not find VLAN for " "binding station to (vlan_id=%d)", sta->vlan_id); - return -1; + ret = -1; + goto done; } else if (sta->vlan_id > 0 && vlan->vlan_id == VLAN_ID_WILDCARD) { vlan = vlan_add_dynamic(hapd, vlan, sta->vlan_id); if (vlan == NULL) { @@ -824,7 +818,8 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, HOSTAPD_LEVEL_DEBUG, "could not add " "dynamic VLAN interface for vlan_id=%d", sta->vlan_id); - return -1; + ret = -1; + goto done; } iface = vlan->ifname; @@ -878,6 +873,12 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, HOSTAPD_LEVEL_DEBUG, "could not bind the STA " "entry to vlan_id=%d", sta->vlan_id); } + +done: + /* During 1x reauth, if the vlan id changes, then remove the old id. */ + if (old_vlanid > 0) + vlan_remove_dynamic(hapd, old_vlanid); + return ret; #else /* CONFIG_NO_VLAN */ return 0; diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c index 4e4a3526..2af2cbce 100644 --- a/src/ap/vlan_init.c +++ b/src/ap/vlan_init.c @@ -891,7 +891,7 @@ struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, struct hostapd_vlan *vlan, int vlan_id) { - struct hostapd_vlan *n; + struct hostapd_vlan *n = NULL; char *ifname, *pos; if (vlan == NULL || vlan_id <= 0 || vlan_id > MAX_VLAN_ID || @@ -904,28 +904,24 @@ struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, if (ifname == NULL) return NULL; pos = os_strchr(ifname, '#'); - if (pos == NULL) { - os_free(ifname); - return NULL; - } + if (pos == NULL) + goto free_ifname; *pos++ = '\0'; n = os_zalloc(sizeof(*n)); - if (n == NULL) { - os_free(ifname); - return NULL; - } + if (n == NULL) + goto free_ifname; n->vlan_id = vlan_id; n->dynamic_vlan = 1; os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", ifname, vlan_id, pos); - os_free(ifname); if (hostapd_vlan_if_add(hapd, n->ifname)) { os_free(n); - return NULL; + n = NULL; + goto free_ifname; } n->next = hapd->conf->vlan; @@ -935,6 +931,8 @@ struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, ifconfig_up(n->ifname); #endif /* CONFIG_FULL_DYNAMIC_VLAN */ +free_ifname: + os_free(ifname); return n; } diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 1905dc94..668cb429 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -1881,7 +1881,10 @@ SM_STATE(WPA_PTK, INITPMK) } else { wpa_printf(MSG_DEBUG, "WPA: Could not get PMK, get_msk: %p", sm->wpa_auth->cb.get_msk); + sm->Disconnect = TRUE; + return; } + os_memset(msk, 0, sizeof(msk)); sm->req_replay_counter_used = 0; /* IEEE 802.11i does not set keyRun to FALSE, but not doing this diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index 97a4537e..2e51935b 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -1034,6 +1034,14 @@ enum p2p_attr_id { P2P_ATTR_OPERATING_CHANNEL = 17, P2P_ATTR_INVITATION_FLAGS = 18, P2P_ATTR_OOB_GO_NEG_CHANNEL = 19, + P2P_ATTR_SERVICE_HASH = 21, + P2P_ATTR_SESSION_INFORMATION_DATA = 22, + P2P_ATTR_CONNECTION_CAPABILITY = 23, + P2P_ATTR_ADVERTISEMENT_ID = 24, + P2P_ATTR_ADVERTISED_SERVICE = 25, + P2P_ATTR_SESSION_ID = 26, + P2P_ATTR_FEATURE_CAPABILITY = 27, + P2P_ATTR_PERSISTENT_GROUP = 28, P2P_ATTR_VENDOR_SPECIFIC = 221 }; @@ -1078,6 +1086,7 @@ enum p2p_status_code { P2P_SC_FAIL_BOTH_GO_INTENT_15 = 9, P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD = 10, P2P_SC_FAIL_REJECTED_BY_USER = 11, + P2P_SC_SUCCESS_DEFERRED = 12, }; enum p2p_role_indication { @@ -1116,6 +1125,7 @@ enum p2p_service_protocol_type { P2P_SERV_UPNP = 2, P2P_SERV_WS_DISCOVERY = 3, P2P_SERV_WIFI_DISPLAY = 4, + P2P_SERV_P2PS = 11, P2P_SERV_VENDOR_SPECIFIC = 255 }; diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h index 59a34126..c8e302a8 100644 --- a/src/common/wpa_ctrl.h +++ b/src/common/wpa_ctrl.h @@ -164,6 +164,7 @@ extern "C" { #define P2P_EVENT_SERV_DISC_REQ "P2P-SERV-DISC-REQ " /* parameters: <src addr> <update indicator> <TLVs> */ #define P2P_EVENT_SERV_DISC_RESP "P2P-SERV-DISC-RESP " +#define P2P_EVENT_SERV_ASP_RESP "P2P-SERV-ASP-RESP " #define P2P_EVENT_INVITATION_RECEIVED "P2P-INVITATION-RECEIVED " #define P2P_EVENT_INVITATION_RESULT "P2P-INVITATION-RESULT " #define P2P_EVENT_FIND_STOPPED "P2P-FIND-STOPPED " @@ -177,6 +178,9 @@ extern "C" { #define ESS_DISASSOC_IMMINENT "ESS-DISASSOC-IMMINENT " #define P2P_EVENT_REMOVE_AND_REFORM_GROUP "P2P-REMOVE-AND-REFORM-GROUP " +#define P2P_EVENT_P2PS_PROVISION_START "P2PS-PROV-START " +#define P2P_EVENT_P2PS_PROVISION_DONE "P2PS-PROV-DONE " + #define INTERWORKING_AP "INTERWORKING-AP " #define INTERWORKING_BLACKLISTED "INTERWORKING-BLACKLISTED " #define INTERWORKING_NO_MATCH "INTERWORKING-NO-MATCH " diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c index f79055cf..f158ef43 100644 --- a/src/crypto/crypto_openssl.c +++ b/src/crypto/crypto_openssl.c @@ -31,17 +31,9 @@ #include "sha384.h" #include "crypto.h" -#if OPENSSL_VERSION_NUMBER < 0x00907000 -#define DES_key_schedule des_key_schedule -#define DES_cblock des_cblock -#define DES_set_key(key, schedule) des_set_key((key), *(schedule)) -#define DES_ecb_encrypt(input, output, ks, enc) \ - des_ecb_encrypt((input), (output), *(ks), (enc)) -#endif /* openssl < 0.9.7 */ - static BIGNUM * get_group5_prime(void) { -#if OPENSSL_VERSION_NUMBER < 0x00908000 || defined(OPENSSL_IS_BORINGSSL) +#ifdef OPENSSL_IS_BORINGSSL static const unsigned char RFC3526_PRIME_1536[] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0x0F,0xDA,0xA2, 0x21,0x68,0xC2,0x34,0xC4,0xC6,0x62,0x8B,0x80,0xDC,0x1C,0xD1, @@ -61,20 +53,11 @@ static BIGNUM * get_group5_prime(void) 0xCA,0x23,0x73,0x27,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, }; return BN_bin2bn(RFC3526_PRIME_1536, sizeof(RFC3526_PRIME_1536), NULL); -#else /* openssl < 0.9.8 */ +#else /* OPENSSL_IS_BORINGSSL */ return get_rfc3526_prime_1536(NULL); -#endif /* openssl < 0.9.8 */ +#endif /* OPENSSL_IS_BORINGSSL */ } -#if OPENSSL_VERSION_NUMBER < 0x00908000 -#ifndef OPENSSL_NO_SHA256 -#ifndef OPENSSL_FIPS -#define NO_SHA256_WRAPPER -#endif -#endif - -#endif /* openssl < 0.9.8 */ - #ifdef OPENSSL_NO_SHA256 #define NO_SHA256_WRAPPER #endif @@ -314,6 +297,33 @@ void aes_decrypt_deinit(void *ctx) } +int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher) +{ + AES_KEY actx; + int res; + + if (AES_set_encrypt_key(kek, kek_len << 3, &actx)) + return -1; + res = AES_wrap_key(&actx, NULL, cipher, plain, n * 8); + OPENSSL_cleanse(&actx, sizeof(actx)); + return res <= 0 ? -1 : 0; +} + + +int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher, + u8 *plain) +{ + AES_KEY actx; + int res; + + if (AES_set_decrypt_key(kek, kek_len << 3, &actx)) + return -1; + res = AES_unwrap_key(&actx, NULL, plain, cipher, (n + 1) * 8); + OPENSSL_cleanse(&actx, sizeof(actx)); + return res <= 0 ? -1 : 0; +} + + int crypto_mod_exp(const u8 *base, size_t base_len, const u8 *power, size_t power_len, const u8 *modulus, size_t modulus_len, @@ -688,43 +698,26 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) } -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, iterations, buflen, buf) != 1) - return -1; -#else /* openssl < 0.9.8 */ - if (PKCS5_PBKDF2_HMAC_SHA1(passphrase, os_strlen(passphrase), ssid, - ssid_len, iterations, 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) +static int openssl_hmac_vector(const EVP_MD *type, const u8 *key, + size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac, + unsigned int mdlen) { 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); + HMAC_Init_ex(&ctx, key, key_len, type, NULL); #else /* openssl < 0.9.9 */ - if (HMAC_Init_ex(&ctx, key, key_len, EVP_sha1(), NULL) != 1) + if (HMAC_Init_ex(&ctx, key, key_len, type, 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; @@ -737,6 +730,43 @@ int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, } +#ifndef CONFIG_FIPS + +int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return openssl_hmac_vector(EVP_md5(), key ,key_len, num_elem, addr, len, + mac, 16); +} + + +int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_FIPS */ + + +int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len, + int iterations, u8 *buf, size_t buflen) +{ + if (PKCS5_PBKDF2_HMAC_SHA1(passphrase, os_strlen(passphrase), ssid, + ssid_len, iterations, buflen, buf) != 1) + return -1; + 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) +{ + return openssl_hmac_vector(EVP_sha1(), key, key_len, num_elem, addr, + len, mac, 20); +} + + int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *mac) { @@ -749,32 +779,8 @@ int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, 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; + return openssl_hmac_vector(EVP_sha256(), key, key_len, num_elem, addr, + len, mac, 32); } @@ -792,23 +798,8 @@ int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, int hmac_sha384_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 (HMAC_Init_ex(&ctx, key, key_len, EVP_sha384(), NULL) != 1) - return -1; - - for (i = 0; i < num_elem; i++) - HMAC_Update(&ctx, addr[i], len[i]); - - mdlen = 32; - res = HMAC_Final(&ctx, mac, &mdlen); - HMAC_CTX_cleanup(&ctx); - - return res == 1 ? 0 : -1; + return openssl_hmac_vector(EVP_sha384(), key, key_len, num_elem, addr, + len, mac, 32); } diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c index e3ca0682..d8c8c56b 100644 --- a/src/crypto/tls_openssl.c +++ b/src/crypto/tls_openssl.c @@ -28,12 +28,6 @@ #include "crypto.h" #include "tls.h" -#if OPENSSL_VERSION_NUMBER >= 0x0090800fL -#define OPENSSL_d2i_TYPE const unsigned char ** -#else -#define OPENSSL_d2i_TYPE unsigned char ** -#endif - #if defined(SSL_CTX_get_app_data) && defined(SSL_CTX_set_app_data) #define OPENSSL_SUPPORTS_CTX_APP_DATA #endif @@ -90,6 +84,7 @@ static struct tls_context *tls_global = NULL; struct tls_connection { struct tls_context *context; + SSL_CTX *ssl_ctx; SSL *ssl; BIO *ssl_in, *ssl_out; #ifndef OPENSSL_NO_ENGINE @@ -400,7 +395,8 @@ static int tls_cryptoapi_cert(SSL *ssl, const char *name) goto err; } - cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &priv->cert->pbCertEncoded, + cert = d2i_X509(NULL, + (const unsigned char **) &priv->cert->pbCertEncoded, priv->cert->cbCertEncoded); if (cert == NULL) { wpa_printf(MSG_INFO, "CryptoAPI: Could not process X509 DER " @@ -500,7 +496,8 @@ static int tls_cryptoapi_ca_cert(SSL_CTX *ssl_ctx, SSL *ssl, const char *name) } while ((ctx = CertEnumCertificatesInStore(cs, ctx))) { - cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &ctx->pbCertEncoded, + cert = d2i_X509(NULL, + (const unsigned char **) &ctx->pbCertEncoded, ctx->cbCertEncoded); if (cert == NULL) { wpa_printf(MSG_INFO, "CryptoAPI: Could not process " @@ -774,7 +771,7 @@ void * tls_init(const struct tls_config *conf) #endif /* CONFIG_FIPS */ SSL_load_error_strings(); SSL_library_init(); -#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256) +#ifndef OPENSSL_NO_SHA256 EVP_add_digest(EVP_sha256()); #endif /* OPENSSL_NO_SHA256 */ /* TODO: if /dev/urandom is available, PRNG is seeded @@ -1045,6 +1042,7 @@ struct tls_connection * tls_connection_init(void *ssl_ctx) conn = os_zalloc(sizeof(*conn)); if (conn == NULL) return NULL; + conn->ssl_ctx = ssl_ctx; conn->ssl = SSL_new(ssl); if (conn->ssl == NULL) { tls_show_errors(MSG_INFO, __func__, @@ -1613,7 +1611,7 @@ static int tls_load_ca_der(void *_ssl_ctx, const char *ca_cert) X509_LOOKUP *lookup; int ret = 0; - lookup = X509_STORE_add_lookup(ssl_ctx->cert_store, + lookup = X509_STORE_add_lookup(SSL_CTX_get_cert_store(ssl_ctx), X509_LOOKUP_file()); if (lookup == NULL) { tls_show_errors(MSG_WARNING, __func__, @@ -1644,18 +1642,19 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, size_t ca_cert_blob_len, const char *ca_path) { SSL_CTX *ssl_ctx = _ssl_ctx; + X509_STORE *store; /* * Remove previously configured trusted CA certificates before adding * new ones. */ - X509_STORE_free(ssl_ctx->cert_store); - ssl_ctx->cert_store = X509_STORE_new(); - if (ssl_ctx->cert_store == NULL) { + store = X509_STORE_new(); + if (store == NULL) { wpa_printf(MSG_DEBUG, "OpenSSL: %s - failed to allocate new " "certificate store", __func__); return -1; } + SSL_CTX_set_cert_store(ssl_ctx, store); SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); conn->ca_cert_verify = 1; @@ -1699,7 +1698,8 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, } if (ca_cert_blob) { - X509 *cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &ca_cert_blob, + X509 *cert = d2i_X509(NULL, + (const unsigned char **) &ca_cert_blob, ca_cert_blob_len); if (cert == NULL) { tls_show_errors(MSG_WARNING, __func__, @@ -1707,7 +1707,8 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, return -1; } - if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) { + if (!X509_STORE_add_cert(SSL_CTX_get_cert_store(ssl_ctx), + cert)) { unsigned long err = ERR_peek_error(); tls_show_errors(MSG_WARNING, __func__, "Failed to add ca_cert_blob to " @@ -2138,7 +2139,7 @@ static int tls_read_pkcs12_blob(SSL_CTX *ssl_ctx, SSL *ssl, #ifdef PKCS12_FUNCS PKCS12 *p12; - p12 = d2i_PKCS12(NULL, (OPENSSL_d2i_TYPE) &blob, len); + p12 = d2i_PKCS12(NULL, (const unsigned char **) &blob, len); if (p12 == NULL) { tls_show_errors(MSG_INFO, __func__, "Failed to use PKCS#12 blob"); @@ -2219,20 +2220,21 @@ static int tls_connection_engine_ca_cert(void *_ssl_ctx, #ifndef OPENSSL_NO_ENGINE X509 *cert; SSL_CTX *ssl_ctx = _ssl_ctx; + X509_STORE *store; if (tls_engine_get_cert(conn, ca_cert_id, &cert)) return -1; /* start off the same as tls_connection_ca_cert */ - X509_STORE_free(ssl_ctx->cert_store); - ssl_ctx->cert_store = X509_STORE_new(); - if (ssl_ctx->cert_store == NULL) { + store = X509_STORE_new(); + if (store == NULL) { wpa_printf(MSG_DEBUG, "OpenSSL: %s - failed to allocate new " "certificate store", __func__); X509_free(cert); return -1; } - if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) { + SSL_CTX_set_cert_store(ssl_ctx, store); + if (!X509_STORE_add_cert(store, cert)) { unsigned long err = ERR_peek_error(); tls_show_errors(MSG_WARNING, __func__, "Failed to add CA certificate from engine " @@ -2900,7 +2902,11 @@ struct wpabuf * tls_connection_decrypt(void *tls_ctx, int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn) { +#if OPENSSL_VERSION_NUMBER >= 0x10001000L + return conn ? SSL_cache_hit(conn->ssl) : 0; +#else return conn ? conn->ssl->hit : 0; +#endif } @@ -3141,7 +3147,7 @@ static int ocsp_resp_cb(SSL *s, void *arg) return 0; } - store = SSL_CTX_get_cert_store(s->ctx); + store = SSL_CTX_get_cert_store(conn->ssl_ctx); if (conn->peer_issuer) { debug_print_cert(conn->peer_issuer, "Add OCSP issuer"); diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c index 62cd4a18..35433f3b 100644 --- a/src/eap_peer/eap.c +++ b/src/eap_peer/eap.c @@ -934,6 +934,15 @@ static int eap_peer_req_is_duplicate(struct eap_sm *sm) } +static int eap_peer_sm_allow_canned(struct eap_sm *sm) +{ + struct eap_peer_config *config = eap_get_config(sm); + + return config && config->phase1 && + os_strstr(config->phase1, "allow_canned_success=1"); +} + + static void eap_peer_sm_step_received(struct eap_sm *sm) { int duplicate = eap_peer_req_is_duplicate(sm); @@ -947,6 +956,17 @@ static void eap_peer_sm_step_received(struct eap_sm *sm) (sm->reqId == sm->lastId || eap_success_workaround(sm, sm->reqId, sm->lastId))) SM_ENTER(EAP, SUCCESS); + else if (sm->workaround && sm->lastId == -1 && sm->rxSuccess && + !sm->rxFailure && !sm->rxReq && eap_peer_sm_allow_canned(sm)) + SM_ENTER(EAP, SUCCESS); /* EAP-Success prior any EAP method */ + else if (sm->workaround && sm->lastId == -1 && sm->rxFailure && + !sm->rxReq && sm->methodState != METHOD_CONT && + eap_peer_sm_allow_canned(sm)) + SM_ENTER(EAP, FAILURE); /* EAP-Failure prior any EAP method */ + else if (sm->workaround && sm->rxSuccess && !sm->rxFailure && + !sm->rxReq && sm->methodState != METHOD_CONT && + eap_peer_sm_allow_canned(sm)) + SM_ENTER(EAP, SUCCESS); /* EAP-Success after Identity */ else if (sm->methodState != METHOD_CONT && ((sm->rxFailure && sm->decision != DECISION_UNCOND_SUCC) || diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h index 903412de..2b1a1d5e 100644 --- a/src/eap_peer/eap_config.h +++ b/src/eap_peer/eap_config.h @@ -418,6 +418,16 @@ struct eap_peer_config { * * EAP-WSC (WPS) uses following options: pin=Device_Password and * uuid=Device_UUID + * + * For wired IEEE 802.1X authentication, "allow_canned_success=1" can be + * used to configure a mode that allows EAP-Success (and EAP-Failure) + * without going through authentication step. Some switches use such + * sequence when forcing the port to be authorized/unauthorized or as a + * fallback option if the authentication server is unreachable. By + * default, wpa_supplicant discards such frames to protect against + * potential attacks by rogue devices, but this option can be used to + * disable that protection for cases where the server/authenticator does + * not need to be authenticated. */ char *phase1; @@ -425,7 +435,9 @@ struct eap_peer_config { * 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. + * "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS. "mschapv2_retry=0" can + * be used to disable MSCHAPv2 password retry in authentication failure + * cases. */ char *phase2; diff --git a/src/eap_peer/eap_mschapv2.c b/src/eap_peer/eap_mschapv2.c index 430c501b..9e486e7d 100644 --- a/src/eap_peer/eap_mschapv2.c +++ b/src/eap_peer/eap_mschapv2.c @@ -472,6 +472,13 @@ static int eap_mschapv2_failure_txt(struct eap_sm *sm, pos += 2; msg = pos; } + if (data->prev_error == ERROR_AUTHENTICATION_FAILURE && retry && + config && config->phase2 && + os_strstr(config->phase2, "mschapv2_retry=0")) { + wpa_printf(MSG_DEBUG, + "EAP-MSCHAPV2: mark password retry disabled based on local configuration"); + retry = 0; + } wpa_msg(sm->msg_ctx, MSG_WARNING, "EAP-MSCHAPV2: failure message: '%s' (retry %sallowed, error " "%d)", diff --git a/src/eap_peer/eap_vendor_test.c b/src/eap_peer/eap_vendor_test.c index 040d1e7f..b61057ee 100644 --- a/src/eap_peer/eap_vendor_test.c +++ b/src/eap_peer/eap_vendor_test.c @@ -1,6 +1,6 @@ /* * EAP peer method: Test method for vendor specific (expanded) EAP type - * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2005-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -14,31 +14,36 @@ #include "common.h" #include "eap_i.h" -#ifdef TEST_PENDING_REQUEST #include "eloop.h" -#endif /* TEST_PENDING_REQUEST */ #define EAP_VENDOR_ID EAP_VENDOR_HOSTAP #define EAP_VENDOR_TYPE 0xfcfbfaf9 -/* #define TEST_PENDING_REQUEST */ - struct eap_vendor_test_data { enum { INIT, CONFIRM, SUCCESS } state; int first_try; + int test_pending_req; }; static void * eap_vendor_test_init(struct eap_sm *sm) { struct eap_vendor_test_data *data; + const u8 *password; + size_t password_len; + data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->state = INIT; data->first_try = 1; + + password = eap_get_config_password(sm, &password_len); + data->test_pending_req = password && password_len == 7 && + os_memcmp(password, "pending", 7) == 0; + return data; } @@ -50,7 +55,6 @@ static void eap_vendor_test_deinit(struct eap_sm *sm, void *priv) } -#ifdef TEST_PENDING_REQUEST static void eap_vendor_ready(void *eloop_ctx, void *timeout_ctx) { struct eap_sm *sm = eloop_ctx; @@ -58,7 +62,6 @@ static void eap_vendor_ready(void *eloop_ctx, void *timeout_ctx) "request"); eap_notify_pending(sm); } -#endif /* TEST_PENDING_REQUEST */ static struct wpabuf * eap_vendor_test_process(struct eap_sm *sm, void *priv, @@ -98,8 +101,7 @@ static struct wpabuf * eap_vendor_test_process(struct eap_sm *sm, void *priv, } if (data->state == CONFIRM) { -#ifdef TEST_PENDING_REQUEST - if (data->first_try) { + if (data->test_pending_req && data->first_try) { data->first_try = 0; wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Testing " "pending request"); @@ -108,7 +110,6 @@ static struct wpabuf * eap_vendor_test_process(struct eap_sm *sm, void *priv, NULL); return NULL; } -#endif /* TEST_PENDING_REQUEST */ } ret->ignore = FALSE; diff --git a/src/eap_server/eap_server_peap.c b/src/eap_server/eap_server_peap.c index 98d608bf..faa0fd2f 100644 --- a/src/eap_server/eap_server_peap.c +++ b/src/eap_server/eap_server_peap.c @@ -344,12 +344,14 @@ static struct wpabuf * eap_peap_build_phase2_tlv(struct eap_sm *sm, size_t mlen; mlen = 6; /* Result TLV */ - if (data->crypto_binding != NO_BINDING) + if (data->peap_version == 0 && data->tlv_request == TLV_REQ_SUCCESS && + data->crypto_binding != NO_BINDING) { mlen += 60; /* Cryptobinding TLV */ #ifdef EAP_SERVER_TNC - if (data->soh_response) - mlen += wpabuf_len(data->soh_response); + if (data->soh_response) + mlen += wpabuf_len(data->soh_response); #endif /* EAP_SERVER_TNC */ + } buf = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, mlen, EAP_CODE_REQUEST, id); diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c index 621318ee..f6150512 100644 --- a/src/eapol_supp/eapol_supp_sm.c +++ b/src/eapol_supp/eapol_supp_sm.c @@ -1634,21 +1634,15 @@ void eapol_sm_notify_cached(struct eapol_sm *sm) /** * eapol_sm_notify_pmkid_attempt - Notification of PMKSA caching * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() - * @attempt: Whether PMKSA caching is tried * - * Notify EAPOL state machines whether PMKSA caching is used. + * Notify EAPOL state machines if PMKSA caching is used. */ -void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm, int attempt) +void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm) { if (sm == NULL) return; - if (attempt) { - wpa_printf(MSG_DEBUG, "RSN: Trying to use cached PMKSA"); - sm->cached_pmk = TRUE; - } else { - wpa_printf(MSG_DEBUG, "RSN: Do not try to use cached PMKSA"); - sm->cached_pmk = FALSE; - } + wpa_printf(MSG_DEBUG, "RSN: Trying to use cached PMKSA"); + sm->cached_pmk = TRUE; } diff --git a/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h index d8ae9d4e..03341a30 100644 --- a/src/eapol_supp/eapol_supp_sm.h +++ b/src/eapol_supp/eapol_supp_sm.h @@ -307,7 +307,7 @@ int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len); const u8 * eapol_sm_get_session_id(struct eapol_sm *sm, size_t *len); void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff); void eapol_sm_notify_cached(struct eapol_sm *sm); -void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm, int attempt); +void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm); void eapol_sm_register_scard_ctx(struct eapol_sm *sm, void *ctx); void eapol_sm_notify_portControl(struct eapol_sm *sm, PortControl portControl); void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm); @@ -380,13 +380,20 @@ static inline int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len) { return -1; } +static inline const u8 * +eapol_sm_get_session_id(struct eapol_sm *sm, size_t *len) +{ + return NULL; +} static inline void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff) { } static inline void eapol_sm_notify_cached(struct eapol_sm *sm) { } -#define eapol_sm_notify_pmkid_attempt(sm, attempt) do { } while (0) +static inline void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm) +{ +} #define eapol_sm_register_scard_ctx(sm, ctx) do { } while (0) static inline void eapol_sm_notify_portControl(struct eapol_sm *sm, PortControl portControl) diff --git a/src/l2_packet/l2_packet.h b/src/l2_packet/l2_packet.h index 7537f93e..2a452458 100644 --- a/src/l2_packet/l2_packet.h +++ b/src/l2_packet/l2_packet.h @@ -68,6 +68,19 @@ struct l2_packet_data * l2_packet_init( void *rx_callback_ctx, int l2_hdr); /** + * l2_packet_init_bridge - Like l2_packet_init() but with bridge workaround + * + * This version of l2_packet_init() can be used to enable a workaround for Linux + * packet socket in case of a station interface in a bridge. + */ +struct l2_packet_data * l2_packet_init_bridge( + const char *br_ifname, const char *ifname, const u8 *own_addr, + unsigned short protocol, + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + void *rx_callback_ctx, int l2_hdr); + +/** * l2_packet_deinit - Deinitialize l2_packet interface * @l2: Pointer to internal l2_packet data from l2_packet_init() */ diff --git a/src/l2_packet/l2_packet_freebsd.c b/src/l2_packet/l2_packet_freebsd.c index d87c32b2..aa836482 100644 --- a/src/l2_packet/l2_packet_freebsd.c +++ b/src/l2_packet/l2_packet_freebsd.c @@ -256,6 +256,18 @@ struct l2_packet_data * l2_packet_init( } +struct l2_packet_data * l2_packet_init_bridge( + const char *br_ifname, const char *ifname, const u8 *own_addr, + unsigned short protocol, + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + void *rx_callback_ctx, int l2_hdr) +{ + return l2_packet_init(br_ifname, own_addr, protocol, rx_callback, + rx_callback_ctx, l2_hdr); +} + + void l2_packet_deinit(struct l2_packet_data *l2) { if (l2 != NULL) { diff --git a/src/l2_packet/l2_packet_linux.c b/src/l2_packet/l2_packet_linux.c index 89ff7db5..68b20089 100644 --- a/src/l2_packet/l2_packet_linux.c +++ b/src/l2_packet/l2_packet_linux.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - Layer2 packet handling with Linux packet sockets - * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -27,6 +27,9 @@ struct l2_packet_data { void *rx_callback_ctx; int l2_hdr; /* whether to include layer 2 (Ethernet) header data * buffers */ + + /* For working around Linux packet socket behavior and regression. */ + int fd_br_rx; }; /* Generated by 'sudo tcpdump -s 3000 -dd greater 278 and ip and udp and @@ -130,6 +133,36 @@ static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx) } l2->rx_callback(l2->rx_callback_ctx, ll.sll_addr, buf, res); + + if (l2->fd_br_rx >= 0) { + wpa_printf(MSG_DEBUG, "l2_packet_receive: Main packet socket for %s seems to have working RX - close workaround bridge socket", + l2->ifname); + eloop_unregister_read_sock(l2->fd_br_rx); + close(l2->fd_br_rx); + l2->fd_br_rx = -1; + } +} + + +static void l2_packet_receive_br(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct l2_packet_data *l2 = eloop_ctx; + u8 buf[2300]; + int res; + struct sockaddr_ll ll; + socklen_t fromlen; + + os_memset(&ll, 0, sizeof(ll)); + fromlen = sizeof(ll); + res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &ll, + &fromlen); + if (res < 0) { + wpa_printf(MSG_DEBUG, "l2_packet_receive_br - recvfrom: %s", + strerror(errno)); + return; + } + + l2->rx_callback(l2->rx_callback_ctx, ll.sll_addr, buf, res); } @@ -150,6 +183,7 @@ struct l2_packet_data * l2_packet_init( l2->rx_callback = rx_callback; l2->rx_callback_ctx = rx_callback_ctx; l2->l2_hdr = l2_hdr; + l2->fd_br_rx = -1; l2->fd = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM, htons(protocol)); @@ -197,6 +231,87 @@ struct l2_packet_data * l2_packet_init( } +struct l2_packet_data * l2_packet_init_bridge( + const char *br_ifname, const char *ifname, const u8 *own_addr, + unsigned short protocol, + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + void *rx_callback_ctx, int l2_hdr) +{ + struct l2_packet_data *l2; + struct sock_filter ethertype_sock_filter_insns[] = { + /* Load ethertype */ + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 2 * ETH_ALEN), + /* Jump over next statement if ethertype does not match */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, protocol, 0, 1), + /* Ethertype match - return all */ + BPF_STMT(BPF_RET | BPF_K, ~0), + /* No match - drop */ + BPF_STMT(BPF_RET | BPF_K, 0) + }; + const struct sock_fprog ethertype_sock_filter = { + .len = ARRAY_SIZE(ethertype_sock_filter_insns), + .filter = ethertype_sock_filter_insns, + }; + struct sockaddr_ll ll; + + l2 = l2_packet_init(br_ifname, own_addr, protocol, rx_callback, + rx_callback_ctx, l2_hdr); + if (!l2) + return NULL; + + /* + * The Linux packet socket behavior has changed over the years and there + * is an inconvenient regression in it that breaks RX for a specific + * protocol from interfaces in a bridge when that interface is not in + * fully operation state (i.e., when in station mode and not completed + * authorization). To work around this, register ETH_P_ALL version of + * the packet socket bound to the real netdev and use socket filter to + * match the ethertype value. This version is less efficient, but + * required for functionality with many kernel version. If the main + * packet socket is found to be working, this less efficient version + * gets closed automatically. + */ + + l2->fd_br_rx = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM, + htons(ETH_P_ALL)); + if (l2->fd_br_rx < 0) { + wpa_printf(MSG_DEBUG, "%s: socket(PF_PACKET-fd_br_rx): %s", + __func__, strerror(errno)); + /* try to continue without the workaround RX socket */ + return l2; + } + + os_memset(&ll, 0, sizeof(ll)); + ll.sll_family = PF_PACKET; + ll.sll_ifindex = if_nametoindex(ifname); + ll.sll_protocol = htons(ETH_P_ALL); + if (bind(l2->fd_br_rx, (struct sockaddr *) &ll, sizeof(ll)) < 0) { + wpa_printf(MSG_DEBUG, "%s: bind[PF_PACKET-fd_br_rx]: %s", + __func__, strerror(errno)); + /* try to continue without the workaround RX socket */ + close(l2->fd_br_rx); + l2->fd_br_rx = -1; + return l2; + } + + if (setsockopt(l2->fd_br_rx, SOL_SOCKET, SO_ATTACH_FILTER, + ðertype_sock_filter, sizeof(struct sock_fprog))) { + wpa_printf(MSG_DEBUG, + "l2_packet_linux: setsockopt(SO_ATTACH_FILTER) failed: %s", + strerror(errno)); + /* try to continue without the workaround RX socket */ + close(l2->fd_br_rx); + l2->fd_br_rx = -1; + return l2; + } + + eloop_register_read_sock(l2->fd_br_rx, l2_packet_receive_br, l2, NULL); + + return l2; +} + + void l2_packet_deinit(struct l2_packet_data *l2) { if (l2 == NULL) @@ -206,7 +321,12 @@ void l2_packet_deinit(struct l2_packet_data *l2) eloop_unregister_read_sock(l2->fd); close(l2->fd); } - + + if (l2->fd_br_rx >= 0) { + eloop_unregister_read_sock(l2->fd_br_rx); + close(l2->fd_br_rx); + } + os_free(l2); } diff --git a/src/l2_packet/l2_packet_ndis.c b/src/l2_packet/l2_packet_ndis.c index 39a62a0a..71677816 100644 --- a/src/l2_packet/l2_packet_ndis.c +++ b/src/l2_packet/l2_packet_ndis.c @@ -450,6 +450,18 @@ struct l2_packet_data * l2_packet_init( } +struct l2_packet_data * l2_packet_init_bridge( + const char *br_ifname, const char *ifname, const u8 *own_addr, + unsigned short protocol, + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + void *rx_callback_ctx, int l2_hdr) +{ + return l2_packet_init(br_ifname, own_addr, protocol, rx_callback, + rx_callback_ctx, l2_hdr); +} + + void l2_packet_deinit(struct l2_packet_data *l2) { if (l2 == NULL) diff --git a/src/l2_packet/l2_packet_none.c b/src/l2_packet/l2_packet_none.c index 0501925c..307fc6da 100644 --- a/src/l2_packet/l2_packet_none.c +++ b/src/l2_packet/l2_packet_none.c @@ -91,6 +91,18 @@ struct l2_packet_data * l2_packet_init( } +struct l2_packet_data * l2_packet_init_bridge( + const char *br_ifname, const char *ifname, const u8 *own_addr, + unsigned short protocol, + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + void *rx_callback_ctx, int l2_hdr) +{ + return l2_packet_init(br_ifname, own_addr, protocol, rx_callback, + rx_callback_ctx, l2_hdr); +} + + void l2_packet_deinit(struct l2_packet_data *l2) { if (l2 == NULL) diff --git a/src/l2_packet/l2_packet_privsep.c b/src/l2_packet/l2_packet_privsep.c index 76dcccc7..e26ca20a 100644 --- a/src/l2_packet/l2_packet_privsep.c +++ b/src/l2_packet/l2_packet_privsep.c @@ -231,6 +231,18 @@ fail: } +struct l2_packet_data * l2_packet_init_bridge( + const char *br_ifname, const char *ifname, const u8 *own_addr, + unsigned short protocol, + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + void *rx_callback_ctx, int l2_hdr) +{ + return l2_packet_init(br_ifname, own_addr, protocol, rx_callback, + rx_callback_ctx, l2_hdr); +} + + void l2_packet_deinit(struct l2_packet_data *l2) { if (l2 == NULL) diff --git a/src/l2_packet/l2_packet_winpcap.c b/src/l2_packet/l2_packet_winpcap.c index b6e50889..74085a31 100644 --- a/src/l2_packet/l2_packet_winpcap.c +++ b/src/l2_packet/l2_packet_winpcap.c @@ -248,6 +248,18 @@ struct l2_packet_data * l2_packet_init( } +struct l2_packet_data * l2_packet_init_bridge( + const char *br_ifname, const char *ifname, const u8 *own_addr, + unsigned short protocol, + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + void *rx_callback_ctx, int l2_hdr) +{ + return l2_packet_init(br_ifname, own_addr, protocol, rx_callback, + rx_callback_ctx, l2_hdr); +} + + static void l2_packet_deinit_timeout(void *eloop_ctx, void *timeout_ctx) { struct l2_packet_data *l2 = eloop_ctx; diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c index f3e31a80..602aa366 100644 --- a/src/p2p/p2p.c +++ b/src/p2p/p2p.c @@ -13,6 +13,8 @@ #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "common/wpa_ctrl.h" +#include "crypto/sha256.h" +#include "crypto/crypto.h" #include "wps/wps_i.h" #include "p2p_i.h" #include "p2p.h" @@ -151,6 +153,19 @@ const char * p2p_get_state_txt(struct p2p_data *p2p) } +struct p2ps_advertisement * p2p_get_p2ps_adv_list(struct p2p_data *p2p) +{ + return p2p ? p2p->p2ps_adv_list : NULL; +} + + +void p2p_set_intended_addr(struct p2p_data *p2p, const u8 *intended_addr) +{ + if (p2p && intended_addr) + os_memcpy(p2p->intended_addr, intended_addr, ETH_ALEN); +} + + u16 p2p_get_provisioning_info(struct p2p_data *p2p, const u8 *addr) { struct p2p_device *dev = NULL; @@ -770,6 +785,12 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, dev->oper_ssid_len = msg.ssid[1]; } + if (msg.adv_service_instance && msg.adv_service_instance_len) { + wpabuf_free(dev->info.p2ps_instance); + dev->info.p2ps_instance = wpabuf_alloc_copy( + msg.adv_service_instance, msg.adv_service_instance_len); + } + if (freq >= 2412 && freq <= 2484 && msg.ds_params && *msg.ds_params >= 1 && *msg.ds_params <= 14) { int ds_freq; @@ -829,7 +850,9 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, p2p_update_peer_vendor_elems(dev, ies, ies_len); - if (dev->flags & P2P_DEV_REPORTED && !wfd_changed) + if (dev->flags & P2P_DEV_REPORTED && !wfd_changed && + (!msg.adv_service_instance || + (dev->flags & P2P_DEV_P2PS_REPORTED))) return 0; p2p_dbg(p2p, "Peer found with Listen frequency %d MHz (rx_time=%u.%06u)", @@ -865,6 +888,9 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, !(dev->flags & P2P_DEV_REPORTED_ONCE)); dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE; + if (msg.adv_service_instance) + dev->flags |= P2P_DEV_P2PS_REPORTED; + return 0; } @@ -899,6 +925,7 @@ static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev) wpabuf_free(dev->info.wfd_subelems); wpabuf_free(dev->info.vendor_elems); wpabuf_free(dev->go_neg_conf); + wpabuf_free(dev->info.p2ps_instance); os_free(dev); } @@ -1083,10 +1110,44 @@ static void p2p_free_req_dev_types(struct p2p_data *p2p) } +static int p2ps_gen_hash(struct p2p_data *p2p, const char *str, u8 *hash) +{ + u8 buf[SHA256_MAC_LEN]; + char str_buf[256]; + const u8 *adv_array; + size_t i, adv_len; + + if (!str || !hash) + return 0; + + if (!str[0]) { + os_memcpy(hash, p2p->wild_card_hash, P2PS_HASH_LEN); + return 1; + } + + adv_array = (u8 *) str_buf; + adv_len = os_strlen(str); + + for (i = 0; str[i] && i < adv_len; i++) { + if (str[i] >= 'A' && str[i] <= 'Z') + str_buf[i] = str[i] - 'A' + 'a'; + else + str_buf[i] = str[i]; + } + + if (sha256_vector(1, &adv_array, &adv_len, buf)) + return 0; + + os_memcpy(hash, buf, P2PS_HASH_LEN); + return 1; +} + + 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, unsigned int search_delay) + const u8 *dev_id, unsigned int search_delay, + u8 seek_count, const char **seek) { int res; @@ -1113,6 +1174,47 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, } else p2p->find_dev_id = NULL; + if (seek_count == 0 || !seek) { + /* Not an ASP search */ + p2p->p2ps_seek = 0; + } else if (seek_count == 1 && seek && (!seek[0] || !seek[0][0])) { + /* + * An empty seek string means no hash values, but still an ASP + * search. + */ + p2p->p2ps_seek_count = 0; + p2p->p2ps_seek = 1; + } else if (seek && seek_count <= P2P_MAX_QUERY_HASH) { + u8 buf[P2PS_HASH_LEN]; + int i; + + p2p->p2ps_seek_count = seek_count; + for (i = 0; i < seek_count; i++) { + if (!p2ps_gen_hash(p2p, seek[i], buf)) + continue; + + /* If asking for wildcard, don't do others */ + if (os_memcmp(buf, p2p->wild_card_hash, + P2PS_HASH_LEN) == 0) { + p2p->p2ps_seek_count = 0; + break; + } + + os_memcpy(&p2p->query_hash[i * P2PS_HASH_LEN], buf, + P2PS_HASH_LEN); + } + p2p->p2ps_seek = 1; + } else { + p2p->p2ps_seek_count = 0; + p2p->p2ps_seek = 1; + } + + /* Special case to perform wildcard search */ + if (p2p->p2ps_seek_count == 0 && p2p->p2ps_seek) { + p2p->p2ps_seek_count = 1; + os_memcpy(&p2p->query_hash, p2p->wild_card_hash, P2PS_HASH_LEN); + } + p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING; p2p_clear_timeout(p2p); p2p->cfg->stop_listen(p2p->cfg->cb_ctx); @@ -1163,8 +1265,11 @@ void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq) p2p_dbg(p2p, "Stopping find"); eloop_cancel_timeout(p2p_find_timeout, p2p, NULL); p2p_clear_timeout(p2p); - if (p2p->state == P2P_SEARCH) + if (p2p->state == P2P_SEARCH || p2p->state == P2P_SD_DURING_FIND) p2p->cfg->find_stopped(p2p->cfg->cb_ctx); + + p2p->p2ps_seek_count = 0; + p2p_set_state(p2p, P2P_IDLE); p2p_free_req_dev_types(p2p); p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING; @@ -1637,7 +1742,14 @@ void p2p_build_ssid(struct p2p_data *p2p, u8 *ssid, size_t *ssid_len) int p2p_go_params(struct p2p_data *p2p, struct p2p_go_neg_results *params) { - p2p_build_ssid(p2p, params->ssid, ¶ms->ssid_len); + if (p2p->ssid_set) { + os_memcpy(params->ssid, p2p->ssid, p2p->ssid_len); + params->ssid_len = p2p->ssid_len; + } else { + p2p_build_ssid(p2p, params->ssid, ¶ms->ssid_len); + } + p2p->ssid_set = 0; + p2p_random(params->passphrase, p2p->cfg->passphrase_len); return 0; } @@ -2036,6 +2148,9 @@ struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p) if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P]) extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P]); + if (p2p->query_count) + extra += MAX_SVC_ADV_IE_LEN; + buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; @@ -2070,10 +2185,38 @@ struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p) p2p_buf_add_device_info(buf, p2p, NULL); p2p_buf_update_ie_hdr(buf, len); + if (p2p->query_count) { + p2p_buf_add_service_instance(buf, p2p, p2p->query_count, + p2p->query_hash, + p2p->p2ps_adv_list); + } + return buf; } +static int p2p_service_find_asp(struct p2p_data *p2p, const u8 *hash) +{ + struct p2ps_advertisement *adv_data; + + p2p_dbg(p2p, "ASP find - ASP list: %p", p2p->p2ps_adv_list); + + /* Wildcard always matches if we have actual services */ + if (os_memcmp(hash, p2p->wild_card_hash, P2PS_HASH_LEN) == 0) + return p2p->p2ps_adv_list != NULL; + + adv_data = p2p->p2ps_adv_list; + while (adv_data) { + p2p_dbg(p2p, "ASP hash: %x =? %x", hash[0], adv_data->hash[0]); + if (os_memcmp(hash, adv_data->hash, P2PS_HASH_LEN) == 0) + return 1; + adv_data = adv_data->next; + } + + return 0; +} + + static enum p2p_probe_req_status p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, const u8 *bssid, const u8 *ie, size_t ie_len) @@ -2084,13 +2227,6 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, struct p2p_message msg; struct wpabuf *ies; - if (!p2p->in_listen || !p2p->drv_in_listen) { - /* not in Listen state - ignore Probe Request */ - p2p_dbg(p2p, "Not in Listen state (in_listen=%d drv_in_listen=%d) - ignore Probe Request", - p2p->in_listen, p2p->drv_in_listen); - return P2P_PREQ_NOT_LISTEN; - } - if (ieee802_11_parse_elems((u8 *) ie, ie_len, &elems, 0) == ParseFailed) { /* Ignore invalid Probe Request frames */ @@ -2141,6 +2277,64 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, return P2P_PREQ_NOT_P2P; } + p2p->p2ps_svc_found = 0; + + if (msg.service_hash && msg.service_hash_count) { + const u8 *hash = msg.service_hash; + u8 *dest = p2p->query_hash; + u8 i; + + p2p->query_count = 0; + for (i = 0; i < msg.service_hash_count; i++) { + if (p2p_service_find_asp(p2p, hash)) { + p2p->p2ps_svc_found = 1; + + if (!os_memcmp(hash, p2p->wild_card_hash, + P2PS_HASH_LEN)) { + /* We found match(es) but wildcard + * will return all */ + p2p->query_count = 1; + os_memcpy(p2p->query_hash, hash, + P2PS_HASH_LEN); + break; + } + + /* Save each matching hash */ + if (p2p->query_count < P2P_MAX_QUERY_HASH) { + os_memcpy(dest, hash, P2PS_HASH_LEN); + dest += P2PS_HASH_LEN; + p2p->query_count++; + } else { + /* We found match(es) but too many to + * return all */ + p2p->query_count = 0; + break; + } + } + hash += P2PS_HASH_LEN; + } + + p2p_dbg(p2p, "ASP adv found: %d", p2p->p2ps_svc_found); + + /* Probed hash unknown */ + if (!p2p->p2ps_svc_found) { + p2p_parse_free(&msg); + return P2P_PREQ_NOT_PROCESSED; + } + } else { + /* This is not a P2PS Probe Request */ + p2p->query_count = 0; + p2p_dbg(p2p, "No P2PS Hash in Probe Request"); + + if (!p2p->in_listen || !p2p->drv_in_listen) { + /* not in Listen state - ignore Probe Request */ + p2p_dbg(p2p, "Not in Listen state (in_listen=%d drv_in_listen=%d) - ignore Probe Request", + p2p->in_listen, p2p->drv_in_listen); + p2p_parse_free(&msg); + return P2P_PREQ_NOT_LISTEN; + } + } + if (msg.device_id && os_memcmp(msg.device_id, p2p->cfg->dev_addr, ETH_ALEN) != 0) { /* Device ID did not match */ @@ -2238,6 +2432,7 @@ p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, p2p_add_dev_from_probe_req(p2p, addr, ie, ie_len); res = p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len); + p2p->query_count = 0; if ((p2p->state == P2P_CONNECT || p2p->state == P2P_CONNECT_LISTEN) && p2p->go_neg_peer && @@ -2395,6 +2590,132 @@ int p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end) } +struct p2ps_advertisement * +p2p_service_p2ps_id(struct p2p_data *p2p, u32 adv_id) +{ + struct p2ps_advertisement *adv_data; + + if (!p2p) + return NULL; + + adv_data = p2p->p2ps_adv_list; + while (adv_data) { + if (adv_data->id == adv_id) + return adv_data; + adv_data = adv_data->next; + } + + return NULL; +} + + +int p2p_service_del_asp(struct p2p_data *p2p, u32 adv_id) +{ + struct p2ps_advertisement *adv_data; + struct p2ps_advertisement **prior; + + if (!p2p) + return -1; + + adv_data = p2p->p2ps_adv_list; + prior = &p2p->p2ps_adv_list; + while (adv_data) { + if (adv_data->id == adv_id) { + p2p_dbg(p2p, "Delete ASP adv_id=0x%x", adv_id); + *prior = adv_data->next; + os_free(adv_data); + return 0; + } + prior = &adv_data->next; + adv_data = adv_data->next; + } + + return -1; +} + + +int p2p_service_add_asp(struct p2p_data *p2p, int auto_accept, u32 adv_id, + const char *adv_str, u8 svc_state, u16 config_methods, + const char *svc_info) +{ + struct p2ps_advertisement *adv_data, *tmp, **prev; + u8 buf[P2PS_HASH_LEN]; + size_t adv_data_len, adv_len, info_len = 0; + + if (!p2p || !adv_str || !adv_str[0]) + return -1; + + if (!(config_methods & p2p->cfg->config_methods)) { + p2p_dbg(p2p, "Config methods not supported svc: 0x%x dev: 0x%x", + config_methods, p2p->cfg->config_methods); + return -1; + } + + if (!p2ps_gen_hash(p2p, adv_str, buf)) + return -1; + + if (svc_info) + info_len = os_strlen(svc_info); + adv_len = os_strlen(adv_str); + adv_data_len = sizeof(struct p2ps_advertisement) + adv_len + 1 + + info_len + 1; + + adv_data = os_zalloc(adv_data_len); + if (!adv_data) + return -1; + + os_memcpy(adv_data->hash, buf, P2PS_HASH_LEN); + adv_data->id = adv_id; + adv_data->state = svc_state; + adv_data->config_methods = config_methods & p2p->cfg->config_methods; + adv_data->auto_accept = (u8) auto_accept; + os_memcpy(adv_data->svc_name, adv_str, adv_len); + + if (svc_info && info_len) { + adv_data->svc_info = &adv_data->svc_name[adv_len + 1]; + os_memcpy(adv_data->svc_info, svc_info, info_len); + } + + /* + * Group Advertisements by service string. They do not need to be + * sorted, but groups allow easier Probe Response instance grouping + */ + tmp = p2p->p2ps_adv_list; + prev = &p2p->p2ps_adv_list; + while (tmp) { + if (tmp->id == adv_data->id) { + if (os_strcmp(tmp->svc_name, adv_data->svc_name) != 0) { + os_free(adv_data); + return -1; + } + adv_data->next = tmp->next; + *prev = adv_data; + os_free(tmp); + goto inserted; + } else { + if (os_strcmp(tmp->svc_name, adv_data->svc_name) == 0) { + adv_data->next = tmp->next; + tmp->next = adv_data; + goto inserted; + } + } + prev = &tmp->next; + tmp = tmp->next; + } + + /* No svc_name match found */ + adv_data->next = p2p->p2ps_adv_list; + p2p->p2ps_adv_list = adv_data; + +inserted: + p2p_dbg(p2p, + "Added ASP advertisement adv_id=0x%x config_methods=0x%x svc_state=0x%x adv_str='%s'", + adv_id, adv_data->config_methods, svc_state, adv_str); + + return 0; +} + + int p2p_parse_dev_addr_in_p2p_ie(struct wpabuf *p2p_ie, u8 *dev_addr) { struct p2p_message msg; @@ -2508,6 +2829,8 @@ struct p2p_data * p2p_init(const struct p2p_config *cfg) p2p->cfg->num_pref_chan = 0; } + p2ps_gen_hash(p2p, P2PS_WILD_HASH_STR, p2p->wild_card_hash); + p2p->min_disc_int = 1; p2p->max_disc_int = 3; p2p->max_disc_tu = -1; @@ -2541,6 +2864,8 @@ struct p2p_data * p2p_init(const struct p2p_config *cfg) void p2p_deinit(struct p2p_data *p2p) { + struct p2ps_advertisement *adv, *prev; + #ifdef CONFIG_WIFI_DISPLAY wpabuf_free(p2p->wfd_ie_beacon); wpabuf_free(p2p->wfd_ie_probe_req); @@ -2569,10 +2894,19 @@ void p2p_deinit(struct p2p_data *p2p) os_free(p2p->cfg->serial_number); os_free(p2p->cfg->pref_chan); os_free(p2p->groups); + os_free(p2p->p2ps_prov); wpabuf_free(p2p->sd_resp); os_free(p2p->after_scan_tx); p2p_remove_wps_vendor_extensions(p2p); os_free(p2p->no_go_freq.range); + + adv = p2p->p2ps_adv_list; + while (adv) { + prev = adv; + adv = adv->next; + os_free(prev); + } + os_free(p2p); } @@ -2837,13 +3171,15 @@ static void p2p_sd_cb(struct p2p_data *p2p, int success) if (p2p->sd_peer) p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p->sd_peer = NULL; - p2p_continue_find(p2p); + if (p2p->state != P2P_IDLE) + p2p_continue_find(p2p); return; } if (p2p->sd_peer == NULL) { p2p_dbg(p2p, "No SD peer entry known"); - p2p_continue_find(p2p); + if (p2p->state != P2P_IDLE) + p2p_continue_find(p2p); return; } @@ -2874,9 +3210,6 @@ static void p2p_retry_pd(struct p2p_data *p2p) { struct p2p_device *dev; - if (p2p->state != P2P_IDLE) - return; - /* * Retry the prov disc req attempt only for the peer that the user had * requested. @@ -2950,6 +3283,51 @@ static void p2p_prov_disc_cb(struct p2p_data *p2p, int success) } +static int p2p_check_after_scan_tx_continuation(struct p2p_data *p2p) +{ + if (p2p->after_scan_tx_in_progress) { + p2p->after_scan_tx_in_progress = 0; + if (p2p->start_after_scan != P2P_AFTER_SCAN_NOTHING && + p2p_run_after_scan(p2p)) + return 1; + if (p2p->state == P2P_SEARCH) { + p2p_dbg(p2p, "Continue find after after_scan_tx completion"); + p2p_continue_find(p2p); + } + } + + return 0; +} + + +static void p2p_prov_disc_resp_cb(struct p2p_data *p2p, int success) +{ + p2p_dbg(p2p, "Provision Discovery Response TX callback: success=%d", + success); + + if (p2p->send_action_in_progress) { + p2p->send_action_in_progress = 0; + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + } + + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + + if (!success) + goto continue_search; + + if (!p2p->cfg->prov_disc_resp_cb || + p2p->cfg->prov_disc_resp_cb(p2p->cfg->cb_ctx) < 1) + goto continue_search; + + p2p_dbg(p2p, + "Post-Provision Discovery operations started - do not try to continue other P2P operations"); + return; + +continue_search: + p2p_check_after_scan_tx_continuation(p2p); +} + + int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq, struct os_reltime *rx_time, int level, const u8 *ies, size_t ies_len) @@ -2992,6 +3370,7 @@ 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 dev_capab; u8 *len; #ifdef CONFIG_WIFI_DISPLAY @@ -3004,8 +3383,15 @@ void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id) p2p->vendor_elem[VENDOR_ELEM_PROBE_REQ_P2P]); len = p2p_buf_add_ie_hdr(ies); - p2p_buf_add_capability(ies, p2p->dev_capab & - ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0); + + dev_capab = p2p->dev_capab & ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; + + /* P2PS requires Probe Request frames to include SD bit */ + if (p2p->p2ps_seek && p2p->p2ps_seek_count) + dev_capab |= P2P_DEV_CAPAB_SERVICE_DISCOVERY; + + p2p_buf_add_capability(ies, dev_capab, 0); + if (dev_id) p2p_buf_add_device_id(ies, dev_id); if (p2p->cfg->reg_class && p2p->cfg->channel) @@ -3015,6 +3401,10 @@ void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id) if (p2p->ext_listen_interval) p2p_buf_add_ext_listen_timing(ies, p2p->ext_listen_period, p2p->ext_listen_interval); + + if (p2p->p2ps_seek && p2p->p2ps_seek_count) + p2p_buf_add_service_hash(ies, p2p); + /* TODO: p2p_buf_add_operating_channel() if GO */ p2p_buf_update_ie_hdr(ies, len); } @@ -3209,9 +3599,9 @@ void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst, int success; p2p_dbg(p2p, "Action frame TX callback (state=%d freq=%u dst=" MACSTR - " src=" MACSTR " bssid=" MACSTR " result=%d", + " src=" MACSTR " bssid=" MACSTR " result=%d p2p_state=%s)", p2p->pending_action_state, freq, MAC2STR(dst), MAC2STR(src), - MAC2STR(bssid), result); + MAC2STR(bssid), result, p2p_state_txt(p2p->state)); success = result == P2P_SEND_ACTION_SUCCESS; state = p2p->pending_action_state; p2p->pending_action_state = P2P_NO_PENDING_ACTION; @@ -3221,16 +3611,7 @@ void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst, p2p->send_action_in_progress = 0; p2p->cfg->send_action_done(p2p->cfg->cb_ctx); } - if (p2p->after_scan_tx_in_progress) { - p2p->after_scan_tx_in_progress = 0; - if (p2p->start_after_scan != P2P_AFTER_SCAN_NOTHING && - p2p_run_after_scan(p2p)) - break; - if (p2p->state == P2P_SEARCH) { - p2p_dbg(p2p, "Continue find after after_scan_tx completion"); - p2p_continue_find(p2p); - } - } + p2p_check_after_scan_tx_continuation(p2p); break; case P2P_PENDING_GO_NEG_REQUEST: p2p_go_neg_req_cb(p2p, success); @@ -3250,6 +3631,9 @@ void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst, case P2P_PENDING_PD: p2p_prov_disc_cb(p2p, success); break; + case P2P_PENDING_PD_RESPONSE: + p2p_prov_disc_resp_cb(p2p, success); + break; case P2P_PENDING_INVITATION_REQUEST: p2p_invitation_req_cb(p2p, success); break; @@ -3456,6 +3840,9 @@ static void p2p_timeout_prov_disc_during_find(struct p2p_data *p2p) static void p2p_timeout_prov_disc_req(struct p2p_data *p2p) { + u32 adv_id = 0; + u8 *adv_mac = NULL; + p2p->pending_action_state = P2P_NO_PENDING_ACTION; /* @@ -3484,12 +3871,18 @@ static void p2p_timeout_prov_disc_req(struct p2p_data *p2p) for_join = 1; } + if (p2p->p2ps_prov) { + adv_id = p2p->p2ps_prov->adv_id; + adv_mac = p2p->p2ps_prov->adv_mac; + } + if (p2p->cfg->prov_disc_fail) p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, p2p->pending_pd_devaddr, for_join ? P2P_PROV_DISC_TIMEOUT_JOIN : - P2P_PROV_DISC_TIMEOUT); + P2P_PROV_DISC_TIMEOUT, + adv_id, adv_mac, NULL); p2p_reset_pending_pd(p2p); } } @@ -3638,6 +4031,8 @@ const char * p2p_wps_method_text(enum p2p_wps_method method) return "PBC"; case WPS_NFC: return "NFC"; + case WPS_P2PS: + return "P2PS"; } return "??"; @@ -3714,7 +4109,7 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info, "country=%c%c\n" "oper_freq=%d\n" "req_config_methods=0x%x\n" - "flags=%s%s%s%s%s%s%s%s%s%s%s%s%s\n" + "flags=%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n" "status=%d\n" "invitation_reqs=%u\n", (int) (now.sec - dev->last_seen.sec), @@ -3740,6 +4135,8 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info, "[PD_PEER_DISPLAY]" : "", dev->flags & P2P_DEV_PD_PEER_KEYPAD ? "[PD_PEER_KEYPAD]" : "", + dev->flags & P2P_DEV_PD_PEER_P2PS ? + "[PD_PEER_P2PS]" : "", dev->flags & P2P_DEV_USER_REJECTED ? "[USER_REJECTED]" : "", dev->flags & P2P_DEV_PEER_WAITING_RESPONSE ? @@ -4171,15 +4568,18 @@ int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel, if (p2p_channel_to_freq(reg_class, channel) < 0) return -1; - p2p_dbg(p2p, "Set Listen channel: reg_class %u channel %u", - reg_class, channel); - /* * Listen channel was set in configuration or set by control interface; * cannot override it. */ - if (p2p->cfg->channel_forced && forced == 0) + if (p2p->cfg->channel_forced && forced == 0) { + p2p_dbg(p2p, + "Listen channel was previously configured - do not override based on optimization"); return -1; + } + + p2p_dbg(p2p, "Set Listen channel: reg_class %u channel %u", + reg_class, channel); if (p2p->state == P2P_IDLE) { p2p->cfg->reg_class = reg_class; diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h index fa886f74..b1c89d71 100644 --- a/src/p2p/p2p.h +++ b/src/p2p/p2p.h @@ -11,6 +11,16 @@ #include "wps/wps_defs.h" +/* P2P ASP Setup Capability */ +#define P2PS_SETUP_NONE 0 +#define P2PS_SETUP_NEW BIT(0) +#define P2PS_SETUP_CLIENT BIT(1) +#define P2PS_SETUP_GROUP_OWNER BIT(2) + +#define P2PS_WILD_HASH_STR "org.wi-fi.wfds" +#define P2PS_HASH_LEN 6 +#define P2P_MAX_QUERY_HASH 6 + /** * P2P_MAX_REG_CLASSES - Maximum number of regulatory classes */ @@ -52,7 +62,8 @@ struct p2p_channels { }; enum p2p_wps_method { - WPS_NOT_READY, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC, WPS_NFC + WPS_NOT_READY, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC, WPS_NFC, + WPS_P2PS }; /** @@ -142,6 +153,93 @@ struct p2p_go_neg_results { unsigned int peer_config_timeout; }; +struct p2ps_provision { + /** + * status - Remote returned provisioning status code + */ + int status; + + /** + * adv_id - P2PS Advertisement ID + */ + u32 adv_id; + + /** + * session_id - P2PS Session ID + */ + u32 session_id; + + /** + * method - WPS Method (to be) used to establish session + */ + u16 method; + + /** + * conncap - Connection Capabilities negotiated between P2P peers + */ + u8 conncap; + + /** + * role - Info about the roles to be used for this connection + */ + u8 role; + + /** + * session_mac - MAC address of the peer that started the session + */ + u8 session_mac[ETH_ALEN]; + + /** + * adv_mac - MAC address of the peer advertised the service + */ + u8 adv_mac[ETH_ALEN]; + + /** + * info - Vendor defined extra Provisioning information + */ + char info[0]; +}; + +struct p2ps_advertisement { + struct p2ps_advertisement *next; + + /** + * svc_info - Pointer to (internal) Service defined information + */ + char *svc_info; + + /** + * id - P2PS Advertisement ID + */ + u32 id; + + /** + * config_methods - WPS Methods which are allowed for this service + */ + u16 config_methods; + + /** + * state - Current state of the service: 0 - Out Of Service, 1-255 Vendor defined + */ + u8 state; + + /** + * auto_accept - Automatically Accept provisioning request if possible. + */ + u8 auto_accept; + + /** + * hash - 6 octet Service Name has to match against incoming Probe Requests + */ + u8 hash[P2PS_HASH_LEN]; + + /** + * svc_name - NULL Terminated UTF-8 Service Name, and svc_info storage + */ + char svc_name[0]; +}; + + struct p2p_data; enum p2p_scan_type { @@ -238,6 +336,11 @@ struct p2p_peer_info { * IE(s) from the frame that was used to discover the peer. */ struct wpabuf *vendor_elems; + + /** + * p2ps_instance - P2PS Application Service Info + */ + struct wpabuf *p2ps_instance; }; enum p2p_prov_disc_status { @@ -245,6 +348,7 @@ enum p2p_prov_disc_status { P2P_PROV_DISC_TIMEOUT, P2P_PROV_DISC_REJECTED, P2P_PROV_DISC_TIMEOUT_JOIN, + P2P_PROV_DISC_INFO_UNAVAILABLE, }; struct p2p_channel { @@ -705,6 +809,9 @@ struct p2p_config { * @ctx: Callback context from cb_ctx * @peer: Source address of the response * @status: Cause of failure, will not be %P2P_PROV_DISC_SUCCESS + * @adv_id: If non-zero, then the adv_id of the PD Request + * @adv_mac: P2P Device Address of the advertizer + * @deferred_session_resp: Deferred session response sent by advertizer * * This callback is used to indicate either a failure or no response * to an earlier provision discovery request. @@ -713,7 +820,9 @@ struct p2p_config { * is not used or failures do not need to be indicated. */ void (*prov_disc_fail)(void *ctx, const u8 *peer, - enum p2p_prov_disc_status status); + enum p2p_prov_disc_status status, + u32 adv_id, const u8 *adv_mac, + const char *deferred_session_resp); /** * invitation_process - Optional callback for processing Invitations @@ -835,6 +944,83 @@ struct p2p_config { * or 0 if not. */ int (*is_p2p_in_progress)(void *ctx); + + /** + * Determine if we have a persistent group we share with remote peer + * @ctx: Callback context from cb_ctx + * @addr: Peer device address to search for + * @ssid: Persistent group SSID or %NULL if any + * @ssid_len: Length of @ssid + * @go_dev_addr: Buffer for returning intended GO P2P Device Address + * @ret_ssid: Buffer for returning group SSID + * @ret_ssid_len: Buffer for returning length of @ssid + * Returns: 1 if a matching persistent group was found, 0 otherwise + */ + int (*get_persistent_group)(void *ctx, const u8 *addr, const u8 *ssid, + size_t ssid_len, u8 *go_dev_addr, + u8 *ret_ssid, size_t *ret_ssid_len); + + /** + * Get information about a possible local GO role + * @ctx: Callback context from cb_ctx + * @intended_addr: Buffer for returning intended GO interface address + * @ssid: Buffer for returning group SSID + * @ssid_len: Buffer for returning length of @ssid + * @group_iface: Buffer for returning whether a separate group interface + * would be used + * Returns: 1 if GO info found, 0 otherwise + * + * This is used to compose New Group settings (SSID, and intended + * address) during P2PS provisioning if results of provisioning *might* + * result in our being an autonomous GO. + */ + int (*get_go_info)(void *ctx, u8 *intended_addr, + u8 *ssid, size_t *ssid_len, int *group_iface); + + /** + * remove_stale_groups - Remove stale P2PS groups + * + * Because P2PS stages *potential* GOs, and remote devices can remove + * credentials unilaterally, we need to make sure we don't let stale + * unusable groups build up. + */ + int (*remove_stale_groups)(void *ctx, const u8 *peer, const u8 *go, + const u8 *ssid, size_t ssid_len); + + /** + * p2ps_prov_complete - P2PS provisioning complete + * + * When P2PS provisioning completes (successfully or not) we must + * transmit all of the results to the upper layers. + */ + void (*p2ps_prov_complete)(void *ctx, u8 status, const u8 *dev, + const u8 *adv_mac, const u8 *ses_mac, + const u8 *grp_mac, u32 adv_id, u32 ses_id, + u8 conncap, int passwd_id, + const u8 *persist_ssid, + size_t persist_ssid_size, int response_done, + int prov_start, const char *session_info); + + /** + * prov_disc_resp_cb - Callback for indicating completion of PD Response + * @ctx: Callback context from cb_ctx + * Returns: 1 if operation was started, 0 otherwise + * + * This callback can be used to perform any pending actions after + * provisioning. It is mainly used for P2PS pending group creation. + */ + int (*prov_disc_resp_cb)(void *ctx); + + /** + * p2ps_group_capability - Determine group capability + * + * This function can be used to determine group capability based on + * information from P2PS PD exchange and the current state of ongoing + * groups and driver capabilities. + * + * P2PS_SETUP_* bitmap is used as the parameters and return value. + */ + u8 (*p2ps_group_capability)(void *ctx, u8 incoming, u8 role); }; @@ -941,12 +1127,15 @@ enum p2p_discovery_type { * 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 + * @seek_count: Number of ASP Service Strings in the seek_string array + * @seek_string: ASP Service Strings to query for in Probe Requests * 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, unsigned int search_delay); + const u8 *dev_id, unsigned int search_delay, + u8 seek_count, const char **seek_string); /** * p2p_notify_scan_trigger_status - Indicate scan trigger status @@ -1058,6 +1247,7 @@ int p2p_reject(struct p2p_data *p2p, const u8 *peer_addr); * p2p_prov_disc_req - Send Provision Discovery Request * @p2p: P2P module context from p2p_init() * @peer_addr: MAC address of the peer P2P client + * @p2ps_prov: Provisioning info for P2PS * @config_methods: WPS Config Methods value (only one bit set) * @join: Whether this is used by a client joining an active group * @force_freq: Forced TX frequency for the frame (mainly for the join case) @@ -1073,7 +1263,8 @@ int p2p_reject(struct p2p_data *p2p, const u8 *peer_addr); * indicated with the p2p_config::prov_disc_resp() callback. */ int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr, - u16 config_methods, int join, int force_freq, + struct p2ps_provision *p2ps_prov, u16 config_methods, + int join, int force_freq, int user_initiated_pd); /** @@ -1816,6 +2007,13 @@ const u8 * p2p_get_go_neg_peer(struct p2p_data *p2p); unsigned int p2p_get_group_num_members(struct p2p_group *group); /** + * p2p_client_limit_reached - Check if client limit is reached + * @group: P2P group context from p2p_group_init() + * Returns: 1 if no of clients limit reached + */ +int p2p_client_limit_reached(struct p2p_group *group); + +/** * p2p_iterate_group_members - Iterate group members * @group: P2P group context from p2p_group_init() * @next: iteration pointer, must be a pointer to a void * that is set to %NULL @@ -2030,4 +2228,14 @@ void p2p_loop_on_known_peers(struct p2p_data *p2p, void p2p_set_vendor_elems(struct p2p_data *p2p, struct wpabuf **vendor_elem); +void p2p_set_intended_addr(struct p2p_data *p2p, const u8 *intended_addr); + +struct p2ps_advertisement * +p2p_service_p2ps_id(struct p2p_data *p2p, u32 adv_id); +int p2p_service_add_asp(struct p2p_data *p2p, int auto_accept, u32 adv_id, + const char *adv_str, u8 svc_state, + u16 config_methods, const char *svc_info); +int p2p_service_del_asp(struct p2p_data *p2p, u32 adv_id); +struct p2ps_advertisement * p2p_get_p2ps_adv_list(struct p2p_data *p2p); + #endif /* P2P_H */ diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c index e9b683d9..92c92066 100644 --- a/src/p2p/p2p_build.c +++ b/src/p2p/p2p_build.c @@ -164,15 +164,18 @@ void p2p_buf_add_device_info(struct wpabuf *buf, struct p2p_data *p2p, if (peer->wps_method == WPS_PBC) methods |= WPS_CONFIG_PUSHBUTTON; else if (peer->wps_method == WPS_PIN_DISPLAY || - peer->wps_method == WPS_PIN_KEYPAD) + peer->wps_method == WPS_PIN_KEYPAD) { methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD; + methods |= WPS_CONFIG_P2PS; + } } else if (p2p->cfg->config_methods) { methods |= p2p->cfg->config_methods & (WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_DISPLAY | - WPS_CONFIG_KEYPAD); + WPS_CONFIG_KEYPAD | WPS_CONFIG_P2PS); } else { methods |= WPS_CONFIG_PUSHBUTTON; methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD; + methods |= WPS_CONFIG_P2PS; } wpabuf_put_be16(buf, methods); @@ -342,6 +345,256 @@ void p2p_buf_add_oob_go_neg_channel(struct wpabuf *buf, const char *country, } +void p2p_buf_add_service_hash(struct wpabuf *buf, struct p2p_data *p2p) +{ + if (!p2p) + return; + + /* Service Hash */ + wpabuf_put_u8(buf, P2P_ATTR_SERVICE_HASH); + wpabuf_put_le16(buf, p2p->p2ps_seek_count * P2PS_HASH_LEN); + wpabuf_put_data(buf, p2p->query_hash, + p2p->p2ps_seek_count * P2PS_HASH_LEN); + wpa_hexdump(MSG_DEBUG, "P2P: * Service Hash", + p2p->query_hash, p2p->p2ps_seek_count * P2PS_HASH_LEN); +} + + +void p2p_buf_add_session_info(struct wpabuf *buf, const char *info) +{ + size_t info_len = 0; + + if (info && info[0]) + info_len = os_strlen(info); + + /* Session Information Data Info */ + wpabuf_put_u8(buf, P2P_ATTR_SESSION_INFORMATION_DATA); + wpabuf_put_le16(buf, (u16) info_len); + + if (info) { + wpabuf_put_data(buf, info, info_len); + wpa_printf(MSG_DEBUG, "P2P: * Session Info Data (%s)", info); + } +} + + +void p2p_buf_add_connection_capability(struct wpabuf *buf, u8 connection_cap) +{ + /* Connection Capability Info */ + wpabuf_put_u8(buf, P2P_ATTR_CONNECTION_CAPABILITY); + wpabuf_put_le16(buf, 1); + wpabuf_put_u8(buf, connection_cap); + wpa_printf(MSG_DEBUG, "P2P: * Connection Capability: 0x%x", + connection_cap); +} + + +void p2p_buf_add_advertisement_id(struct wpabuf *buf, u32 id, const u8 *mac) +{ + if (!buf || !mac) + return; + + /* Advertisement ID Info */ + wpabuf_put_u8(buf, P2P_ATTR_ADVERTISEMENT_ID); + wpabuf_put_le16(buf, (u16) (sizeof(u32) + ETH_ALEN)); + wpabuf_put_le32(buf, id); + wpabuf_put_data(buf, mac, ETH_ALEN); + wpa_printf(MSG_DEBUG, "P2P: * Advertisement ID (%x) " MACSTR, + id, MAC2STR(mac)); +} + + +void p2p_buf_add_service_instance(struct wpabuf *buf, struct p2p_data *p2p, + u8 hash_count, const u8 *hash, + struct p2ps_advertisement *adv_list) +{ + struct p2ps_advertisement *adv; + struct wpabuf *tmp_buf; + u8 *tag_len = NULL, *ie_len = NULL; + size_t svc_len = 0, remaining = 0, total_len = 0; + + if (!adv_list || !hash) + return; + + /* Allocate temp buffer, allowing for overflow of 1 instance */ + tmp_buf = wpabuf_alloc(MAX_SVC_ADV_IE_LEN + 256 + P2PS_HASH_LEN); + if (!tmp_buf) + return; + + for (adv = adv_list; adv && total_len <= MAX_SVC_ADV_LEN; + adv = adv->next) { + u8 count = hash_count; + const u8 *test = hash; + + while (count--) { + /* Check for wildcard */ + if (os_memcmp(test, p2p->wild_card_hash, + P2PS_HASH_LEN) == 0) { + total_len = MAX_SVC_ADV_LEN + 1; + goto wild_hash; + } + + if (os_memcmp(test, adv->hash, P2PS_HASH_LEN) == 0) + goto hash_match; + + test += P2PS_HASH_LEN; + } + + /* No matches found - Skip this Adv Instance */ + continue; + +hash_match: + if (!tag_len) { + tag_len = p2p_buf_add_ie_hdr(tmp_buf); + remaining = 255 - 4; + if (!ie_len) { + wpabuf_put_u8(tmp_buf, + P2P_ATTR_ADVERTISED_SERVICE); + ie_len = wpabuf_put(tmp_buf, sizeof(u16)); + remaining -= (sizeof(u8) + sizeof(u16)); + } + } + + svc_len = os_strlen(adv->svc_name); + + if (7 + svc_len + total_len > MAX_SVC_ADV_LEN) { + /* Can't fit... return wildcard */ + total_len = MAX_SVC_ADV_LEN + 1; + break; + } + + if (remaining <= (sizeof(adv->id) + + sizeof(adv->config_methods))) { + size_t front = remaining; + size_t back = (sizeof(adv->id) + + sizeof(adv->config_methods)) - front; + u8 holder[sizeof(adv->id) + + sizeof(adv->config_methods)]; + + /* This works even if front or back == 0 */ + WPA_PUT_LE32(holder, adv->id); + WPA_PUT_BE16(&holder[sizeof(adv->id)], + adv->config_methods); + wpabuf_put_data(tmp_buf, holder, front); + p2p_buf_update_ie_hdr(tmp_buf, tag_len); + tag_len = p2p_buf_add_ie_hdr(tmp_buf); + wpabuf_put_data(tmp_buf, &holder[front], back); + remaining = 255 - (sizeof(adv->id) + + sizeof(adv->config_methods)) - back; + } else { + wpabuf_put_le32(tmp_buf, adv->id); + wpabuf_put_be16(tmp_buf, adv->config_methods); + remaining -= (sizeof(adv->id) + + sizeof(adv->config_methods)); + } + + /* We are guaranteed at least one byte for svc_len */ + wpabuf_put_u8(tmp_buf, svc_len); + remaining -= sizeof(u8); + + if (remaining < svc_len) { + size_t front = remaining; + size_t back = svc_len - front; + + wpabuf_put_data(tmp_buf, adv->svc_name, front); + p2p_buf_update_ie_hdr(tmp_buf, tag_len); + tag_len = p2p_buf_add_ie_hdr(tmp_buf); + + /* In rare cases, we must split across 3 attributes */ + if (back > 255 - 4) { + wpabuf_put_data(tmp_buf, + &adv->svc_name[front], 255 - 4); + back -= 255 - 4; + front += 255 - 4; + p2p_buf_update_ie_hdr(tmp_buf, tag_len); + tag_len = p2p_buf_add_ie_hdr(tmp_buf); + } + + wpabuf_put_data(tmp_buf, &adv->svc_name[front], back); + remaining = 255 - 4 - back; + } else { + wpabuf_put_data(tmp_buf, adv->svc_name, svc_len); + remaining -= svc_len; + } + + /* adv_id config_methods svc_string */ + total_len += sizeof(u32) + sizeof(u16) + sizeof(u8) + svc_len; + } + + if (tag_len) + p2p_buf_update_ie_hdr(tmp_buf, tag_len); + + if (ie_len) + WPA_PUT_LE16(ie_len, (u16) total_len); + +wild_hash: + /* If all fit, return matching instances, otherwise the wildcard */ + if (total_len <= MAX_SVC_ADV_LEN) { + wpabuf_put_buf(buf, tmp_buf); + } else { + char *wild_card = P2PS_WILD_HASH_STR; + u8 wild_len; + + /* Insert wildcard instance */ + tag_len = p2p_buf_add_ie_hdr(buf); + wpabuf_put_u8(buf, P2P_ATTR_ADVERTISED_SERVICE); + ie_len = wpabuf_put(buf, sizeof(u16)); + + wild_len = (u8) os_strlen(wild_card); + wpabuf_put_le32(buf, 0); + wpabuf_put_be16(buf, 0); + wpabuf_put_u8(buf, wild_len); + wpabuf_put_data(buf, wild_card, wild_len); + + WPA_PUT_LE16(ie_len, 4 + 2 + 1 + wild_len); + p2p_buf_update_ie_hdr(buf, tag_len); + } + + wpabuf_free(tmp_buf); +} + + +void p2p_buf_add_session_id(struct wpabuf *buf, u32 id, const u8 *mac) +{ + if (!buf || !mac) + return; + + /* Session ID Info */ + wpabuf_put_u8(buf, P2P_ATTR_SESSION_ID); + wpabuf_put_le16(buf, (u16) (sizeof(u32) + ETH_ALEN)); + wpabuf_put_le32(buf, id); + wpabuf_put_data(buf, mac, ETH_ALEN); + wpa_printf(MSG_DEBUG, "P2P: * Session ID Info (%x) " MACSTR, + id, MAC2STR(mac)); +} + + +void p2p_buf_add_feature_capability(struct wpabuf *buf, u16 len, const u8 *mask) +{ + if (!buf || !len || !mask) + return; + + /* Feature Capability */ + wpabuf_put_u8(buf, P2P_ATTR_FEATURE_CAPABILITY); + wpabuf_put_le16(buf, len); + wpabuf_put_data(buf, mask, len); + wpa_printf(MSG_DEBUG, "P2P: * Feature Capability (%d)", len); +} + + +void p2p_buf_add_persistent_group_info(struct wpabuf *buf, const u8 *dev_addr, + const u8 *ssid, size_t ssid_len) +{ + /* P2P Group ID */ + wpabuf_put_u8(buf, P2P_ATTR_PERSISTENT_GROUP); + wpabuf_put_le16(buf, ETH_ALEN + ssid_len); + wpabuf_put_data(buf, dev_addr, ETH_ALEN); + wpabuf_put_data(buf, ssid, ssid_len); + wpa_printf(MSG_DEBUG, "P2P: * P2P Group ID " MACSTR, + MAC2STR(dev_addr)); +} + + static int p2p_add_wps_string(struct wpabuf *buf, enum wps_attribute attr, const char *val) { diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c index c654c5a8..98abf9d2 100644 --- a/src/p2p/p2p_go_neg.c +++ b/src/p2p/p2p_go_neg.c @@ -107,6 +107,8 @@ u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method) return DEV_PW_PUSHBUTTON; case WPS_NFC: return DEV_PW_NFC_CONNECTION_HANDOVER; + case WPS_P2PS: + return DEV_PW_P2PS_DEFAULT; default: return DEV_PW_DEFAULT; } @@ -124,6 +126,8 @@ static const char * p2p_wps_method_str(enum p2p_wps_method wps_method) return "PBC"; case WPS_NFC: return "NFC"; + case WPS_P2PS: + return "P2PS"; default: return "??"; } @@ -218,10 +222,12 @@ int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev) config_method = WPS_CONFIG_DISPLAY; else if (dev->wps_method == WPS_PBC) config_method = WPS_CONFIG_PUSHBUTTON; + else if (dev->wps_method == WPS_P2PS) + config_method = WPS_CONFIG_P2PS; else return -1; return p2p_prov_disc_req(p2p, dev->info.p2p_device_addr, - config_method, 0, 0, 1); + NULL, config_method, 0, 0, 1); } freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq; @@ -488,8 +494,8 @@ void p2p_reselect_channel(struct p2p_data *p2p, } -static int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev, - u8 *status) +int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev, + u8 *status) { struct p2p_channels tmp, intersection; @@ -743,6 +749,16 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, goto fail; } break; + case DEV_PW_P2PS_DEFAULT: + p2p_dbg(p2p, "Peer using P2PS pin"); + if (dev->wps_method != WPS_P2PS) { + p2p_dbg(p2p, + "We have wps_method=%s -> incompatible", + p2p_wps_method_str(dev->wps_method)); + status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; + goto fail; + } + break; default: if (msg.dev_password_id && msg.dev_password_id == dev->oob_pw_id) { @@ -1102,6 +1118,15 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, goto fail; } break; + case DEV_PW_P2PS_DEFAULT: + p2p_dbg(p2p, "P2P: Peer using P2PS default pin"); + if (dev->wps_method != WPS_P2PS) { + p2p_dbg(p2p, "We have wps_method=%s -> incompatible", + p2p_wps_method_str(dev->wps_method)); + status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; + goto fail; + } + break; default: if (msg.dev_password_id && msg.dev_password_id == dev->oob_pw_id) { diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c index da8588aa..41ca99fa 100644 --- a/src/p2p/p2p_group.c +++ b/src/p2p/p2p_group.c @@ -981,10 +981,22 @@ u8 p2p_group_presence_req(struct p2p_group *group, unsigned int p2p_get_group_num_members(struct p2p_group *group) { + if (!group) + return 0; + return group->num_members; } +int p2p_client_limit_reached(struct p2p_group *group) +{ + if (!group || !group->cfg) + return 1; + + return group->num_members >= group->cfg->max_clients; +} + + const u8 * p2p_iterate_group_members(struct p2p_group *group, void **next) { struct p2p_group_member *iter = *next; diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h index 62711e7c..6af19ced 100644 --- a/src/p2p/p2p_i.h +++ b/src/p2p/p2p_i.h @@ -16,6 +16,13 @@ enum p2p_role_indication; +/* + * To force Service Instances to fit within a single P2P Tag, MAX_SVC_ADV_LEN + * must equal 248 or less. Must have a minimum size of 19. + */ +#define MAX_SVC_ADV_LEN 600 +#define MAX_SVC_ADV_IE_LEN (9 + MAX_SVC_ADV_LEN + (5 * (MAX_SVC_ADV_LEN / 240))) + enum p2p_go_state { UNKNOWN_GO, LOCAL_GO, @@ -98,6 +105,8 @@ struct p2p_device { #define P2P_DEV_PD_BEFORE_GO_NEG BIT(17) #define P2P_DEV_NO_PREF_CHAN BIT(18) #define P2P_DEV_WAIT_INV_REQ_ACK BIT(19) +#define P2P_DEV_P2PS_REPORTED BIT(20) +#define P2P_DEV_PD_PEER_P2PS BIT(21) unsigned int flags; int status; /* enum p2p_status_code */ @@ -354,6 +363,7 @@ struct p2p_data { P2P_PENDING_GO_NEG_CONFIRM, P2P_PENDING_SD, P2P_PENDING_PD, + P2P_PENDING_PD_RESPONSE, P2P_PENDING_INVITATION_REQUEST, P2P_PENDING_INVITATION_RESPONSE, P2P_PENDING_DEV_DISC_REQUEST, @@ -492,6 +502,16 @@ struct p2p_data { u8 pending_channel; u8 pending_channel_forced; + /* ASP Support */ + struct p2ps_advertisement *p2ps_adv_list; + struct p2ps_provision *p2ps_prov; + u8 wild_card_hash[P2PS_HASH_LEN]; + u8 query_hash[P2P_MAX_QUERY_HASH * P2PS_HASH_LEN]; + u8 query_count; + u8 p2ps_seek; + u8 p2ps_seek_count; + u8 p2ps_svc_found; + #ifdef CONFIG_WIFI_DISPLAY struct wpabuf *wfd_ie_beacon; struct wpabuf *wfd_ie_probe_req; @@ -586,6 +606,31 @@ struct p2p_message { /* SSID IE */ const u8 *ssid; + + /* P2PS */ + u8 service_hash_count; + const u8 *service_hash; + + const u8 *session_info; + size_t session_info_len; + + const u8 *conn_cap; + + const u8 *adv_id; + const u8 *adv_mac; + + const u8 *adv_service_instance; + size_t adv_service_instance_len; + + const u8 *session_id; + const u8 *session_mac; + + const u8 *feature_cap; + size_t feature_cap_len; + + const u8 *persistent_dev; + const u8 *persistent_ssid; + size_t persistent_ssid_len; }; @@ -698,6 +743,18 @@ void p2p_buf_add_p2p_interface(struct wpabuf *buf, struct p2p_data *p2p); void p2p_buf_add_oob_go_neg_channel(struct wpabuf *buf, const char *country, u8 oper_class, u8 channel, enum p2p_role_indication role); +void p2p_buf_add_service_hash(struct wpabuf *buf, struct p2p_data *p2p); +void p2p_buf_add_session_info(struct wpabuf *buf, const char *info); +void p2p_buf_add_connection_capability(struct wpabuf *buf, u8 connection_cap); +void p2p_buf_add_advertisement_id(struct wpabuf *buf, u32 id, const u8 *mac); +void p2p_buf_add_service_instance(struct wpabuf *buf, struct p2p_data *p2p, + u8 count, const u8 *hash, + struct p2ps_advertisement *adv_list); +void p2p_buf_add_session_id(struct wpabuf *buf, u32 id, const u8 *mac); +void p2p_buf_add_feature_capability(struct wpabuf *buf, u16 len, + const u8 *mask); +void p2p_buf_add_persistent_group_info(struct wpabuf *buf, const u8 *dev_addr, + const u8 *ssid, size_t ssid_len); int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id, int all_attr); @@ -793,6 +850,8 @@ int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev, unsigned int force_freq, unsigned int pref_freq, int go); void p2p_go_neg_wait_timeout(void *eloop_ctx, void *timeout_ctx); +int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev, + u8 *status); void p2p_dbg(struct p2p_data *p2p, const char *fmt, ...) PRINTF_FORMAT(2, 3); void p2p_info(struct p2p_data *p2p, const char *fmt, ...) diff --git a/src/p2p/p2p_parse.c b/src/p2p/p2p_parse.c index 52ba19e0..fd6a4610 100644 --- a/src/p2p/p2p_parse.c +++ b/src/p2p/p2p_parse.c @@ -281,6 +281,112 @@ static int p2p_parse_attribute(u8 id, const u8 *data, u16 len, data[0], data[1], data[2], data[3], data[4], data[5]); break; + case P2P_ATTR_SERVICE_HASH: + if (len < P2PS_HASH_LEN) { + wpa_printf(MSG_DEBUG, + "P2P: Too short Service Hash (length %u)", + len); + return -1; + } + msg->service_hash_count = len / P2PS_HASH_LEN; + msg->service_hash = data; + wpa_hexdump(MSG_DEBUG, "P2P: * Service Hash(s)", data, len); + break; + case P2P_ATTR_SESSION_INFORMATION_DATA: + msg->session_info = data; + msg->session_info_len = len; + wpa_printf(MSG_DEBUG, "P2P: * Service Instance: %u bytes - %p", + len, data); + break; + case P2P_ATTR_CONNECTION_CAPABILITY: + if (len < 1) { + wpa_printf(MSG_DEBUG, + "P2P: Too short Connection Capability (length %u)", + len); + return -1; + } + msg->conn_cap = data; + wpa_printf(MSG_DEBUG, "P2P: * Connection Capability: 0x%x", + *msg->conn_cap); + break; + case P2P_ATTR_ADVERTISEMENT_ID: + if (len < 10) { + wpa_printf(MSG_DEBUG, + "P2P: Too short Advertisement ID (length %u)", + len); + return -1; + } + msg->adv_id = data; + msg->adv_mac = &data[sizeof(u32)]; + wpa_printf(MSG_DEBUG, "P2P: * Advertisement ID %x", + WPA_GET_LE32(data)); + break; + case P2P_ATTR_ADVERTISED_SERVICE: + if (len < 8) { + wpa_printf(MSG_DEBUG, + "P2P: Too short Service Instance (length %u)", + len); + return -1; + } + msg->adv_service_instance = data; + msg->adv_service_instance_len = len; + if (len <= 255 + 8) { + char str[256]; + u8 namelen; + + namelen = data[6]; + if (namelen > len - 7) + break; + os_memcpy(str, &data[7], namelen); + str[namelen] = '\0'; + wpa_printf(MSG_DEBUG, "P2P: * Service Instance: %x-%s", + WPA_GET_LE32(data), str); + } else { + wpa_printf(MSG_DEBUG, "P2P: * Service Instance: %p", + data); + } + break; + case P2P_ATTR_SESSION_ID: + if (len < sizeof(u32) + ETH_ALEN) { + wpa_printf(MSG_DEBUG, + "P2P: Too short Session ID Info (length %u)", + len); + return -1; + } + msg->session_id = data; + msg->session_mac = &data[sizeof(u32)]; + wpa_printf(MSG_DEBUG, "P2P: * Session ID: %x " MACSTR, + WPA_GET_LE32(data), MAC2STR(msg->session_mac)); + break; + case P2P_ATTR_FEATURE_CAPABILITY: + if (!len) { + wpa_printf(MSG_DEBUG, + "P2P: Too short Feature Capability (length %u)", + len); + return -1; + } + msg->feature_cap = data; + msg->feature_cap_len = len; + wpa_printf(MSG_DEBUG, "P2P: * Feature Cap (length=%u)", len); + break; + case P2P_ATTR_PERSISTENT_GROUP: + { + if (len < ETH_ALEN) { + wpa_printf(MSG_DEBUG, + "P2P: Too short Persistent Group Info (length %u)", + len); + return -1; + } + + msg->persistent_dev = data; + msg->persistent_ssid_len = len - ETH_ALEN; + msg->persistent_ssid = &data[ETH_ALEN]; + wpa_printf(MSG_DEBUG, "P2P: * Persistent Group: " MACSTR " %s", + MAC2STR(msg->persistent_dev), + wpa_ssid_txt(msg->persistent_ssid, + msg->persistent_ssid_len)); + break; + } default: wpa_printf(MSG_DEBUG, "P2P: Skipped unknown attribute %d " "(length %d)", id, len); diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c index e1013672..328b1e02 100644 --- a/src/p2p/p2p_pd.c +++ b/src/p2p/p2p_pd.c @@ -40,14 +40,132 @@ static void p2p_build_wps_ie_config_methods(struct wpabuf *buf, } +static void p2ps_add_new_group_info(struct p2p_data *p2p, struct wpabuf *buf) +{ + int found; + u8 intended_addr[ETH_ALEN]; + u8 ssid[32]; + size_t ssid_len; + int group_iface; + + if (!p2p->cfg->get_go_info) + return; + + found = p2p->cfg->get_go_info( + p2p->cfg->cb_ctx, intended_addr, ssid, + &ssid_len, &group_iface); + if (found) { + p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, + ssid, ssid_len); + p2p_buf_add_intended_addr(buf, intended_addr); + } else { + if (!p2p->ssid_set) { + p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len); + p2p->ssid_set = 1; + } + + /* Add pre-composed P2P Group ID */ + p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, + p2p->ssid, p2p->ssid_len); + + if (group_iface) + p2p_buf_add_intended_addr( + buf, p2p->intended_addr); + else + p2p_buf_add_intended_addr( + buf, p2p->cfg->dev_addr); + } +} + + +static void p2ps_add_pd_req_attrs(struct p2p_data *p2p, struct p2p_device *dev, + struct wpabuf *buf, u16 config_methods) +{ + struct p2ps_provision *prov = p2p->p2ps_prov; + u8 feat_cap_mask[] = { 1, 0 }; + int shared_group = 0; + u8 ssid[32]; + size_t ssid_len; + u8 go_dev_addr[ETH_ALEN]; + + /* If we might be explicite group owner, add GO details */ + if (prov->conncap & (P2PS_SETUP_GROUP_OWNER | + P2PS_SETUP_NEW)) + p2ps_add_new_group_info(p2p, buf); + + if (prov->status >= 0) + p2p_buf_add_status(buf, (u8) prov->status); + else + prov->method = config_methods; + + if (p2p->cfg->get_persistent_group) { + shared_group = p2p->cfg->get_persistent_group( + p2p->cfg->cb_ctx, dev->info.p2p_device_addr, NULL, 0, + go_dev_addr, ssid, &ssid_len); + } + + /* Add Operating Channel if conncap includes GO */ + if (shared_group || + (prov->conncap & (P2PS_SETUP_GROUP_OWNER | + P2PS_SETUP_NEW))) { + u8 tmp; + + p2p_go_select_channel(p2p, dev, &tmp); + + if (p2p->op_reg_class && p2p->op_channel) + p2p_buf_add_operating_channel(buf, p2p->cfg->country, + p2p->op_reg_class, + p2p->op_channel); + else + p2p_buf_add_operating_channel(buf, p2p->cfg->country, + p2p->cfg->op_reg_class, + p2p->cfg->op_channel); + } + + p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->cfg->channels); + + if (prov->info[0]) + p2p_buf_add_session_info(buf, prov->info); + + p2p_buf_add_connection_capability(buf, prov->conncap); + + p2p_buf_add_advertisement_id(buf, prov->adv_id, prov->adv_mac); + + if (shared_group || prov->conncap == P2PS_SETUP_NEW || + prov->conncap == + (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW) || + prov->conncap == + (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT)) { + /* Add Config Timeout */ + p2p_buf_add_config_timeout(buf, p2p->go_timeout, + p2p->client_timeout); + } + + p2p_buf_add_listen_channel(buf, p2p->cfg->country, p2p->cfg->reg_class, + p2p->cfg->channel); + + p2p_buf_add_session_id(buf, prov->session_id, prov->session_mac); + + p2p_buf_add_feature_capability(buf, sizeof(feat_cap_mask), + feat_cap_mask); + + if (shared_group) + p2p_buf_add_persistent_group_info(buf, go_dev_addr, + ssid, ssid_len); +} + + static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p, - u8 dialog_token, - u16 config_methods, - struct p2p_device *go) + struct p2p_device *dev, + int join) { struct wpabuf *buf; u8 *len; size_t extra = 0; + u8 dialog_token = dev->dialog_token; + u16 config_methods = dev->req_config_methods; + struct p2p_device *go = join ? dev : NULL; + u8 group_capab; #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_prov_disc_req) @@ -57,6 +175,10 @@ static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p, if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]) extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]); + if (p2p->p2ps_prov) + extra += os_strlen(p2p->p2ps_prov->info) + 1 + + sizeof(struct p2ps_provision); + buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; @@ -64,10 +186,23 @@ static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p, p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_REQ, dialog_token); len = p2p_buf_add_ie_hdr(buf); + + group_capab = 0; + if (p2p->p2ps_prov) { + group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP; + group_capab |= P2P_GROUP_CAPAB_PERSISTENT_RECONN; + if (p2p->cross_connect) + group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; + if (p2p->cfg->p2p_intra_bss) + group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; + } p2p_buf_add_capability(buf, p2p->dev_capab & - ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0); + ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, + group_capab); p2p_buf_add_device_info(buf, p2p, NULL); - if (go) { + if (p2p->p2ps_prov) { + p2ps_add_pd_req_attrs(p2p, dev, buf, config_methods); + } else if (go) { p2p_buf_add_group_id(buf, go->info.p2p_device_addr, go->oper_ssid, go->oper_ssid_len); } @@ -89,13 +224,19 @@ static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p, static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, + struct p2p_device *dev, u8 dialog_token, + enum p2p_status_code status, u16 config_methods, + u32 adv_id, const u8 *group_id, - size_t group_id_len) + size_t group_id_len, + const u8 *persist_ssid, + size_t persist_ssid_len) { struct wpabuf *buf; size_t extra = 0; + int persist = 0; #ifdef CONFIG_WIFI_DISPLAY struct wpabuf *wfd_ie = p2p->wfd_ie_prov_disc_resp; @@ -121,12 +262,103 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]) extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]); - buf = wpabuf_alloc(100 + extra); + buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_RESP, dialog_token); + /* Add P2P IE for P2PS */ + if (p2p->p2ps_prov && p2p->p2ps_prov->adv_id == adv_id) { + u8 feat_cap_mask[] = { 1, 0 }; + u8 *len = p2p_buf_add_ie_hdr(buf); + struct p2ps_provision *prov = p2p->p2ps_prov; + u8 group_capab; + + if (!status && prov->status != -1) + status = prov->status; + + p2p_buf_add_status(buf, status); + group_capab = P2P_GROUP_CAPAB_PERSISTENT_GROUP | + P2P_GROUP_CAPAB_PERSISTENT_RECONN; + if (p2p->cross_connect) + group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; + if (p2p->cfg->p2p_intra_bss) + group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; + p2p_buf_add_capability(buf, p2p->dev_capab & + ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, + group_capab); + p2p_buf_add_device_info(buf, p2p, NULL); + + if (persist_ssid && p2p->cfg->get_persistent_group && + (status == P2P_SC_SUCCESS || + status == P2P_SC_SUCCESS_DEFERRED)) { + u8 ssid[32]; + size_t ssid_len; + u8 go_dev_addr[ETH_ALEN]; + + persist = p2p->cfg->get_persistent_group( + p2p->cfg->cb_ctx, + dev->info.p2p_device_addr, + persist_ssid, persist_ssid_len, go_dev_addr, + ssid, &ssid_len); + if (persist) + p2p_buf_add_persistent_group_info( + buf, go_dev_addr, ssid, ssid_len); + } + + if (!persist && (prov->conncap & P2PS_SETUP_GROUP_OWNER)) + p2ps_add_new_group_info(p2p, buf); + + /* Add Operating Channel if conncap indicates GO */ + if (persist || (prov->conncap & P2PS_SETUP_GROUP_OWNER)) { + u8 tmp; + + if (dev) + p2p_go_select_channel(p2p, dev, &tmp); + + if (p2p->op_reg_class && p2p->op_channel) + p2p_buf_add_operating_channel( + buf, p2p->cfg->country, + p2p->op_reg_class, + p2p->op_channel); + else + p2p_buf_add_operating_channel( + buf, p2p->cfg->country, + p2p->cfg->op_reg_class, + p2p->cfg->op_channel); + } + + p2p_buf_add_channel_list(buf, p2p->cfg->country, + &p2p->cfg->channels); + + if (!persist && (status == P2P_SC_SUCCESS || + status == P2P_SC_SUCCESS_DEFERRED)) + p2p_buf_add_connection_capability(buf, prov->conncap); + + p2p_buf_add_advertisement_id(buf, adv_id, prov->adv_mac); + + p2p_buf_add_config_timeout(buf, p2p->go_timeout, + p2p->client_timeout); + + p2p_buf_add_session_id(buf, prov->session_id, + prov->session_mac); + + p2p_buf_add_feature_capability(buf, sizeof(feat_cap_mask), + feat_cap_mask); + p2p_buf_update_ie_hdr(buf, len); + } else if (status != P2P_SC_SUCCESS || adv_id) { + u8 *len = p2p_buf_add_ie_hdr(buf); + + p2p_buf_add_status(buf, status); + + if (p2p->p2ps_prov) + p2p_buf_add_advertisement_id(buf, adv_id, + p2p->p2ps_prov->adv_mac); + + p2p_buf_update_ie_hdr(buf, len); + } + /* WPS IE with Config Methods attribute */ p2p_build_wps_ie_config_methods(buf, config_methods); @@ -142,14 +374,50 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, } +static int p2ps_setup_p2ps_prov(struct p2p_data *p2p, u32 adv_id, + u32 session_id, u16 method, + const u8 *session_mac, const u8 *adv_mac) +{ + struct p2ps_provision *tmp; + + if (!p2p->p2ps_prov) { + p2p->p2ps_prov = os_zalloc(sizeof(struct p2ps_provision) + 1); + if (!p2p->p2ps_prov) + return -1; + } else { + os_memset(p2p->p2ps_prov, 0, sizeof(struct p2ps_provision) + 1); + } + + tmp = p2p->p2ps_prov; + tmp->adv_id = adv_id; + tmp->session_id = session_id; + tmp->method = method; + os_memcpy(tmp->session_mac, session_mac, ETH_ALEN); + os_memcpy(tmp->adv_mac, adv_mac, ETH_ALEN); + tmp->info[0] = '\0'; + + return 0; +} + + void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq) { struct p2p_message msg; struct p2p_device *dev; int freq; - int reject = 1; + enum p2p_status_code reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; struct wpabuf *resp; + u32 adv_id = 0; + struct p2ps_advertisement *p2ps_adv = NULL; + u8 conncap = P2PS_SETUP_NEW; + u8 auto_accept = 0; + u32 session_id = 0; + u8 session_mac[ETH_ALEN]; + u8 adv_mac[ETH_ALEN]; + u8 group_mac[ETH_ALEN]; + int passwd_id = DEV_PW_DEFAULT; + u16 config_methods; if (p2p_parse(data, len, &msg)) return; @@ -175,12 +443,13 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, if (!(msg.wps_config_methods & (WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD | - WPS_CONFIG_PUSHBUTTON))) { + WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_P2PS))) { p2p_dbg(p2p, "Unsupported Config Methods in Provision Discovery Request"); goto out; } - if (msg.group_id) { + /* Legacy (non-P2PS) - Unknown groups allowed for P2PS */ + if (!msg.adv_id && msg.group_id) { size_t i; for (i = 0; i < p2p->num_groups; i++) { if (p2p_group_is_group_id_match(p2p->groups[i], @@ -194,28 +463,203 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, } } - if (dev) + if (dev) { dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY | - P2P_DEV_PD_PEER_KEYPAD); + P2P_DEV_PD_PEER_KEYPAD | + P2P_DEV_PD_PEER_P2PS); + + /* Remove stale persistent groups */ + if (p2p->cfg->remove_stale_groups) { + p2p->cfg->remove_stale_groups( + p2p->cfg->cb_ctx, dev->info.p2p_device_addr, + msg.persistent_dev, + msg.persistent_ssid, msg.persistent_ssid_len); + } + } if (msg.wps_config_methods & WPS_CONFIG_DISPLAY) { p2p_dbg(p2p, "Peer " MACSTR " requested us to show a PIN on display", MAC2STR(sa)); if (dev) dev->flags |= P2P_DEV_PD_PEER_KEYPAD; + passwd_id = DEV_PW_USER_SPECIFIED; } else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) { p2p_dbg(p2p, "Peer " MACSTR " requested us to write its PIN using keypad", MAC2STR(sa)); if (dev) dev->flags |= P2P_DEV_PD_PEER_DISPLAY; + passwd_id = DEV_PW_REGISTRAR_SPECIFIED; + } else if (msg.wps_config_methods & WPS_CONFIG_P2PS) { + p2p_dbg(p2p, "Peer " MACSTR " requesting P2PS PIN", + MAC2STR(sa)); + if (dev) + dev->flags |= P2P_DEV_PD_PEER_P2PS; + passwd_id = DEV_PW_P2PS_DEFAULT; } - reject = 0; + reject = P2P_SC_SUCCESS; + + os_memset(session_mac, 0, ETH_ALEN); + os_memset(adv_mac, 0, ETH_ALEN); + os_memset(group_mac, 0, ETH_ALEN); + + if (msg.adv_id && msg.session_id && msg.session_mac && msg.adv_mac && + (msg.status || msg.conn_cap)) { + u8 remote_conncap; + + if (msg.intended_addr) + os_memcpy(group_mac, msg.intended_addr, ETH_ALEN); + + os_memcpy(session_mac, msg.session_mac, ETH_ALEN); + os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN); + + session_id = WPA_GET_LE32(msg.session_id); + adv_id = WPA_GET_LE32(msg.adv_id); + + if (!msg.status) + p2ps_adv = p2p_service_p2ps_id(p2p, adv_id); + + p2p_dbg(p2p, "adv_id: %x - p2ps_adv - %p", adv_id, p2ps_adv); + + if (msg.conn_cap) + conncap = *msg.conn_cap; + remote_conncap = conncap; + + if (p2ps_adv) { + auto_accept = p2ps_adv->auto_accept; + conncap = p2p->cfg->p2ps_group_capability( + p2p->cfg->cb_ctx, conncap, auto_accept); + + p2p_dbg(p2p, "Conncap: local:%d remote:%d result:%d", + auto_accept, remote_conncap, conncap); + + if (p2ps_adv->config_methods && + !(msg.wps_config_methods & + p2ps_adv->config_methods)) { + p2p_dbg(p2p, + "Unsupported config methods in Provision Discovery Request (own=0x%x peer=0x%x)", + p2ps_adv->config_methods, + msg.wps_config_methods); + reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + } else if (!p2ps_adv->state) { + p2p_dbg(p2p, "P2PS state unavailable"); + reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; + } else if (!conncap) { + p2p_dbg(p2p, "Conncap resolution failed"); + reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + } + + if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) { + p2p_dbg(p2p, "Keypad - always defer"); + auto_accept = 0; + } + + if (auto_accept || reject != P2P_SC_SUCCESS) { + struct p2ps_provision *tmp; + + if (reject == P2P_SC_SUCCESS && !conncap) { + reject = + P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + } + + if (p2ps_setup_p2ps_prov( + p2p, adv_id, session_id, + msg.wps_config_methods, + session_mac, adv_mac) < 0) { + reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; + goto out; + } + + tmp = p2p->p2ps_prov; + if (conncap) { + tmp->conncap = conncap; + tmp->status = P2P_SC_SUCCESS; + } else { + tmp->conncap = auto_accept; + tmp->status = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + } + + if (reject != P2P_SC_SUCCESS) + goto out; + } + } else if (!msg.status) { + reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + goto out; + } + + if (!msg.status && !auto_accept && + (!p2p->p2ps_prov || p2p->p2ps_prov->adv_id != adv_id)) { + struct p2ps_provision *tmp; + + if (!conncap) { + reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + goto out; + } + + if (p2ps_setup_p2ps_prov(p2p, adv_id, session_id, + msg.wps_config_methods, + session_mac, adv_mac) < 0) { + reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; + goto out; + } + tmp = p2p->p2ps_prov; + reject = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; + tmp->status = reject; + } + + if (msg.status) { + if (*msg.status && + *msg.status != P2P_SC_SUCCESS_DEFERRED) { + reject = *msg.status; + } else if (*msg.status == P2P_SC_SUCCESS_DEFERRED && + p2p->p2ps_prov) { + u16 method = p2p->p2ps_prov->method; + + conncap = p2p->cfg->p2ps_group_capability( + p2p->cfg->cb_ctx, remote_conncap, + p2p->p2ps_prov->conncap); + + p2p_dbg(p2p, + "Conncap: local:%d remote:%d result:%d", + p2p->p2ps_prov->conncap, + remote_conncap, conncap); + + /* + * Ensure that if we asked for PIN originally, + * our method is consistent with original + * request. + */ + if (method & WPS_CONFIG_DISPLAY) + method = WPS_CONFIG_KEYPAD; + else if (method & WPS_CONFIG_KEYPAD) + method = WPS_CONFIG_DISPLAY; + + /* Reject this "Deferred Accept* if incompatible + * conncap or method */ + if (!conncap || + !(msg.wps_config_methods & method)) + reject = + P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + else + reject = P2P_SC_SUCCESS; + + p2p->p2ps_prov->status = reject; + p2p->p2ps_prov->conncap = conncap; + } + } + } out: - resp = p2p_build_prov_disc_resp(p2p, msg.dialog_token, - reject ? 0 : msg.wps_config_methods, - msg.group_id, msg.group_id_len); + if (reject == P2P_SC_SUCCESS || + reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) + config_methods = msg.wps_config_methods; + else + config_methods = 0; + resp = p2p_build_prov_disc_resp(p2p, dev, msg.dialog_token, reject, + config_methods, adv_id, + msg.group_id, msg.group_id_len, + msg.persistent_ssid, + msg.persistent_ssid_len); if (resp == NULL) { p2p_parse_free(&msg); return; @@ -232,7 +676,7 @@ out: p2p_parse_free(&msg); return; } - p2p->pending_action_state = P2P_NO_PENDING_ACTION; + p2p->pending_action_state = P2P_PENDING_PD_RESPONSE; if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, p2p->cfg->dev_addr, wpabuf_head(resp), wpabuf_len(resp), 200) < 0) { @@ -242,7 +686,91 @@ out: wpabuf_free(resp); - if (!reject && p2p->cfg->prov_disc_req) { + if (!p2p->cfg->p2ps_prov_complete) { + /* Don't emit anything */ + } else if (msg.status && *msg.status != P2P_SC_SUCCESS && + *msg.status != P2P_SC_SUCCESS_DEFERRED) { + reject = *msg.status; + p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, reject, + sa, adv_mac, session_mac, + NULL, adv_id, session_id, + 0, 0, msg.persistent_ssid, + msg.persistent_ssid_len, + 0, 0, NULL); + } else if (msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED && + p2p->p2ps_prov) { + p2p->p2ps_prov->status = reject; + p2p->p2ps_prov->conncap = conncap; + + if (reject != P2P_SC_SUCCESS) + p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, reject, + sa, adv_mac, session_mac, + NULL, adv_id, + session_id, conncap, 0, + msg.persistent_ssid, + msg.persistent_ssid_len, 0, + 0, NULL); + else + p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, + *msg.status, + sa, adv_mac, session_mac, + group_mac, adv_id, + session_id, conncap, + passwd_id, + msg.persistent_ssid, + msg.persistent_ssid_len, 0, + 0, NULL); + } else if (msg.status && p2p->p2ps_prov) { + p2p->p2ps_prov->status = P2P_SC_SUCCESS; + p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, *msg.status, sa, + adv_mac, session_mac, group_mac, + adv_id, session_id, conncap, + passwd_id, + msg.persistent_ssid, + msg.persistent_ssid_len, + 0, 0, NULL); + } else if (msg.status) { + } else if (auto_accept && reject == P2P_SC_SUCCESS) { + p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, P2P_SC_SUCCESS, + sa, adv_mac, session_mac, + group_mac, adv_id, session_id, + conncap, passwd_id, + msg.persistent_ssid, + msg.persistent_ssid_len, + 0, 0, NULL); + } else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE && + (!msg.session_info || !msg.session_info_len)) { + p2p->p2ps_prov->method = msg.wps_config_methods; + + p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, P2P_SC_SUCCESS, + sa, adv_mac, session_mac, + group_mac, adv_id, session_id, + conncap, passwd_id, + msg.persistent_ssid, + msg.persistent_ssid_len, + 0, 1, NULL); + } else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) { + size_t buf_len = msg.session_info_len; + char *buf = os_malloc(2 * buf_len + 1); + + if (buf) { + p2p->p2ps_prov->method = msg.wps_config_methods; + + utf8_escape((char *) msg.session_info, buf_len, + buf, 2 * buf_len + 1); + + p2p->cfg->p2ps_prov_complete( + p2p->cfg->cb_ctx, P2P_SC_SUCCESS, sa, + adv_mac, session_mac, group_mac, adv_id, + session_id, conncap, passwd_id, + msg.persistent_ssid, msg.persistent_ssid_len, + 0, 1, buf); + + os_free(buf); + } + } + + if (reject == P2P_SC_SUCCESS && p2p->cfg->prov_disc_req) { const u8 *dev_addr = sa; if (msg.p2p_device_addr) dev_addr = msg.p2p_device_addr; @@ -265,11 +793,48 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, struct p2p_message msg; struct p2p_device *dev; u16 report_config_methods = 0, req_config_methods; + u8 status = P2P_SC_SUCCESS; int success = 0; + u32 adv_id = 0; + u8 conncap = P2PS_SETUP_NEW; + u8 adv_mac[ETH_ALEN]; + u8 group_mac[ETH_ALEN]; + int passwd_id = DEV_PW_DEFAULT; if (p2p_parse(data, len, &msg)) return; + /* Parse the P2PS members present */ + if (msg.status) + status = *msg.status; + + if (msg.intended_addr) + os_memcpy(group_mac, msg.intended_addr, ETH_ALEN); + else + os_memset(group_mac, 0, ETH_ALEN); + + if (msg.adv_mac) + os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN); + else + os_memset(adv_mac, 0, ETH_ALEN); + + if (msg.adv_id) + adv_id = WPA_GET_LE32(msg.adv_id); + + if (msg.conn_cap) { + conncap = *msg.conn_cap; + + /* Switch bits to local relative */ + switch (conncap) { + case P2PS_SETUP_GROUP_OWNER: + conncap = P2PS_SETUP_CLIENT; + break; + case P2PS_SETUP_CLIENT: + conncap = P2PS_SETUP_GROUP_OWNER; + break; + } + } + p2p_dbg(p2p, "Received Provision Discovery Response from " MACSTR " with config methods 0x%x", MAC2STR(sa), msg.wps_config_methods); @@ -313,23 +878,109 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, msg.wps_config_methods, req_config_methods); if (p2p->cfg->prov_disc_fail) p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa, - P2P_PROV_DISC_REJECTED); + P2P_PROV_DISC_REJECTED, + adv_id, adv_mac, NULL); p2p_parse_free(&msg); + os_free(p2p->p2ps_prov); + p2p->p2ps_prov = NULL; goto out; } report_config_methods = req_config_methods; dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY | - P2P_DEV_PD_PEER_KEYPAD); + P2P_DEV_PD_PEER_KEYPAD | + P2P_DEV_PD_PEER_P2PS); if (req_config_methods & WPS_CONFIG_DISPLAY) { p2p_dbg(p2p, "Peer " MACSTR " accepted to show a PIN on display", MAC2STR(sa)); dev->flags |= P2P_DEV_PD_PEER_DISPLAY; + passwd_id = DEV_PW_REGISTRAR_SPECIFIED; } else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) { p2p_dbg(p2p, "Peer " MACSTR " accepted to write our PIN using keypad", MAC2STR(sa)); dev->flags |= P2P_DEV_PD_PEER_KEYPAD; + passwd_id = DEV_PW_USER_SPECIFIED; + } else if (msg.wps_config_methods & WPS_CONFIG_P2PS) { + p2p_dbg(p2p, "Peer " MACSTR " accepted P2PS PIN", + MAC2STR(sa)); + dev->flags |= P2P_DEV_PD_PEER_P2PS; + passwd_id = DEV_PW_P2PS_DEFAULT; + } + + if ((msg.conn_cap || msg.persistent_dev) && + msg.adv_id && + (status == P2P_SC_SUCCESS || status == P2P_SC_SUCCESS_DEFERRED) && + p2p->p2ps_prov) { + if (p2p->cfg->p2ps_prov_complete) { + p2p->cfg->p2ps_prov_complete( + p2p->cfg->cb_ctx, status, sa, adv_mac, + p2p->p2ps_prov->session_mac, + group_mac, adv_id, p2p->p2ps_prov->session_id, + conncap, passwd_id, msg.persistent_ssid, + msg.persistent_ssid_len, 1, 0, NULL); + } + os_free(p2p->p2ps_prov); + p2p->p2ps_prov = NULL; + } + + if (status != P2P_SC_SUCCESS && + status != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE && + status != P2P_SC_SUCCESS_DEFERRED && p2p->p2ps_prov) { + if (p2p->cfg->p2ps_prov_complete) + p2p->cfg->p2ps_prov_complete( + p2p->cfg->cb_ctx, status, sa, adv_mac, + p2p->p2ps_prov->session_mac, + group_mac, adv_id, p2p->p2ps_prov->session_id, + 0, 0, NULL, 0, 1, 0, NULL); + os_free(p2p->p2ps_prov); + p2p->p2ps_prov = NULL; + } + + if (status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) { + if (p2p->cfg->remove_stale_groups) { + p2p->cfg->remove_stale_groups(p2p->cfg->cb_ctx, + dev->info.p2p_device_addr, + NULL, NULL, 0); + } + + if (msg.session_info && msg.session_info_len) { + size_t info_len = msg.session_info_len; + char *deferred_sess_resp = os_malloc(2 * info_len + 1); + + if (!deferred_sess_resp) { + p2p_parse_free(&msg); + os_free(p2p->p2ps_prov); + p2p->p2ps_prov = NULL; + goto out; + } + utf8_escape((char *) msg.session_info, info_len, + deferred_sess_resp, 2 * info_len + 1); + + if (p2p->cfg->prov_disc_fail) + p2p->cfg->prov_disc_fail( + p2p->cfg->cb_ctx, sa, + P2P_PROV_DISC_INFO_UNAVAILABLE, + adv_id, adv_mac, + deferred_sess_resp); + os_free(deferred_sess_resp); + } else + if (p2p->cfg->prov_disc_fail) + p2p->cfg->prov_disc_fail( + p2p->cfg->cb_ctx, sa, + P2P_PROV_DISC_INFO_UNAVAILABLE, + adv_id, adv_mac, NULL); + } else if (msg.wps_config_methods != dev->req_config_methods || + status != P2P_SC_SUCCESS) { + p2p_dbg(p2p, "Peer rejected our Provision Discovery Request"); + if (p2p->cfg->prov_disc_fail) + p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa, + P2P_PROV_DISC_REJECTED, 0, + NULL, NULL); + p2p_parse_free(&msg); + os_free(p2p->p2ps_prov); + p2p->p2ps_prov = NULL; + goto out; } /* Store the provisioning info */ @@ -388,9 +1039,33 @@ int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev, /* TODO: use device discoverability request through GO */ } - req = p2p_build_prov_disc_req(p2p, dev->dialog_token, - dev->req_config_methods, - join ? dev : NULL); + if (p2p->p2ps_prov) { + if (p2p->p2ps_prov->status == P2P_SC_SUCCESS_DEFERRED) { + if (p2p->p2ps_prov->method == WPS_CONFIG_DISPLAY) + dev->req_config_methods = WPS_CONFIG_KEYPAD; + else if (p2p->p2ps_prov->method == WPS_CONFIG_KEYPAD) + dev->req_config_methods = WPS_CONFIG_DISPLAY; + else + dev->req_config_methods = WPS_CONFIG_P2PS; + } else { + /* Order of preference, based on peer's capabilities */ + if (p2p->p2ps_prov->method) + dev->req_config_methods = + p2p->p2ps_prov->method; + else if (dev->info.config_methods & WPS_CONFIG_P2PS) + dev->req_config_methods = WPS_CONFIG_P2PS; + else if (dev->info.config_methods & WPS_CONFIG_DISPLAY) + dev->req_config_methods = WPS_CONFIG_DISPLAY; + else + dev->req_config_methods = WPS_CONFIG_KEYPAD; + } + p2p_dbg(p2p, + "Building PD Request based on P2PS config method 0x%x status %d --> req_config_methods 0x%x", + p2p->p2ps_prov->method, p2p->p2ps_prov->status, + dev->req_config_methods); + } + + req = p2p_build_prov_disc_req(p2p, dev, join); if (req == NULL) return -1; @@ -413,6 +1088,7 @@ int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev, int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr, + struct p2ps_provision *p2ps_prov, u16 config_methods, int join, int force_freq, int user_initiated_pd) { @@ -424,17 +1100,28 @@ int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr, if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { p2p_dbg(p2p, "Provision Discovery Request destination " MACSTR " not yet known", MAC2STR(peer_addr)); + os_free(p2ps_prov); return -1; } p2p_dbg(p2p, "Provision Discovery Request with " MACSTR " (config methods 0x%x)", MAC2STR(peer_addr), config_methods); - if (config_methods == 0) + if (config_methods == 0 && !p2ps_prov) { + os_free(p2ps_prov); return -1; + } + + if (p2ps_prov && p2ps_prov->status == P2P_SC_SUCCESS_DEFERRED && + p2p->p2ps_prov) { + /* Use cached method from deferred provisioning */ + p2ps_prov->method = p2p->p2ps_prov->method; + } /* Reset provisioning info */ dev->wps_prov_info = 0; + os_free(p2p->p2ps_prov); + p2p->p2ps_prov = p2ps_prov; dev->req_config_methods = config_methods; if (join) diff --git a/src/rsn_supp/preauth.c b/src/rsn_supp/preauth.c index 63569120..c6534af2 100644 --- a/src/rsn_supp/preauth.c +++ b/src/rsn_supp/preauth.c @@ -172,6 +172,7 @@ int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst, { struct eapol_config eapol_conf; struct eapol_ctx *ctx; + int ret; if (sm->preauth_eapol) return -1; @@ -197,14 +198,16 @@ int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst, wpa_printf(MSG_WARNING, "RSN: Failed to initialize L2 " "packet processing (bridge) for " "pre-authentication"); - return -2; + ret = -2; + goto fail; } } ctx = os_zalloc(sizeof(*ctx)); if (ctx == NULL) { wpa_printf(MSG_WARNING, "Failed to allocate EAPOL context."); - return -4; + ret = -4; + goto fail; } ctx->ctx = sm->ctx->ctx; ctx->msg_ctx = sm->ctx->ctx; @@ -222,7 +225,8 @@ int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst, os_free(ctx); wpa_printf(MSG_WARNING, "RSN: Failed to initialize EAPOL " "state machines for pre-authentication"); - return -3; + ret = -3; + goto fail; } os_memset(&eapol_conf, 0, sizeof(eapol_conf)); eapol_conf.accept_802_1x_keys = 0; @@ -247,6 +251,15 @@ int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst, rsn_preauth_timeout, sm, NULL); return 0; + +fail: + if (sm->l2_preauth_br) { + l2_packet_deinit(sm->l2_preauth_br); + sm->l2_preauth_br = NULL; + } + l2_packet_deinit(sm->l2_preauth); + sm->l2_preauth = NULL; + return ret; } diff --git a/src/utils/common.c b/src/utils/common.c index 93f17227..5fd795f3 100644 --- a/src/utils/common.c +++ b/src/utils/common.c @@ -976,3 +976,89 @@ char * str_token(char *str, const char *delim, char **context) *context = end; return pos; } + + +size_t utf8_unescape(const char *inp, size_t in_size, + char *outp, size_t out_size) +{ + size_t res_size = 0; + + if (!inp || !outp) + return 0; + + if (!in_size) + in_size = os_strlen(inp); + + /* Advance past leading single quote */ + if (*inp == '\'' && in_size) { + inp++; + in_size--; + } + + while (in_size--) { + if (res_size >= out_size) + return 0; + + switch (*inp) { + case '\'': + /* Terminate on bare single quote */ + *outp = '\0'; + return res_size; + + case '\\': + if (!in_size--) + return 0; + inp++; + /* fall through */ + + default: + *outp++ = *inp++; + res_size++; + } + } + + /* NUL terminate if space allows */ + if (res_size < out_size) + *outp = '\0'; + + return res_size; +} + + +size_t utf8_escape(const char *inp, size_t in_size, + char *outp, size_t out_size) +{ + size_t res_size = 0; + + if (!inp || !outp) + return 0; + + /* inp may or may not be NUL terminated, but must be if 0 size + * is specified */ + if (!in_size) + in_size = os_strlen(inp); + + while (in_size--) { + if (res_size++ >= out_size) + return 0; + + switch (*inp) { + case '\\': + case '\'': + if (res_size++ >= out_size) + return 0; + *outp++ = '\\'; + /* fall through */ + + default: + *outp++ = *inp++; + break; + } + } + + /* NUL terminate if space allows */ + if (res_size < out_size) + *outp = '\0'; + + return res_size; +} diff --git a/src/utils/common.h b/src/utils/common.h index 82a51e57..576e8e7e 100644 --- a/src/utils/common.h +++ b/src/utils/common.h @@ -548,6 +548,10 @@ int random_mac_addr(u8 *addr); int random_mac_addr_keep_oui(u8 *addr); char * str_token(char *str, const char *delim, char **context); +size_t utf8_escape(const char *inp, size_t in_size, + char *outp, size_t out_size); +size_t utf8_unescape(const char *inp, size_t in_size, + char *outp, size_t out_size); /* diff --git a/src/wps/wps_common.c b/src/wps/wps_common.c index 222d4855..c1ede6a9 100644 --- a/src/wps/wps_common.c +++ b/src/wps/wps_common.c @@ -535,6 +535,9 @@ u16 wps_config_methods_str2bin(const char *str) #ifdef CONFIG_WPS_NFC methods |= WPS_CONFIG_NFC_INTERFACE; #endif /* CONFIG_WPS_NFC */ +#ifdef CONFIG_P2P + methods |= WPS_CONFIG_P2PS; +#endif /* CONFIG_P2P */ } else { if (os_strstr(str, "ethernet")) methods |= WPS_CONFIG_ETHERNET; @@ -560,6 +563,8 @@ u16 wps_config_methods_str2bin(const char *str) methods |= WPS_CONFIG_VIRT_PUSHBUTTON; if (os_strstr(str, "physical_push_button")) methods |= WPS_CONFIG_PHY_PUSHBUTTON; + if (os_strstr(str, "p2ps")) + methods |= WPS_CONFIG_P2PS; } return methods; diff --git a/src/wps/wps_defs.h b/src/wps/wps_defs.h index da005a41..25cd14a0 100644 --- a/src/wps/wps_defs.h +++ b/src/wps/wps_defs.h @@ -154,7 +154,8 @@ enum wps_dev_password_id { DEV_PW_REKEY = 0x0003, DEV_PW_PUSHBUTTON = 0x0004, DEV_PW_REGISTRAR_SPECIFIED = 0x0005, - DEV_PW_NFC_CONNECTION_HANDOVER = 0x0007 + DEV_PW_NFC_CONNECTION_HANDOVER = 0x0007, + DEV_PW_P2PS_DEFAULT = 0x0008 }; /* Message Type */ @@ -244,6 +245,7 @@ enum wps_error_indication { #define WPS_CONFIG_KEYPAD 0x0100 #define WPS_CONFIG_VIRT_PUSHBUTTON 0x0280 #define WPS_CONFIG_PHY_PUSHBUTTON 0x0480 +#define WPS_CONFIG_P2PS 0x1000 #define WPS_CONFIG_VIRT_DISPLAY 0x2008 #define WPS_CONFIG_PHY_DISPLAY 0x4008 diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c index 8ee1ea98..48b7e128 100644 --- a/src/wps/wps_registrar.c +++ b/src/wps/wps_registrar.c @@ -2578,6 +2578,7 @@ static enum wps_process_res wps_process_m1(struct wps_data *wps, if (wps->dev_pw_id < 0x10 && wps->dev_pw_id != DEV_PW_DEFAULT && + wps->dev_pw_id != DEV_PW_P2PS_DEFAULT && wps->dev_pw_id != DEV_PW_USER_SPECIFIED && wps->dev_pw_id != DEV_PW_MACHINE_SPECIFIED && wps->dev_pw_id != DEV_PW_REGISTRAR_SPECIFIED && diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk index 96a969e1..579582b6 100644 --- a/wpa_supplicant/Android.mk +++ b/wpa_supplicant/Android.mk @@ -1127,7 +1127,9 @@ ifdef CONFIG_INTERNAL_AES AESOBJS += src/crypto/aes-internal.c src/crypto/aes-internal-dec.c endif +ifneq ($(CONFIG_TLS), openssl) AESOBJS += src/crypto/aes-unwrap.c +endif ifdef NEED_AES_EAX AESOBJS += src/crypto/aes-eax.c NEED_AES_CTR=y @@ -1148,8 +1150,10 @@ endif endif ifdef NEED_AES_WRAP NEED_AES_ENC=y +ifneq ($(CONFIG_TLS), openssl) AESOBJS += src/crypto/aes-wrap.c endif +endif ifdef NEED_AES_CBC NEED_AES_ENC=y AESOBJS += src/crypto/aes-cbc.c @@ -1195,8 +1199,10 @@ endif MD5OBJS = ifndef CONFIG_FIPS +ifneq ($(CONFIG_TLS), openssl) MD5OBJS += src/crypto/md5.c endif +endif ifdef NEED_MD5 ifdef CONFIG_INTERNAL_MD5 MD5OBJS += src/crypto/md5-internal.c diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index 21486c4c..95fbe789 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -10,6 +10,7 @@ export LIBDIR ?= /usr/local/lib/ export BINDIR ?= /usr/local/sbin/ PKG_CONFIG ?= pkg-config +CFLAGS += $(EXTRA_CFLAGS) CFLAGS += -I$(abspath ../src) CFLAGS += -I$(abspath ../src/utils) @@ -1142,7 +1143,9 @@ ifdef CONFIG_INTERNAL_AES AESOBJS += ../src/crypto/aes-internal.o ../src/crypto/aes-internal-dec.o endif +ifneq ($(CONFIG_TLS), openssl) AESOBJS += ../src/crypto/aes-unwrap.o +endif ifdef NEED_AES_EAX AESOBJS += ../src/crypto/aes-eax.o NEED_AES_CTR=y @@ -1166,8 +1169,10 @@ AESOBJS += ../src/crypto/aes-siv.o endif ifdef NEED_AES_WRAP NEED_AES_ENC=y +ifneq ($(CONFIG_TLS), openssl) AESOBJS += ../src/crypto/aes-wrap.o endif +endif ifdef NEED_AES_CBC NEED_AES_ENC=y AESOBJS += ../src/crypto/aes-cbc.o @@ -1208,8 +1213,10 @@ endif endif ifndef CONFIG_FIPS +ifneq ($(CONFIG_TLS), openssl) MD5OBJS += ../src/crypto/md5.o endif +endif ifdef NEED_MD5 ifdef CONFIG_INTERNAL_MD5 MD5OBJS += ../src/crypto/md5-internal.o diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c index 33af428d..b15207dc 100644 --- a/wpa_supplicant/config_file.c +++ b/wpa_supplicant/config_file.c @@ -20,6 +20,9 @@ #include "eap_peer/eap_methods.h" #include "eap_peer/eap.h" +#ifdef ANDROID +#include <sys/stat.h> +#endif static int newline_terminated(const char *buf, size_t buflen) { @@ -1245,12 +1248,21 @@ int wpa_config_write(const char *name, struct wpa_config *config) struct wpa_config_blob *blob; #endif /* CONFIG_NO_CONFIG_BLOBS */ int ret = 0; + int tmp_len = os_strlen(name) + 5; /* allow space for .tmp suffix */ + char *tmp_name = os_malloc(tmp_len); - wpa_printf(MSG_DEBUG, "Writing configuration file '%s'", name); + if (tmp_name == NULL) + tmp_name = (char *)name; + else + os_snprintf(tmp_name, tmp_len, "%s.tmp", name); + + wpa_printf(MSG_DEBUG, "Writing configuration file '%s'", tmp_name); - f = fopen(name, "w"); + f = fopen(tmp_name, "w"); if (f == NULL) { - wpa_printf(MSG_DEBUG, "Failed to open '%s' for writing", name); + wpa_printf(MSG_DEBUG, "Failed to open '%s' for writing", tmp_name); + if (tmp_name != name) + os_free(tmp_name); return -1; } @@ -1285,6 +1297,17 @@ int wpa_config_write(const char *name, struct wpa_config *config) fclose(f); + if (tmp_name != name) { + int chmod_ret = 0; +#ifdef ANDROID + chmod_ret = chmod(tmp_name, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); +#endif + if (chmod_ret != 0 || rename(tmp_name, name) != 0) + ret = -1; + + os_free(tmp_name); + } + wpa_printf(MSG_DEBUG, "Configuration file '%s' written %ssuccessfully", name, ret ? "un" : ""); return ret; diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index caa480cb..c59ccc3c 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -1701,6 +1701,8 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, #ifdef CONFIG_HS20 const u8 *hs20; #endif /* CONFIG_HS20 */ + const u8 *sess_id; + size_t sess_id_len; if (os_strcmp(params, "-DRIVER") == 0) return wpa_drv_status(wpa_s, buf, buflen); @@ -1933,6 +1935,24 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, pos += res; } + sess_id = eapol_sm_get_session_id(wpa_s->eapol, &sess_id_len); + if (sess_id) { + char *start = pos; + + ret = os_snprintf(pos, end - pos, "eap_session_id="); + if (os_snprintf_error(end - pos, ret)) + return start - buf; + pos += ret; + ret = wpa_snprintf_hex(pos, end - pos, sess_id, sess_id_len); + if (ret <= 0) + return start - buf; + pos += ret; + ret = os_snprintf(pos, end - pos, "\n"); + if (os_snprintf_error(end - pos, ret)) + return start - buf; + pos += ret; + } + res = rsn_preauth_get_status(wpa_s->wpa, pos, end - pos, verbose); if (res >= 0) pos += res; @@ -4463,6 +4483,8 @@ static int p2p_ctrl_find(struct wpa_supplicant *wpa_s, char *cmd) u8 dev_type[WPS_DEV_TYPE_LEN], *_dev_type = NULL; char *pos; unsigned int search_delay; + const char *seek[P2P_MAX_QUERY_HASH + 1]; + u8 seek_count = 0; if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { wpa_dbg(wpa_s, MSG_INFO, @@ -4497,8 +4519,180 @@ static int p2p_ctrl_find(struct wpa_supplicant *wpa_s, char *cmd) } else search_delay = wpas_p2p_search_delay(wpa_s); + /* Must be searched for last, because it adds nul termination */ + pos = os_strstr(cmd, " seek="); + while (pos && seek_count < P2P_MAX_QUERY_HASH + 1) { + char *term; + + term = os_strchr(pos + 1, ' '); + seek[seek_count++] = pos + 6; + pos = os_strstr(pos + 6, " seek="); + + if (term) + *term = '\0'; + } + + if (!seek_count) + return wpas_p2p_find(wpa_s, timeout, type, _dev_type != NULL, + _dev_type, _dev_id, + search_delay, 0, NULL); + + if (seek_count > P2P_MAX_QUERY_HASH) { + seek[0] = NULL; + return wpas_p2p_find(wpa_s, timeout, type, _dev_type != NULL, + _dev_type, _dev_id, + search_delay, 1, seek); + } + return wpas_p2p_find(wpa_s, timeout, type, _dev_type != NULL, _dev_type, - _dev_id, search_delay); + _dev_id, search_delay, seek_count, seek); +} + + +static struct p2ps_provision * p2p_parse_asp_provision_cmd(const char *cmd) +{ + struct p2ps_provision *p2ps_prov; + char *pos; + size_t info_len = 0; + char *info = NULL; + u8 role = P2PS_SETUP_NONE; + long long unsigned val; + + pos = os_strstr(cmd, "info="); + if (pos) { + pos += 5; + info_len = os_strlen(pos); + + if (info_len) { + info = os_malloc(info_len + 1); + if (info) { + info_len = utf8_unescape(pos, info_len, + info, info_len + 1); + } else + info_len = 0; + } + } + + p2ps_prov = os_zalloc(sizeof(struct p2ps_provision) + info_len + 1); + if (p2ps_prov == NULL) { + os_free(info); + return NULL; + } + + if (info) { + os_memcpy(p2ps_prov->info, info, info_len); + p2ps_prov->info[info_len] = '\0'; + os_free(info); + } + + pos = os_strstr(cmd, "status="); + if (pos) + p2ps_prov->status = atoi(pos + 7); + else + p2ps_prov->status = -1; + + pos = os_strstr(cmd, "adv_id="); + if (!pos || sscanf(pos + 7, "%llx", &val) != 1 || val > 0xffffffffULL) + goto invalid_args; + p2ps_prov->adv_id = val; + + pos = os_strstr(cmd, "method="); + if (pos) + p2ps_prov->method = strtol(pos + 7, NULL, 16); + else + p2ps_prov->method = 0; + + pos = os_strstr(cmd, "session="); + if (!pos || sscanf(pos + 8, "%llx", &val) != 1 || val > 0xffffffffULL) + goto invalid_args; + p2ps_prov->session_id = val; + + pos = os_strstr(cmd, "adv_mac="); + if (!pos || hwaddr_aton(pos + 8, p2ps_prov->adv_mac)) + goto invalid_args; + + pos = os_strstr(cmd, "session_mac="); + if (!pos || hwaddr_aton(pos + 12, p2ps_prov->session_mac)) + goto invalid_args; + + /* force conncap with tstCap (no sanity checks) */ + pos = os_strstr(cmd, "tstCap="); + if (pos) { + role = strtol(pos + 7, NULL, 16); + } else { + pos = os_strstr(cmd, "role="); + if (pos) { + role = strtol(pos + 5, NULL, 16); + if (role != P2PS_SETUP_CLIENT && + role != P2PS_SETUP_GROUP_OWNER) + role = P2PS_SETUP_NONE; + } + } + p2ps_prov->role = role; + + return p2ps_prov; + +invalid_args: + os_free(p2ps_prov); + return NULL; +} + + +static int p2p_ctrl_asp_provision_resp(struct wpa_supplicant *wpa_s, char *cmd) +{ + u8 addr[ETH_ALEN]; + struct p2ps_provision *p2ps_prov; + char *pos; + + /* <addr> id=<adv_id> [role=<conncap>] [info=<infodata>] */ + + wpa_printf(MSG_DEBUG, "%s: %s", __func__, cmd); + + if (hwaddr_aton(cmd, addr)) + return -1; + + pos = cmd + 17; + if (*pos != ' ') + return -1; + + p2ps_prov = p2p_parse_asp_provision_cmd(pos); + if (!p2ps_prov) + return -1; + + if (p2ps_prov->status < 0) { + os_free(p2ps_prov); + return -1; + } + + return wpas_p2p_prov_disc(wpa_s, addr, NULL, WPAS_P2P_PD_FOR_ASP, + p2ps_prov); +} + + +static int p2p_ctrl_asp_provision(struct wpa_supplicant *wpa_s, char *cmd) +{ + u8 addr[ETH_ALEN]; + struct p2ps_provision *p2ps_prov; + char *pos; + + /* <addr> id=<adv_id> adv_mac=<adv_mac> conncap=<conncap> + * session=<ses_id> mac=<ses_mac> [info=<infodata>] + */ + + wpa_printf(MSG_DEBUG, "%s: %s", __func__, cmd); + if (hwaddr_aton(cmd, addr)) + return -1; + + pos = cmd + 17; + if (*pos != ' ') + return -1; + + p2ps_prov = p2p_parse_asp_provision_cmd(pos); + if (!p2ps_prov) + return -1; + + return wpas_p2p_prov_disc(wpa_s, addr, NULL, WPAS_P2P_PD_FOR_ASP, + p2ps_prov); } @@ -4520,7 +4714,7 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd, int pd; int ht40, vht; - /* <addr> <"pbc" | "pin" | PIN> [label|display|keypad] + /* <addr> <"pbc" | "pin" | PIN> [label|display|keypad|p2ps] * [persistent|persistent=<network id>] * [join] [auth] [go_intent=<0..15>] [freq=<in MHz>] [provdisc] * [ht40] [vht] */ @@ -4584,6 +4778,8 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd, *pos++ = '\0'; if (os_strncmp(pos, "display", 7) == 0) wps_method = WPS_PIN_DISPLAY; + else if (os_strncmp(pos, "p2ps", 4) == 0) + wps_method = WPS_P2PS; } if (!wps_pin_str_valid(pin)) { os_memcpy(buf, "FAIL-INVALID-PIN\n", 17); @@ -4650,7 +4846,7 @@ static int p2p_ctrl_prov_disc(struct wpa_supplicant *wpa_s, char *cmd) else if (os_strstr(pos, " auto") != NULL) use = WPAS_P2P_PD_AUTO; - return wpas_p2p_prov_disc(wpa_s, addr, pos, use); + return wpas_p2p_prov_disc(wpa_s, addr, pos, use, NULL); } @@ -4703,6 +4899,40 @@ static int p2p_ctrl_serv_disc_req(struct wpa_supplicant *wpa_s, char *cmd, } 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 if (os_strncmp(pos, "asp ", 4) == 0) { + char *svc_str; + char *svc_info = NULL; + u32 id; + + pos += 4; + if (sscanf(pos, "%x", &id) != 1 || id > 0xff) + return -1; + + pos = os_strchr(pos, ' '); + if (pos == NULL || pos[1] == '\0' || pos[1] == ' ') + return -1; + + svc_str = pos + 1; + + pos = os_strchr(svc_str, ' '); + + if (pos) + *pos++ = '\0'; + + /* All remaining data is the svc_info string */ + if (pos && pos[0] && pos[0] != ' ') { + len = os_strlen(pos); + + /* Unescape in place */ + len = utf8_unescape(pos, len, pos, len); + if (len > 0xff) + return -1; + + svc_info = pos; + } + + ref = wpas_p2p_sd_request_asp(wpa_s, dst, (u8) id, + svc_str, svc_info); } else { len = os_strlen(pos); if (len & 1) @@ -4865,6 +5095,106 @@ static int p2p_ctrl_service_add_upnp(struct wpa_supplicant *wpa_s, char *cmd) } +static int p2p_ctrl_service_add_asp(struct wpa_supplicant *wpa_s, + u8 replace, char *cmd) +{ + char *pos; + char *adv_str; + u32 auto_accept, adv_id, svc_state, config_methods; + char *svc_info = NULL; + + pos = os_strchr(cmd, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + + /* Auto-Accept value is mandatory, and must be one of the + * single values (0, 1, 2, 4) */ + auto_accept = atoi(cmd); + switch (auto_accept) { + case P2PS_SETUP_NONE: /* No auto-accept */ + case P2PS_SETUP_NEW: + case P2PS_SETUP_CLIENT: + case P2PS_SETUP_GROUP_OWNER: + break; + default: + return -1; + } + + /* Advertisement ID is mandatory */ + cmd = pos; + pos = os_strchr(cmd, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + + /* Handle Adv_ID == 0 (wildcard "org.wi-fi.wfds") internally. */ + if (sscanf(cmd, "%x", &adv_id) != 1 || adv_id == 0) + return -1; + + /* Only allow replacements if exist, and adds if not */ + if (wpas_p2p_service_p2ps_id_exists(wpa_s, adv_id)) { + if (!replace) + return -1; + } else { + if (replace) + return -1; + } + + /* svc_state between 0 - 0xff is mandatory */ + if (sscanf(pos, "%x", &svc_state) != 1 || svc_state > 0xff) + return -1; + + pos = os_strchr(pos, ' '); + if (pos == NULL) + return -1; + + /* config_methods is mandatory */ + pos++; + if (sscanf(pos, "%x", &config_methods) != 1) + return -1; + + if (!(config_methods & + (WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD | WPS_CONFIG_P2PS))) + return -1; + + pos = os_strchr(pos, ' '); + if (pos == NULL) + return -1; + + pos++; + adv_str = pos; + + /* Advertisement string is mandatory */ + if (!pos[0] || pos[0] == ' ') + return -1; + + /* Terminate svc string */ + pos = os_strchr(pos, ' '); + if (pos != NULL) + *pos++ = '\0'; + + /* Service and Response Information are optional */ + if (pos && pos[0]) { + size_t len; + + /* Note the bare ' included, which cannot exist legally + * in unescaped string. */ + svc_info = os_strstr(pos, "svc_info='"); + + if (svc_info) { + svc_info += 9; + len = os_strlen(svc_info); + utf8_unescape(svc_info, len, svc_info, len); + } + } + + return wpas_p2p_service_add_asp(wpa_s, auto_accept, adv_id, adv_str, + (u8) svc_state, (u16) config_methods, + svc_info); +} + + static int p2p_ctrl_service_add(struct wpa_supplicant *wpa_s, char *cmd) { char *pos; @@ -4878,6 +5208,8 @@ static int p2p_ctrl_service_add(struct wpa_supplicant *wpa_s, char *cmd) return p2p_ctrl_service_add_bonjour(wpa_s, pos); if (os_strcmp(cmd, "upnp") == 0) return p2p_ctrl_service_add_upnp(wpa_s, pos); + if (os_strcmp(cmd, "asp") == 0) + return p2p_ctrl_service_add_asp(wpa_s, 0, pos); wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd); return -1; } @@ -4925,6 +5257,17 @@ static int p2p_ctrl_service_del_upnp(struct wpa_supplicant *wpa_s, char *cmd) } +static int p2p_ctrl_service_del_asp(struct wpa_supplicant *wpa_s, char *cmd) +{ + u32 adv_id; + + if (sscanf(cmd, "%x", &adv_id) != 1) + return -1; + + return wpas_p2p_service_del_asp(wpa_s, adv_id); +} + + static int p2p_ctrl_service_del(struct wpa_supplicant *wpa_s, char *cmd) { char *pos; @@ -4938,6 +5281,25 @@ static int p2p_ctrl_service_del(struct wpa_supplicant *wpa_s, char *cmd) return p2p_ctrl_service_del_bonjour(wpa_s, pos); if (os_strcmp(cmd, "upnp") == 0) return p2p_ctrl_service_del_upnp(wpa_s, pos); + if (os_strcmp(cmd, "asp") == 0) + return p2p_ctrl_service_del_asp(wpa_s, pos); + wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd); + return -1; +} + + +static int p2p_ctrl_service_replace(struct wpa_supplicant *wpa_s, char *cmd) +{ + char *pos; + + pos = os_strchr(cmd, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + + if (os_strcmp(cmd, "asp") == 0) + return p2p_ctrl_service_add_asp(wpa_s, 1, pos); + wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd); return -1; } @@ -6201,6 +6563,7 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) p2p_wpa_s->p2p_disable_ip_addr_req = 0; os_free(p2p_wpa_s->global->p2p_go_avoid_freq.range); p2p_wpa_s->global->p2p_go_avoid_freq.range = NULL; + p2p_wpa_s->global->pending_p2ps_group = 0; #endif /* CONFIG_P2P */ #ifdef CONFIG_WPS_TESTING @@ -7634,13 +7997,19 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, #endif /* CONFIG_MESH */ #ifdef CONFIG_P2P } else if (os_strncmp(buf, "P2P_FIND ", 9) == 0) { - if (p2p_ctrl_find(wpa_s, buf + 9)) + if (p2p_ctrl_find(wpa_s, buf + 8)) reply_len = -1; } else if (os_strcmp(buf, "P2P_FIND") == 0) { if (p2p_ctrl_find(wpa_s, "")) reply_len = -1; } else if (os_strcmp(buf, "P2P_STOP_FIND") == 0) { wpas_p2p_stop_find(wpa_s); + } else if (os_strncmp(buf, "P2P_ASP_PROVISION ", 18) == 0) { + if (p2p_ctrl_asp_provision(wpa_s, buf + 18)) + reply_len = -1; + } else if (os_strncmp(buf, "P2P_ASP_PROVISION_RESP ", 23) == 0) { + if (p2p_ctrl_asp_provision_resp(wpa_s, buf + 23)) + reply_len = -1; } else if (os_strncmp(buf, "P2P_CONNECT ", 12) == 0) { reply_len = p2p_ctrl_connect(wpa_s, buf + 12, reply, reply_size); @@ -7686,6 +8055,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "P2P_SERVICE_DEL ", 16) == 0) { if (p2p_ctrl_service_del(wpa_s, buf + 16) < 0) reply_len = -1; + } else if (os_strncmp(buf, "P2P_SERVICE_REP ", 16) == 0) { + if (p2p_ctrl_service_replace(wpa_s, buf + 16) < 0) + reply_len = -1; } else if (os_strncmp(buf, "P2P_REJECT ", 11) == 0) { if (p2p_ctrl_reject(wpa_s, buf + 11) < 0) reply_len = -1; @@ -8279,6 +8651,7 @@ static char * wpas_global_ctrl_iface_redir_p2p(struct wpa_global *global, "P2P_SERV_DISC_EXTERNAL ", "P2P_SERVICE_ADD ", "P2P_SERVICE_DEL ", + "P2P_SERVICE_REP ", "P2P_REJECT ", "P2P_INVITE ", "P2P_PEER ", @@ -8292,6 +8665,8 @@ static char * wpas_global_ctrl_iface_redir_p2p(struct wpa_global *global, "NFC_GET_HANDOVER_SEL ", "NFC_GET_HANDOVER_REQ ", "NFC_REPORT_HANDOVER ", + "P2P_ASP_PROVISION ", + "P2P_ASP_PROVISION_RESP ", NULL }; int found = 0; diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c index 9c880a23..24822bfd 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c +++ b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c @@ -131,7 +131,7 @@ DBusMessage * wpas_dbus_handler_p2p_find(DBusMessage *message, wpa_s = wpa_s->p2p_dev; wpas_p2p_find(wpa_s, timeout, type, num_req_dev_types, req_dev_types, - NULL, 0); + NULL, 0, 0, NULL); os_free(req_dev_types); return reply; @@ -730,7 +730,7 @@ DBusMessage * wpas_dbus_handler_p2p_prov_disc_req(DBusMessage *message, wpa_s = wpa_s->p2p_dev; if (wpas_p2p_prov_disc(wpa_s, peer_addr, config_method, - WPAS_P2P_PD_FOR_GO_NEG) < 0) + WPAS_P2P_PD_FOR_GO_NEG, NULL) < 0) return wpas_dbus_error_unknown_error(message, "Failed to send provision discovery request"); diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index f1f8864e..539832ce 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -240,7 +240,7 @@ static void wpa_find_assoc_pmkid(struct wpa_supplicant *wpa_s) ie.pmkid + i * PMKID_LEN, NULL, NULL, 0); if (pmksa_set == 0) { - eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 1); + eapol_sm_notify_pmkid_attempt(wpa_s->eapol); break; } } diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c index 5ea046fa..5b66211a 100644 --- a/wpa_supplicant/interworking.c +++ b/wpa_supplicant/interworking.c @@ -250,8 +250,8 @@ static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s, struct wpabuf *extra = NULL; int all = wpa_s->fetch_all_anqp; - wpa_printf(MSG_DEBUG, "Interworking: ANQP Query Request to " MACSTR, - MAC2STR(bss->bssid)); + wpa_msg(wpa_s, MSG_DEBUG, "Interworking: ANQP Query Request to " MACSTR, + MAC2STR(bss->bssid)); wpa_s->interworking_gas_bss = bss; info_ids[num_info_ids++] = ANQP_CAPABILITY_LIST; @@ -312,14 +312,14 @@ static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s, res = gas_query_req(wpa_s->gas, bss->bssid, bss->freq, buf, interworking_anqp_resp_cb, wpa_s); if (res < 0) { - wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request"); + wpa_msg(wpa_s, MSG_DEBUG, "ANQP: Failed to send Query Request"); wpabuf_free(buf); ret = -1; eloop_register_timeout(0, 0, interworking_continue_anqp, wpa_s, NULL); } else - wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token " - "%u", res); + wpa_msg(wpa_s, MSG_DEBUG, + "ANQP: Query started with dialog token %u", res); return ret; } @@ -966,8 +966,8 @@ static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s, if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL) return -1; - wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR " (3GPP)", - MAC2STR(bss->bssid)); + wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Connect with " MACSTR + " (3GPP)", MAC2STR(bss->bssid)); if (already_connected(wpa_s, cred, bss)) { wpa_msg(wpa_s, MSG_INFO, INTERWORKING_ALREADY_CONNECTED MACSTR, @@ -1024,13 +1024,13 @@ static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s, break; } if (res < 0) { - wpa_printf(MSG_DEBUG, "Selected EAP method (%d) not supported", - eap_type); + wpa_msg(wpa_s, MSG_DEBUG, + "Selected EAP method (%d) not supported", eap_type); goto fail; } if (!cred->pcsc && set_root_nai(ssid, cred->imsi, prefix) < 0) { - wpa_printf(MSG_DEBUG, "Failed to set Root NAI"); + wpa_msg(wpa_s, MSG_DEBUG, "Failed to set Root NAI"); goto fail; } @@ -1503,8 +1503,8 @@ static int interworking_connect_roaming_consortium( { struct wpa_ssid *ssid; - wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR " based on " - "roaming consortium match", MAC2STR(bss->bssid)); + wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Connect with " MACSTR + " based on roaming consortium match", MAC2STR(bss->bssid)); if (already_connected(wpa_s, cred, bss)) { wpa_msg(wpa_s, MSG_INFO, INTERWORKING_ALREADY_CONNECTED MACSTR, @@ -1532,8 +1532,8 @@ static int interworking_connect_roaming_consortium( goto fail; if (cred->eap_method == NULL) { - wpa_printf(MSG_DEBUG, "Interworking: No EAP method set for " - "credential using roaming consortium"); + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: No EAP method set for credential using roaming consortium"); goto fail; } @@ -1572,8 +1572,9 @@ static int interworking_connect_helper(struct wpa_supplicant *wpa_s, return -1; if (disallowed_bssid(wpa_s, bss->bssid) || disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) { - wpa_printf(MSG_DEBUG, "Interworking: Reject connection to disallowed BSS " - MACSTR, MAC2STR(bss->bssid)); + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: Reject connection to disallowed BSS " + MACSTR, MAC2STR(bss->bssid)); return -1; } @@ -1586,27 +1587,26 @@ static int interworking_connect_helper(struct wpa_supplicant *wpa_s, * 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"); + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: Network does not use RSN"); return -1; } cred_rc = interworking_credentials_available_roaming_consortium( wpa_s, bss, 0, excl); if (cred_rc) { - wpa_printf(MSG_DEBUG, "Interworking: Highest roaming " - "consortium matching credential priority %d " - "sp_priority %d", - cred_rc->priority, cred_rc->sp_priority); + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: Highest roaming consortium matching credential priority %d sp_priority %d", + cred_rc->priority, cred_rc->sp_priority); if (allow_excluded && excl && !(*excl)) excl = NULL; } cred = interworking_credentials_available_realm(wpa_s, bss, 0, excl); if (cred) { - wpa_printf(MSG_DEBUG, "Interworking: Highest NAI Realm list " - "matching credential priority %d sp_priority %d", - cred->priority, cred->sp_priority); + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: Highest NAI Realm list matching credential priority %d sp_priority %d", + cred->priority, cred->sp_priority); if (allow_excluded && excl && !(*excl)) excl = NULL; } @@ -1614,22 +1614,22 @@ static int interworking_connect_helper(struct wpa_supplicant *wpa_s, cred_3gpp = interworking_credentials_available_3gpp(wpa_s, bss, 0, excl); if (cred_3gpp) { - wpa_printf(MSG_DEBUG, "Interworking: Highest 3GPP matching " - "credential priority %d sp_priority %d", - cred_3gpp->priority, cred_3gpp->sp_priority); + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: Highest 3GPP matching credential priority %d sp_priority %d", + cred_3gpp->priority, cred_3gpp->sp_priority); if (allow_excluded && excl && !(*excl)) excl = NULL; } if (!cred_rc && !cred && !cred_3gpp) { - wpa_printf(MSG_DEBUG, "Interworking: No full credential matches - consider options without BW(etc.) limits"); + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: No full credential matches - consider options without BW(etc.) limits"); cred_rc = interworking_credentials_available_roaming_consortium( wpa_s, bss, 1, excl); if (cred_rc) { - wpa_printf(MSG_DEBUG, "Interworking: Highest roaming " - "consortium matching credential priority %d " - "sp_priority %d (ignore BW)", - cred_rc->priority, cred_rc->sp_priority); + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: Highest roaming consortium matching credential priority %d sp_priority %d (ignore BW)", + cred_rc->priority, cred_rc->sp_priority); if (allow_excluded && excl && !(*excl)) excl = NULL; } @@ -1637,10 +1637,9 @@ static int interworking_connect_helper(struct wpa_supplicant *wpa_s, cred = interworking_credentials_available_realm(wpa_s, bss, 1, excl); if (cred) { - wpa_printf(MSG_DEBUG, "Interworking: Highest NAI Realm " - "list matching credential priority %d " - "sp_priority %d (ignore BW)", - cred->priority, cred->sp_priority); + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: Highest NAI Realm list matching credential priority %d sp_priority %d (ignore BW)", + cred->priority, cred->sp_priority); if (allow_excluded && excl && !(*excl)) excl = NULL; } @@ -1648,10 +1647,9 @@ static int interworking_connect_helper(struct wpa_supplicant *wpa_s, cred_3gpp = interworking_credentials_available_3gpp(wpa_s, bss, 1, excl); if (cred_3gpp) { - wpa_printf(MSG_DEBUG, "Interworking: Highest 3GPP " - "matching credential priority %d " - "sp_priority %d (ignore BW)", - cred_3gpp->priority, cred_3gpp->sp_priority); + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: Highest 3GPP matching credential priority %d sp_priority %d (ignore BW)", + cred_3gpp->priority, cred_3gpp->sp_priority); if (allow_excluded && excl && !(*excl)) excl = NULL; } @@ -1669,16 +1667,18 @@ static int interworking_connect_helper(struct wpa_supplicant *wpa_s, } if (cred == NULL) { - wpa_printf(MSG_DEBUG, "Interworking: No matching credentials " - "found for " MACSTR, MAC2STR(bss->bssid)); + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: No matching credentials found for " + MACSTR, MAC2STR(bss->bssid)); return -1; } realm = nai_realm_parse(bss->anqp ? bss->anqp->nai_realm : NULL, &count); if (realm == NULL) { - wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI " - "Realm list from " MACSTR, MAC2STR(bss->bssid)); + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: Could not parse NAI Realm list from " + MACSTR, MAC2STR(bss->bssid)); return -1; } @@ -1691,15 +1691,15 @@ static int interworking_connect_helper(struct wpa_supplicant *wpa_s, } if (!eap) { - wpa_printf(MSG_DEBUG, "Interworking: No matching credentials " - "and EAP method found for " MACSTR, - MAC2STR(bss->bssid)); + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: No matching credentials and EAP method found for " + MACSTR, MAC2STR(bss->bssid)); nai_realm_free(realm, count); return -1; } - wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR, - MAC2STR(bss->bssid)); + wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Connect with " MACSTR, + MAC2STR(bss->bssid)); if (already_connected(wpa_s, cred, bss)) { wpa_msg(wpa_s, MSG_INFO, INTERWORKING_ALREADY_CONNECTED MACSTR, @@ -1927,10 +1927,12 @@ static struct wpa_cred * interworking_credentials_available_3gpp( #if defined(PCSC_FUNCS) || defined(CONFIG_EAP_PROXY) compare: #endif /* PCSC_FUNCS || CONFIG_EAP_PROXY */ - wpa_printf(MSG_DEBUG, "Interworking: Parsing 3GPP info from " - MACSTR, MAC2STR(bss->bssid)); + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: Parsing 3GPP info from " MACSTR, + MAC2STR(bss->bssid)); ret = plmn_id_match(bss->anqp->anqp_3gpp, imsi, mnc_len); - wpa_printf(MSG_DEBUG, "PLMN match %sfound", ret ? "" : "not "); + wpa_msg(wpa_s, MSG_DEBUG, "PLMN match %sfound", + ret ? "" : "not "); if (ret) { if (cred_no_required_oi_match(cred, bss)) continue; @@ -1982,12 +1984,13 @@ static struct wpa_cred * interworking_credentials_available_realm( if (wpa_s->conf->cred == NULL) return NULL; - wpa_printf(MSG_DEBUG, "Interworking: Parsing NAI Realm list from " - MACSTR, MAC2STR(bss->bssid)); + wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Parsing NAI Realm list from " + MACSTR, MAC2STR(bss->bssid)); realm = nai_realm_parse(bss->anqp->nai_realm, &count); if (realm == NULL) { - wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI " - "Realm list from " MACSTR, MAC2STR(bss->bssid)); + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: Could not parse NAI Realm list from " + MACSTR, MAC2STR(bss->bssid)); return NULL; } @@ -2026,6 +2029,9 @@ static struct wpa_cred * interworking_credentials_available_realm( } } break; + } else { + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: realm-find-eap returned false"); } } } @@ -2166,8 +2172,9 @@ int interworking_home_sp_cred(struct wpa_supplicant *wpa_s, realm = os_strchr(nai, '@'); if (realm) realm++; - wpa_printf(MSG_DEBUG, "Interworking: Search for match " - "with SIM/USIM domain %s", realm); + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: Search for match with SIM/USIM domain %s", + realm); if (realm && domain_name_list_contains(domain_names, realm, 1)) return 1; @@ -2180,8 +2187,9 @@ int interworking_home_sp_cred(struct wpa_supplicant *wpa_s, return ret; for (i = 0; i < cred->num_domain; i++) { - wpa_printf(MSG_DEBUG, "Interworking: Search for match with " - "home SP FQDN %s", cred->domain[i]); + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: Search for match with home SP FQDN %s", + cred->domain[i]); if (domain_name_list_contains(domain_names, cred->domain[i], 1)) return 1; } @@ -2357,14 +2365,16 @@ static void interworking_select_network(struct wpa_supplicant *wpa_s) &excluded); 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)); + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: Credential match with " MACSTR + " but network does not use RSN", + MAC2STR(bss->bssid)); continue; } if (!excluded) @@ -2455,8 +2465,8 @@ static void interworking_select_network(struct wpa_supplicant *wpa_s) * have matching APs. */ if (interworking_find_network_match(wpa_s)) { - wpa_printf(MSG_DEBUG, "Interworking: Possible BSS " - "match for enabled network configurations"); + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: Possible BSS match for enabled network configurations"); if (wpa_s->auto_select) { interworking_reconnect(wpa_s); return; @@ -2464,8 +2474,8 @@ static void interworking_select_network(struct wpa_supplicant *wpa_s) } if (wpa_s->auto_network_select) { - wpa_printf(MSG_DEBUG, "Interworking: Continue " - "scanning after ANQP fetch"); + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: Continue scanning after ANQP fetch"); wpa_supplicant_req_scan(wpa_s, wpa_s->scan_interval, 0); return; @@ -2516,9 +2526,10 @@ interworking_match_anqp_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) os_memcmp(bss->ssid, other->ssid, bss->ssid_len) != 0) continue; - wpa_printf(MSG_DEBUG, "Interworking: Share ANQP data with " - "already fetched BSSID " MACSTR " and " MACSTR, - MAC2STR(other->bssid), MAC2STR(bss->bssid)); + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: Share ANQP data with already fetched BSSID " + MACSTR " and " MACSTR, + MAC2STR(other->bssid), MAC2STR(bss->bssid)); other->anqp->users++; return other->anqp; } @@ -2661,8 +2672,9 @@ int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, if (freq <= 0) return -1; - wpa_printf(MSG_DEBUG, "ANQP: Query Request to " MACSTR " for %u id(s)", - MAC2STR(dst), (unsigned int) num_ids); + wpa_msg(wpa_s, MSG_DEBUG, + "ANQP: Query Request to " MACSTR " for %u id(s)", + MAC2STR(dst), (unsigned int) num_ids); #ifdef CONFIG_HS20 if (subtypes != 0) { @@ -2680,12 +2692,13 @@ int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, res = gas_query_req(wpa_s->gas, dst, freq, buf, anqp_resp_cb, wpa_s); if (res < 0) { - wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request"); + wpa_msg(wpa_s, MSG_DEBUG, "ANQP: Failed to send Query Request"); wpabuf_free(buf); ret = -1; - } else - wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token " - "%u", res); + } else { + wpa_msg(wpa_s, MSG_DEBUG, + "ANQP: Query started with dialog token %u", res); + } return ret; } @@ -2801,22 +2814,23 @@ static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s, pos, slen); break; default: - wpa_printf(MSG_DEBUG, "HS20: Unsupported ANQP " - "vendor type %u", type); + wpa_msg(wpa_s, MSG_DEBUG, + "HS20: Unsupported ANQP vendor type %u", + type); break; } break; #endif /* CONFIG_HS20 */ default: - wpa_printf(MSG_DEBUG, "Interworking: Unsupported " - "vendor-specific ANQP OUI %06x", - WPA_GET_BE24(pos)); + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: Unsupported vendor-specific ANQP OUI %06x", + WPA_GET_BE24(pos)); return; } break; default: - wpa_printf(MSG_DEBUG, "Interworking: Unsupported ANQP Info ID " - "%u", info_id); + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: Unsupported ANQP Info ID %u", info_id); break; } } @@ -2848,8 +2862,8 @@ void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token, pos = wpabuf_head(adv_proto); if (wpabuf_len(adv_proto) < 4 || pos[0] != WLAN_EID_ADV_PROTO || pos[1] < 2 || pos[3] != ACCESS_NETWORK_QUERY_PROTOCOL) { - wpa_printf(MSG_DEBUG, "ANQP: Unexpected Advertisement " - "Protocol in response"); + wpa_msg(wpa_s, MSG_DEBUG, + "ANQP: Unexpected Advertisement Protocol in response"); if (wpa_s->fetch_osu_icon_in_progress) hs20_icon_fetch_failed(wpa_s); anqp_result = "INVALID_FRAME"; @@ -2878,7 +2892,7 @@ void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token, unsigned int left = end - pos; if (left < 4) { - wpa_printf(MSG_DEBUG, "ANQP: Invalid element"); + wpa_msg(wpa_s, MSG_DEBUG, "ANQP: Invalid element"); anqp_result = "INVALID_FRAME"; goto out_parse_done; } @@ -2888,8 +2902,9 @@ void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token, pos += 2; left -= 4; if (left < slen) { - wpa_printf(MSG_DEBUG, "ANQP: Invalid element length " - "for Info ID %u", info_id); + wpa_msg(wpa_s, MSG_DEBUG, + "ANQP: Invalid element length for Info ID %u", + info_id); anqp_result = "INVALID_FRAME"; goto out_parse_done; } @@ -2909,8 +2924,8 @@ out: static void interworking_scan_res_handler(struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res) { - wpa_printf(MSG_DEBUG, "Interworking: Scan results available - start " - "ANQP fetch"); + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: Scan results available - start ANQP fetch"); interworking_start_fetch_anqp(wpa_s); } @@ -2924,8 +2939,8 @@ int interworking_select(struct wpa_supplicant *wpa_s, int auto_select, wpa_s->auto_select = !!auto_select; wpa_s->fetch_all_anqp = 0; wpa_s->fetch_osu_info = 0; - wpa_printf(MSG_DEBUG, "Interworking: Start scan for network " - "selection"); + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: Start scan for network selection"); wpa_s->scan_res_handler = interworking_scan_res_handler; wpa_s->normal_scans = 0; wpa_s->scan_req = MANUAL_SCAN_REQ; @@ -2986,8 +3001,8 @@ int gas_send_request(struct wpa_supplicant *wpa_s, const u8 *dst, if (freq <= 0) return -1; - wpa_printf(MSG_DEBUG, "GAS request to " MACSTR " (freq %d MHz)", - MAC2STR(dst), freq); + wpa_msg(wpa_s, 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); @@ -3013,12 +3028,12 @@ int gas_send_request(struct wpa_supplicant *wpa_s, const u8 *dst, 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"); + wpa_msg(wpa_s, MSG_DEBUG, "GAS: Failed to send Query Request"); wpabuf_free(buf); ret = -1; } else - wpa_printf(MSG_DEBUG, "GAS: Query started with dialog token " - "%u", res); + wpa_msg(wpa_s, MSG_DEBUG, + "GAS: Query started with dialog token %u", res); return ret; } diff --git a/wpa_supplicant/mesh_mpm.c b/wpa_supplicant/mesh_mpm.c index 4a259ff4..1d6f2be2 100644 --- a/wpa_supplicant/mesh_mpm.c +++ b/wpa_supplicant/mesh_mpm.c @@ -193,6 +193,11 @@ static void mesh_mpm_init_link(struct wpa_supplicant *wpa_s, sta->my_lid = llid; sta->peer_lid = 0; + + /* + * We do not use wpa_mesh_set_plink_state() here because there is no + * entry in kernel yet. + */ sta->plink_state = PLINK_LISTEN; } @@ -348,9 +353,9 @@ fail: /* configure peering state in ours and driver's station entry */ -static void -wpa_mesh_set_plink_state(struct wpa_supplicant *wpa_s, struct sta_info *sta, - enum mesh_plink_state state) +void wpa_mesh_set_plink_state(struct wpa_supplicant *wpa_s, + struct sta_info *sta, + enum mesh_plink_state state) { struct hostapd_sta_add_params params; int ret; @@ -379,14 +384,7 @@ static void mesh_mpm_fsm_restart(struct wpa_supplicant *wpa_s, eloop_cancel_timeout(plink_timer, wpa_s, sta); - if (sta->mpm_close_reason == WLAN_REASON_MESH_CLOSE_RCVD) { - ap_free_sta(hapd, sta); - return; - } - - wpa_mesh_set_plink_state(wpa_s, sta, PLINK_LISTEN); - sta->my_lid = sta->peer_lid = sta->mpm_close_reason = 0; - sta->mpm_retries = 0; + ap_free_sta(hapd, sta); } @@ -417,7 +415,7 @@ static void plink_timer(void *eloop_ctx, void *user_data) /* confirm timer */ if (!reason) reason = WLAN_REASON_MESH_CONFIRM_TIMEOUT; - sta->plink_state = PLINK_HOLDING; + wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING); eloop_register_timeout(conf->dot11MeshHoldingTimeout / 1000, (conf->dot11MeshHoldingTimeout % 1000) * 1000, plink_timer, wpa_s, sta); @@ -521,27 +519,34 @@ void mesh_mpm_auth_peer(struct wpa_supplicant *wpa_s, const u8 *addr) mesh_mpm_plink_open(wpa_s, sta, PLINK_OPEN_SENT); } - -void wpa_mesh_new_mesh_peer(struct wpa_supplicant *wpa_s, const u8 *addr, - struct ieee802_11_elems *elems) +/* + * Initialize a sta_info structure for a peer and upload it into the driver + * in preparation for beginning authentication or peering. This is done when a + * Beacon (secure or open mesh) or a peering open frame (for open mesh) is + * received from the peer for the first time. + */ +static struct sta_info * mesh_mpm_add_peer(struct wpa_supplicant *wpa_s, + const u8 *addr, + struct ieee802_11_elems *elems) { struct hostapd_sta_add_params params; struct mesh_conf *conf = wpa_s->ifmsh->mconf; struct hostapd_data *data = wpa_s->ifmsh->bss[0]; struct sta_info *sta; - struct wpa_ssid *ssid = wpa_s->current_ssid; - int ret = 0; + int ret; sta = ap_get_sta(data, addr); if (!sta) { sta = ap_sta_add(data, addr); if (!sta) - return; + return NULL; } /* initialize sta */ - if (copy_supp_rates(wpa_s, sta, elems)) - return; + if (copy_supp_rates(wpa_s, sta, elems)) { + ap_free_sta(data, sta); + return NULL; + } mesh_mpm_init_link(wpa_s, sta); @@ -575,9 +580,26 @@ void wpa_mesh_new_mesh_peer(struct wpa_supplicant *wpa_s, const u8 *addr, wpa_msg(wpa_s, MSG_ERROR, "Driver failed to insert " MACSTR ": %d", MAC2STR(addr), ret); - return; + ap_free_sta(data, sta); + return NULL; } + return sta; +} + + +void wpa_mesh_new_mesh_peer(struct wpa_supplicant *wpa_s, const u8 *addr, + struct ieee802_11_elems *elems) +{ + struct mesh_conf *conf = wpa_s->ifmsh->mconf; + struct hostapd_data *data = wpa_s->ifmsh->bss[0]; + struct sta_info *sta; + struct wpa_ssid *ssid = wpa_s->current_ssid; + + sta = mesh_mpm_add_peer(wpa_s, addr, elems); + if (!sta) + return; + if (ssid && ssid->no_auto_peer) { wpa_msg(wpa_s, MSG_INFO, "will not initiate new peer link with " MACSTR " because of no_auto_peer", MAC2STR(addr)); @@ -930,6 +952,15 @@ void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s, wpa_printf(MSG_DEBUG, "MPM: plid=0x%x llid=0x%x", plid, llid); sta = ap_get_sta(hapd, mgmt->sa); + + /* + * If this is an open frame from an unknown STA, and this is an + * open mesh, then go ahead and add the peer before proceeding. + */ + if (!sta && action_field == PLINK_OPEN && + !(mconf->security & MESH_CONF_SEC_AMPE)) + sta = mesh_mpm_add_peer(wpa_s, mgmt->sa, &elems); + if (!sta) { wpa_printf(MSG_DEBUG, "MPM: No STA entry for peer"); return; diff --git a/wpa_supplicant/mesh_mpm.h b/wpa_supplicant/mesh_mpm.h index 2f7f6a78..7ebaef0c 100644 --- a/wpa_supplicant/mesh_mpm.h +++ b/wpa_supplicant/mesh_mpm.h @@ -15,6 +15,9 @@ void wpa_mesh_new_mesh_peer(struct wpa_supplicant *wpa_s, const u8 *addr, void mesh_mpm_deinit(struct wpa_supplicant *wpa_s, struct hostapd_iface *ifmsh); void mesh_mpm_auth_peer(struct wpa_supplicant *wpa_s, const u8 *addr); void mesh_mpm_free_sta(struct sta_info *sta); +void wpa_mesh_set_plink_state(struct wpa_supplicant *wpa_s, + struct sta_info *sta, + enum mesh_plink_state state); #ifdef CONFIG_MESH diff --git a/wpa_supplicant/mesh_rsn.c b/wpa_supplicant/mesh_rsn.c index aee325a3..da4cb03c 100644 --- a/wpa_supplicant/mesh_rsn.c +++ b/wpa_supplicant/mesh_rsn.c @@ -41,7 +41,7 @@ void mesh_auth_timer(void *eloop_ctx, void *user_data) mesh_rsn_auth_sae_sta(wpa_s, sta); } else { /* block the STA if exceeded the number of attempts */ - sta->plink_state = PLINK_BLOCKED; + wpa_mesh_set_plink_state(wpa_s, sta, PLINK_BLOCKED); sta->sae->state = SAE_NOTHING; } sta->sae_auth_retry++; diff --git a/wpa_supplicant/offchannel.c b/wpa_supplicant/offchannel.c index 7a863476..63af83af 100644 --- a/wpa_supplicant/offchannel.c +++ b/wpa_supplicant/offchannel.c @@ -354,15 +354,18 @@ int offchannel_send_action(struct wpa_supplicant *wpa_s, unsigned int freq, */ void offchannel_send_action_done(struct wpa_supplicant *wpa_s) { - wpa_printf(MSG_DEBUG, "Off-channel: Action frame sequence done " - "notification"); + wpa_printf(MSG_DEBUG, + "Off-channel: Action frame sequence done notification: pending_action_tx=%p drv_offchan_tx=%d action_tx_wait_time=%d off_channel_freq=%d roc_waiting_drv_freq=%d", + wpa_s->pending_action_tx, + !!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX), + wpa_s->action_tx_wait_time, wpa_s->off_channel_freq, + wpa_s->roc_waiting_drv_freq); wpabuf_free(wpa_s->pending_action_tx); wpa_s->pending_action_tx = NULL; if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX && wpa_s->action_tx_wait_time) wpa_drv_send_action_cancel_wait(wpa_s); - - if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) { + else if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) { wpa_drv_cancel_remain_on_channel(wpa_s); wpa_s->off_channel_freq = 0; wpa_s->roc_waiting_drv_freq = 0; diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c index 65c1b486..9e1d6656 100644 --- a/wpa_supplicant/p2p_supplicant.c +++ b/wpa_supplicant/p2p_supplicant.c @@ -123,6 +123,8 @@ static void wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s); static void wpas_stop_listen(void *ctx); static void wpas_p2p_psk_failure_removal(void *eloop_ctx, void *timeout_ctx); static void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s); +static int wpas_p2p_add_group_interface(struct wpa_supplicant *wpa_s, + enum wpa_driver_if_type type); /* @@ -475,6 +477,287 @@ static int wpas_p2p_disconnect_safely(struct wpa_supplicant *wpa_s, } +/* Determine total number of clients in active groups where we are the GO */ +static unsigned int p2p_group_go_member_count(struct wpa_supplicant *wpa_s) +{ + unsigned int count = 0; + struct wpa_ssid *s; + + for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) { + for (s = wpa_s->conf->ssid; s; s = s->next) { + wpa_printf(MSG_DEBUG, + "P2P: sup:%p ssid:%p disabled:%d p2p:%d mode:%d", + wpa_s, s, s->disabled, s->p2p_group, + s->mode); + if (!s->disabled && s->p2p_group && + s->mode == WPAS_MODE_P2P_GO) { + count += p2p_get_group_num_members( + wpa_s->p2p_group); + } + } + } + + return count; +} + + +/* Find an interface for a P2P group where we are the GO */ +static struct wpa_supplicant * +wpas_p2p_get_go_group(struct wpa_supplicant *wpa_s) +{ + struct wpa_supplicant *save = NULL; + struct wpa_ssid *s; + + if (!wpa_s) + return NULL; + + for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) { + for (s = wpa_s->conf->ssid; s; s = s->next) { + if (s->disabled || !s->p2p_group || + s->mode != WPAS_MODE_P2P_GO) + continue; + + /* Prefer a group with connected clients */ + if (p2p_get_group_num_members(wpa_s->p2p_group)) + return wpa_s; + save = wpa_s; + } + } + + /* No group with connected clients, so pick the one without (if any) */ + return save; +} + + +/* Find an active P2P group where we are the GO */ +static struct wpa_ssid * wpas_p2p_group_go_ssid(struct wpa_supplicant *wpa_s, + u8 *bssid) +{ + struct wpa_ssid *s, *empty = NULL; + + if (!wpa_s) + return 0; + + for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) { + for (s = wpa_s->conf->ssid; s; s = s->next) { + if (s->disabled || !s->p2p_group || + s->mode != WPAS_MODE_P2P_GO) + continue; + + os_memcpy(bssid, wpa_s->own_addr, ETH_ALEN); + if (p2p_get_group_num_members(wpa_s->p2p_group)) + return s; + empty = s; + } + } + + return empty; +} + + +/* Find a persistent group where we are the GO */ +static struct wpa_ssid * +wpas_p2p_get_persistent_go(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *s; + + for (s = wpa_s->conf->ssid; s; s = s->next) { + if (s->disabled == 2 && s->mode == WPAS_MODE_P2P_GO) + return s; + } + + return NULL; +} + + +static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role) +{ + struct wpa_supplicant *wpa_s = ctx, *tmp_wpa_s; + struct wpa_ssid *s; + u8 conncap = P2PS_SETUP_NONE; + unsigned int owned_members = 0; + unsigned int owner = 0; + unsigned int client = 0; + struct wpa_supplicant *go_wpa_s; + struct wpa_ssid *persistent_go; + int p2p_no_group_iface; + + wpa_printf(MSG_DEBUG, "P2P: Conncap - in:%d role:%d", incoming, role); + + /* + * For non-concurrent capable devices: + * If persistent_go, then no new. + * If GO, then no client. + * If client, then no GO. + */ + go_wpa_s = wpas_p2p_get_go_group(wpa_s); + persistent_go = wpas_p2p_get_persistent_go(wpa_s); + p2p_no_group_iface = wpa_s->conf->p2p_no_group_iface; + + wpa_printf(MSG_DEBUG, "P2P: GO(iface)=%p persistent(ssid)=%p", + go_wpa_s, persistent_go); + + for (tmp_wpa_s = wpa_s->global->ifaces; tmp_wpa_s; + tmp_wpa_s = tmp_wpa_s->next) { + for (s = tmp_wpa_s->conf->ssid; s; s = s->next) { + wpa_printf(MSG_DEBUG, + "P2P: sup:%p ssid:%p disabled:%d p2p:%d mode:%d", + tmp_wpa_s, s, s->disabled, + s->p2p_group, s->mode); + if (!s->disabled && s->p2p_group) { + if (s->mode == WPAS_MODE_P2P_GO) { + owned_members += + p2p_get_group_num_members( + tmp_wpa_s->p2p_group); + owner++; + } else + client++; + } + } + } + + /* If not concurrent, restrict our choices */ + if (p2p_no_group_iface) { + wpa_printf(MSG_DEBUG, "P2P: p2p_no_group_iface"); + + if (client) + return P2PS_SETUP_NONE; + + if (go_wpa_s) { + if (role == P2PS_SETUP_CLIENT || + incoming == P2PS_SETUP_GROUP_OWNER || + p2p_client_limit_reached(go_wpa_s->p2p_group)) + return P2PS_SETUP_NONE; + + return P2PS_SETUP_GROUP_OWNER; + } + + if (persistent_go) { + if (role == P2PS_SETUP_NONE || role == P2PS_SETUP_NEW) { + if (!incoming) + return P2PS_SETUP_GROUP_OWNER | + P2PS_SETUP_CLIENT; + if (incoming == P2PS_SETUP_NEW) { + u8 r; + + if (os_get_random(&r, sizeof(r)) < 0 || + (r & 1)) + return P2PS_SETUP_CLIENT; + return P2PS_SETUP_GROUP_OWNER; + } + } + } + } + + /* If a required role has been specified, handle it here */ + if (role && role != P2PS_SETUP_NEW) { + switch (incoming) { + case P2PS_SETUP_NONE: + case P2PS_SETUP_NEW: + case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT: + case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW: + conncap = role; + goto grp_owner; + + case P2PS_SETUP_GROUP_OWNER: + /* + * Must be a complimentary role - cannot be a client to + * more than one peer. + */ + if (incoming == role || client) + return P2PS_SETUP_NONE; + + return P2PS_SETUP_CLIENT; + + case P2PS_SETUP_CLIENT: + /* Must be a complimentary role */ + if (incoming != role) { + conncap = P2PS_SETUP_GROUP_OWNER; + goto grp_owner; + } + + default: + return P2PS_SETUP_NONE; + } + } + + /* + * For now, we only will support ownership of one group, and being a + * client of one group. Therefore, if we have either an existing GO + * group, or an existing client group, we will not do a new GO + * negotiation, but rather try to re-use the existing groups. + */ + switch (incoming) { + case P2PS_SETUP_NONE: + case P2PS_SETUP_NEW: + if (client) + conncap = P2PS_SETUP_GROUP_OWNER; + else if (!owned_members) + conncap = P2PS_SETUP_NEW; + else if (incoming == P2PS_SETUP_NONE) + conncap = P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT; + else + conncap = P2PS_SETUP_CLIENT; + break; + + case P2PS_SETUP_CLIENT: + conncap = P2PS_SETUP_GROUP_OWNER; + break; + + case P2PS_SETUP_GROUP_OWNER: + if (!client) + conncap = P2PS_SETUP_CLIENT; + break; + + case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW: + case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT: + if (client) + conncap = P2PS_SETUP_GROUP_OWNER; + else { + u8 r; + + if (os_get_random(&r, sizeof(r)) < 0 || + (r & 1)) + conncap = P2PS_SETUP_CLIENT; + else + conncap = P2PS_SETUP_GROUP_OWNER; + } + break; + + default: + return P2PS_SETUP_NONE; + } + +grp_owner: + if ((conncap & P2PS_SETUP_GROUP_OWNER) || + (!incoming && (conncap & P2PS_SETUP_NEW))) { + if (go_wpa_s && p2p_client_limit_reached(go_wpa_s->p2p_group)) + conncap &= ~P2PS_SETUP_GROUP_OWNER; + wpa_printf(MSG_DEBUG, "P2P: GOs:%d members:%d conncap:%d", + owner, owned_members, conncap); + + s = wpas_p2p_get_persistent_go(wpa_s); + + if (!s && !owner && p2p_no_group_iface) { + p2p_set_intended_addr(wpa_s->global->p2p, + wpa_s->own_addr); + } else if (!s && !owner) { + if (wpas_p2p_add_group_interface(wpa_s, + WPA_IF_P2P_GO) < 0) { + wpa_printf(MSG_ERROR, + "P2P: Failed to allocate a new interface for the group"); + return P2PS_SETUP_NONE; + } + wpa_s->global->pending_group_iface_for_p2ps = 1; + p2p_set_intended_addr(wpa_s->global->p2p, + wpa_s->pending_interface_addr); + } + } + + return conncap; +} + + static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, enum p2p_group_removal_reason removal_reason) { @@ -1268,6 +1551,8 @@ static void wpas_start_wps_enrollee(struct wpa_supplicant *wpa_s, #endif /* CONFIG_WPS_NFC */ } else { u16 dev_pw_id = DEV_PW_DEFAULT; + if (wpa_s->p2p_wps_method == WPS_P2PS) + dev_pw_id = DEV_PW_P2PS_DEFAULT; if (wpa_s->p2p_wps_method == WPS_PIN_KEYPAD) dev_pw_id = DEV_PW_REGISTRAR_SPECIFIED; wpas_wps_start_pin(wpa_s, res->peer_interface_addr, @@ -1348,6 +1633,16 @@ static void p2p_go_save_group_common_freqs(struct wpa_supplicant *wpa_s, } +static void p2p_config_write(struct wpa_supplicant *wpa_s) +{ +#ifndef CONFIG_NO_CONFIG_WRITE + if (wpa_s->parent->conf->update_config && + wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf)) + wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration"); +#endif /* CONFIG_NO_CONFIG_WRITE */ +} + + static void p2p_go_configured(void *ctx, void *data) { struct wpa_supplicant *wpa_s = ctx; @@ -1371,6 +1666,16 @@ static void p2p_go_configured(void *ctx, void *data) params->persistent_group, ""); wpa_s->group_formation_reported = 1; + if (wpa_s->parent->p2ps_join_addr_valid) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2PS: Setting default PIN for " MACSTR, + MAC2STR(wpa_s->parent->p2ps_join_addr)); + wpa_supplicant_ap_wps_pin(wpa_s, + wpa_s->parent->p2ps_join_addr, + "12345670", NULL, 0, 0); + wpa_s->parent->p2ps_join_addr_valid = 0; + } + os_get_reltime(&wpa_s->global->p2p_go_wait_client); if (params->persistent_group) { network_id = wpas_p2p_store_persistent_group( @@ -1639,6 +1944,7 @@ static void wpas_p2p_remove_pending_group_interface( wpa_s->pending_interface_name); os_memset(wpa_s->pending_interface_addr, 0, ETH_ALEN); wpa_s->pending_interface_name[0] = '\0'; + wpa_s->global->pending_group_iface_for_p2ps = 0; } @@ -1684,6 +1990,7 @@ wpas_p2p_init_group_interface(struct wpa_supplicant *wpa_s, int go) group_wpa_s->p2p_group_interface = go ? P2P_GROUP_INTERFACE_GO : P2P_GROUP_INTERFACE_CLIENT; wpa_s->global->p2p_group_formation = group_wpa_s; + wpa_s->global->pending_group_iface_for_p2ps = 0; wpas_p2p_clone_config(group_wpa_s, wpa_s); @@ -1847,6 +2154,52 @@ static void wpas_dev_found(void *ctx, const u8 *addr, WFD_SUBELEM_DEVICE_INFO); #endif /* CONFIG_WIFI_DISPLAY */ + if (info->p2ps_instance) { + char str[256]; + const u8 *buf = wpabuf_head(info->p2ps_instance); + size_t len = wpabuf_len(info->p2ps_instance); + + while (len) { + u32 id; + u16 methods; + u8 str_len; + + if (len < 4 + 2 + 1) + break; + id = WPA_GET_LE32(buf); + buf += sizeof(u32); + methods = WPA_GET_BE16(buf); + buf += sizeof(u16); + str_len = *buf++; + if (str_len > len - 4 - 2 - 1) + break; + os_memcpy(str, buf, str_len); + str[str_len] = '\0'; + buf += str_len; + len -= str_len + sizeof(u32) + sizeof(u16) + sizeof(u8); + + wpa_msg_global(wpa_s, MSG_INFO, + P2P_EVENT_DEVICE_FOUND MACSTR + " p2p_dev_addr=" MACSTR + " pri_dev_type=%s name='%s'" + " config_methods=0x%x" + " dev_capab=0x%x" + " group_capab=0x%x" + " adv_id=%x asp_svc=%s%s", + MAC2STR(addr), + MAC2STR(info->p2p_device_addr), + wps_dev_type_bin2str( + info->pri_dev_type, + devtype, sizeof(devtype)), + info->device_name, methods, + info->dev_capab, info->group_capab, + id, str, + info->vendor_elems ? + " vendor_elems=1" : ""); + } + goto done; + } + wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_DEVICE_FOUND MACSTR " p2p_dev_addr=" MACSTR " pri_dev_type=%s name='%s' config_methods=0x%x " @@ -1861,6 +2214,7 @@ static void wpas_dev_found(void *ctx, const u8 *addr, info->vendor_elems ? " vendor_elems=1" : "", new_device); +done: os_free(wfd_dev_info_hex); #endif /* CONFIG_NO_STDOUT_DEBUG */ @@ -2187,8 +2541,8 @@ wpas_p2p_service_get_upnp(struct wpa_supplicant *wpa_s, u8 version, } -static void wpas_sd_add_proto_not_avail(struct wpabuf *resp, u8 srv_proto, - u8 srv_trans_id) +static void wpas_sd_add_empty(struct wpabuf *resp, u8 srv_proto, + u8 srv_trans_id, u8 status) { u8 *len_pos; @@ -2200,12 +2554,35 @@ static void wpas_sd_add_proto_not_avail(struct wpabuf *resp, u8 srv_proto, wpabuf_put_u8(resp, srv_proto); wpabuf_put_u8(resp, srv_trans_id); /* Status Code */ - wpabuf_put_u8(resp, P2P_SD_PROTO_NOT_AVAILABLE); + wpabuf_put_u8(resp, status); /* Response Data: empty */ WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2); } +static void wpas_sd_add_proto_not_avail(struct wpabuf *resp, u8 srv_proto, + u8 srv_trans_id) +{ + wpas_sd_add_empty(resp, srv_proto, srv_trans_id, + P2P_SD_PROTO_NOT_AVAILABLE); +} + + +static void wpas_sd_add_bad_request(struct wpabuf *resp, u8 srv_proto, + u8 srv_trans_id) +{ + wpas_sd_add_empty(resp, srv_proto, srv_trans_id, P2P_SD_BAD_REQUEST); +} + + +static void wpas_sd_add_not_found(struct wpabuf *resp, u8 srv_proto, + u8 srv_trans_id) +{ + wpas_sd_add_empty(resp, srv_proto, srv_trans_id, + P2P_SD_REQUESTED_INFO_NOT_AVAILABLE); +} + + static void wpas_sd_all_bonjour(struct wpa_supplicant *wpa_s, struct wpabuf *resp, u8 srv_trans_id) { @@ -2513,6 +2890,148 @@ static void wpas_sd_req_wfd(struct wpa_supplicant *wpa_s, #endif /* CONFIG_WIFI_DISPLAY */ +static int find_p2ps_substr(struct p2ps_advertisement *adv_data, + const u8 *needle, size_t needle_len) +{ + const u8 *haystack = (const u8 *) adv_data->svc_info; + size_t haystack_len, i; + + /* Allow search term to be empty */ + if (!needle || !needle_len) + return 1; + + if (!haystack) + return 0; + + haystack_len = os_strlen(adv_data->svc_info); + for (i = 0; i < haystack_len; i++) { + if (haystack_len - i < needle_len) + break; + if (os_memcmp(haystack + i, needle, needle_len) == 0) + return 1; + } + + return 0; +} + + +static void wpas_sd_req_asp(struct wpa_supplicant *wpa_s, + struct wpabuf *resp, u8 srv_trans_id, + const u8 *query, size_t query_len) +{ + struct p2ps_advertisement *adv_data; + const u8 *svc = &query[1]; + const u8 *info = NULL; + size_t svc_len = query[0]; + size_t info_len = 0; + int prefix = 0; + u8 *count_pos = NULL; + u8 *len_pos = NULL; + + wpa_hexdump(MSG_DEBUG, "P2P: SD Request for ASP", query, query_len); + + if (!wpa_s->global->p2p) { + wpa_printf(MSG_DEBUG, "P2P: ASP protocol not available"); + wpas_sd_add_proto_not_avail(resp, P2P_SERV_P2PS, srv_trans_id); + return; + } + + /* Info block is optional */ + if (svc_len + 1 < query_len) { + info = &svc[svc_len]; + info_len = *info++; + } + + /* Range check length of svc string and info block */ + if (svc_len + (info_len ? info_len + 2 : 1) > query_len) { + wpa_printf(MSG_DEBUG, "P2P: ASP bad request"); + wpas_sd_add_bad_request(resp, P2P_SERV_P2PS, srv_trans_id); + return; + } + + /* Detect and correct for prefix search */ + if (svc_len && svc[svc_len - 1] == '*') { + prefix = 1; + svc_len--; + } + + for (adv_data = p2p_get_p2ps_adv_list(wpa_s->global->p2p); + adv_data; adv_data = adv_data->next) { + /* If not a prefix match, reject length mismatches */ + if (!prefix && svc_len != os_strlen(adv_data->svc_name)) + continue; + + /* Search each service for request */ + if (os_memcmp(adv_data->svc_name, svc, svc_len) == 0 && + find_p2ps_substr(adv_data, info, info_len)) { + size_t len = os_strlen(adv_data->svc_name); + size_t svc_info_len = 0; + + if (adv_data->svc_info) + svc_info_len = os_strlen(adv_data->svc_info); + + if (len > 0xff || svc_info_len > 0xffff) + return; + + /* Length & Count to be filled as we go */ + if (!len_pos && !count_pos) { + if (wpabuf_tailroom(resp) < + len + svc_info_len + 16) + return; + + len_pos = wpabuf_put(resp, 2); + wpabuf_put_u8(resp, P2P_SERV_P2PS); + wpabuf_put_u8(resp, srv_trans_id); + /* Status Code */ + wpabuf_put_u8(resp, P2P_SD_SUCCESS); + count_pos = wpabuf_put(resp, 1); + *count_pos = 0; + } else if (wpabuf_tailroom(resp) < + len + svc_info_len + 10) + return; + + if (svc_info_len) { + wpa_printf(MSG_DEBUG, + "P2P: Add Svc: %s info: %s", + adv_data->svc_name, + adv_data->svc_info); + } else { + wpa_printf(MSG_DEBUG, "P2P: Add Svc: %s", + adv_data->svc_name); + } + + /* Advertisement ID */ + wpabuf_put_le32(resp, adv_data->id); + + /* Config Methods */ + wpabuf_put_be16(resp, adv_data->config_methods); + + /* Service Name */ + wpabuf_put_u8(resp, (u8) len); + wpabuf_put_data(resp, adv_data->svc_name, len); + + /* Service State */ + wpabuf_put_u8(resp, adv_data->state); + + /* Service Information */ + wpabuf_put_le16(resp, (u16) svc_info_len); + wpabuf_put_data(resp, adv_data->svc_info, svc_info_len); + + /* Update length and count */ + (*count_pos)++; + WPA_PUT_LE16(len_pos, + (u8 *) wpabuf_put(resp, 0) - len_pos - 2); + } + } + + /* Return error if no matching svc found */ + if (count_pos == NULL) { + wpa_printf(MSG_DEBUG, "P2P: ASP service not found"); + wpas_sd_add_not_found(resp, P2P_SERV_P2PS, srv_trans_id); + } +} + + static void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token, u16 update_indic, const u8 *tlvs, size_t tlvs_len) { @@ -2610,6 +3129,10 @@ static void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token, pos, tlv_end - pos); break; #endif /* CONFIG_WIFI_DISPLAY */ + case P2P_SERV_P2PS: + wpas_sd_req_asp(wpa_s, resp, srv_trans_id, + pos, tlv_end - pos); + break; default: wpa_printf(MSG_DEBUG, "P2P: Unavailable service " "protocol %u", srv_proto); @@ -2631,6 +3154,80 @@ done: } +static void wpas_sd_p2ps_serv_response(struct wpa_supplicant *wpa_s, + const u8 *sa, u8 srv_trans_id, + const u8 *pos, const u8 *tlv_end) +{ + u8 left = *pos++; + u32 adv_id; + u8 svc_status; + u16 config_methods; + char svc_str[256]; + + while (left-- && pos < tlv_end) { + char *buf = NULL; + size_t buf_len; + u8 svc_len; + + /* Sanity check fixed length+svc_str */ + if (pos + 6 >= tlv_end) + break; + svc_len = pos[6]; + if (pos + svc_len + 10 > tlv_end) + break; + + /* Advertisement ID */ + adv_id = WPA_GET_LE32(pos); + pos += sizeof(u32); + + /* Config Methods */ + config_methods = WPA_GET_BE16(pos); + pos += sizeof(u16); + + /* Service Name */ + pos++; /* svc_len */ + os_memcpy(svc_str, pos, svc_len); + svc_str[svc_len] = '\0'; + pos += svc_len; + + /* Service Status */ + svc_status = *pos++; + + /* Service Information Length */ + buf_len = WPA_GET_LE16(pos); + pos += sizeof(u16); + + /* Sanity check buffer length */ + if (buf_len > (unsigned int) (tlv_end - pos)) + break; + + if (buf_len) { + buf = os_zalloc(2 * buf_len + 1); + if (buf) { + utf8_escape((const char *) pos, buf_len, buf, + 2 * buf_len + 1); + } + } + + pos += buf_len; + + if (buf) { + wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_SERV_ASP_RESP + MACSTR " %x %x %x %x %s '%s'", + MAC2STR(sa), srv_trans_id, adv_id, + svc_status, config_methods, svc_str, + buf); + os_free(buf); + } else { + wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_SERV_ASP_RESP + MACSTR " %x %x %x %x %s", + MAC2STR(sa), srv_trans_id, adv_id, + svc_status, config_methods, svc_str); + } + } +} + + static void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic, const u8 *tlvs, size_t tlvs_len) { @@ -2689,6 +3286,11 @@ static void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic, wpa_hexdump(MSG_MSGDUMP, "P2P: Response Data", pos, tlv_end - pos); + if (srv_proto == P2P_SERV_P2PS && pos < tlv_end) { + wpas_sd_p2ps_serv_response(wpa_s, sa, srv_trans_id, + pos, tlv_end); + } + pos = tlv_end; } @@ -2725,6 +3327,39 @@ u64 wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst, } +u64 wpas_p2p_sd_request_asp(struct wpa_supplicant *wpa_s, const u8 *dst, u8 id, + const char *svc_str, const char *info_substr) +{ + struct wpabuf *tlvs; + size_t plen, svc_len, substr_len = 0; + u64 ret; + + svc_len = os_strlen(svc_str); + if (info_substr) + substr_len = os_strlen(info_substr); + + if (svc_len > 0xff || substr_len > 0xff) + return 0; + + plen = 1 + 1 + 1 + svc_len + 1 + substr_len; + tlvs = wpabuf_alloc(2 + plen); + if (tlvs == NULL) + return 0; + + wpabuf_put_le16(tlvs, plen); + wpabuf_put_u8(tlvs, P2P_SERV_P2PS); + wpabuf_put_u8(tlvs, id); /* Service Transaction ID */ + wpabuf_put_u8(tlvs, (u8) svc_len); /* Service String Length */ + wpabuf_put_data(tlvs, svc_str, svc_len); + wpabuf_put_u8(tlvs, (u8) substr_len); /* Info Substring Length */ + wpabuf_put_data(tlvs, info_substr, substr_len); + ret = wpas_p2p_sd_request(wpa_s, dst, tlvs); + wpabuf_free(tlvs); + + return ret; +} + + #ifdef CONFIG_WIFI_DISPLAY static u64 wpas_p2p_sd_request_wfd(struct wpa_supplicant *wpa_s, const u8 *dst, @@ -2866,6 +3501,35 @@ void wpas_p2p_service_flush(struct wpa_supplicant *wpa_s) } +int wpas_p2p_service_p2ps_id_exists(struct wpa_supplicant *wpa_s, u32 adv_id) +{ + if (adv_id == 0) + return 1; + + if (p2p_service_p2ps_id(wpa_s->global->p2p, adv_id)) + return 1; + + return 0; +} + + +int wpas_p2p_service_del_asp(struct wpa_supplicant *wpa_s, u32 adv_id) +{ + return p2p_service_del_asp(wpa_s->global->p2p, adv_id); +} + + +int wpas_p2p_service_add_asp(struct wpa_supplicant *wpa_s, + int auto_accept, u32 adv_id, + const char *adv_str, u8 svc_state, + u16 config_methods, const char *svc_info) +{ + return p2p_service_add_asp(wpa_s->global->p2p, auto_accept, adv_id, + adv_str, svc_state, config_methods, + svc_info); +} + + int wpas_p2p_service_add_bonjour(struct wpa_supplicant *wpa_s, struct wpabuf *query, struct wpabuf *resp) { @@ -3055,7 +3719,9 @@ static void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods) static void wpas_prov_disc_fail(void *ctx, const u8 *peer, - enum p2p_prov_disc_status status) + enum p2p_prov_disc_status status, + u32 adv_id, const u8 *adv_mac, + const char *deferred_session_resp) { struct wpa_supplicant *wpa_s = ctx; @@ -3075,9 +3741,21 @@ static void wpas_prov_disc_fail(void *ctx, const u8 *peer, return; } - wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE - " p2p_dev_addr=" MACSTR " status=%d", - MAC2STR(peer), status); + if (adv_id && adv_mac && deferred_session_resp) { + wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE + " p2p_dev_addr=" MACSTR " status=%d adv_id=%x" + " deferred_session_resp='%s'", + MAC2STR(peer), status, adv_id, + deferred_session_resp); + } else if (adv_id && adv_mac) { + wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE + " p2p_dev_addr=" MACSTR " status=%d adv_id=%x", + MAC2STR(peer), status, adv_id); + } else { + wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE + " p2p_dev_addr=" MACSTR " status=%d", + MAC2STR(peer), status); + } wpas_notify_p2p_provision_discovery(wpa_s, peer, 0 /* response */, status, 0, 0); @@ -4012,6 +4690,337 @@ static void wpas_presence_resp(void *ctx, const u8 *src, u8 status, } +static int wpas_get_persistent_group(void *ctx, const u8 *addr, const u8 *ssid, + size_t ssid_len, u8 *go_dev_addr, + u8 *ret_ssid, size_t *ret_ssid_len) +{ + struct wpa_supplicant *wpa_s = ctx; + struct wpa_ssid *s; + + s = wpas_p2p_get_persistent(wpa_s, addr, ssid, ssid_len); + if (s) { + os_memcpy(ret_ssid, s->ssid, s->ssid_len); + *ret_ssid_len = s->ssid_len; + os_memcpy(go_dev_addr, s->bssid, ETH_ALEN); + return 1; + } + + return 0; +} + + +static int wpas_get_go_info(void *ctx, u8 *intended_addr, + u8 *ssid, size_t *ssid_len, int *group_iface) +{ + struct wpa_supplicant *wpa_s = ctx; + struct wpa_ssid *s; + u8 bssid[ETH_ALEN]; + + s = wpas_p2p_group_go_ssid(wpa_s, bssid); + if (!s) { + s = wpas_p2p_get_persistent_go(wpa_s); + if (s) + os_memcpy(bssid, s->bssid, ETH_ALEN); + } + + *group_iface = wpas_p2p_create_iface(wpa_s); + if (!s) + return 0; + + os_memcpy(intended_addr, bssid, ETH_ALEN); + os_memcpy(ssid, s->ssid, s->ssid_len); + *ssid_len = s->ssid_len; + + return 1; +} + + +static int wpas_remove_stale_groups(void *ctx, const u8 *peer, const u8 *go, + const u8 *ssid, size_t ssid_len) +{ + struct wpa_supplicant *wpa_s = ctx; + struct wpa_ssid *s; + int save_config = 0; + size_t i; + + /* Start with our first choice of Persistent Groups */ + while ((s = wpas_p2p_get_persistent(wpa_s, peer, NULL, 0))) { + if (go && ssid && ssid_len && + s->ssid_len == ssid_len && + os_memcmp(go, s->bssid, ETH_ALEN) == 0 && + os_memcmp(ssid, s->ssid, ssid_len) == 0) + break; + + /* Remove stale persistent group */ + if (s->mode != WPAS_MODE_P2P_GO || s->num_p2p_clients <= 1) { + wpa_config_remove_network(wpa_s->conf, s->id); + save_config = 1; + continue; + } + + for (i = 0; i < s->num_p2p_clients; i++) { + if (os_memcmp(s->p2p_client_list + i * 2 * ETH_ALEN, + peer, ETH_ALEN) != 0) + continue; + + os_memmove(s->p2p_client_list + i * 2 * ETH_ALEN, + s->p2p_client_list + (i + 1) * 2 * ETH_ALEN, + (s->num_p2p_clients - i - 1) * 2 * ETH_ALEN); + break; + } + s->num_p2p_clients--; + save_config = 1; + } + + if (save_config) + p2p_config_write(wpa_s); + + /* Return TRUE if valid SSID remains */ + return s != NULL; +} + + +static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, + const u8 *adv_mac, const u8 *ses_mac, + const u8 *grp_mac, u32 adv_id, u32 ses_id, + u8 conncap, int passwd_id, + const u8 *persist_ssid, + size_t persist_ssid_size, int response_done, + int prov_start, const char *session_info) +{ + struct wpa_supplicant *wpa_s = ctx; + u8 mac[ETH_ALEN]; + struct wpa_ssid *persistent_go, *stale, *s; + int save_config = 0; + struct wpa_supplicant *go_wpa_s; + + if (!dev) + return; + + os_memset(mac, 0, ETH_ALEN); + if (!adv_mac) + adv_mac = mac; + if (!ses_mac) + ses_mac = mac; + if (!grp_mac) + grp_mac = mac; + + if (prov_start) { + if (session_info == NULL) { + wpa_msg_global(wpa_s, MSG_INFO, + P2P_EVENT_P2PS_PROVISION_START MACSTR + " adv_id=%x conncap=%x" + " adv_mac=" MACSTR + " session=%x mac=" MACSTR + " dev_passwd_id=%d", + MAC2STR(dev), adv_id, conncap, + MAC2STR(adv_mac), + ses_id, MAC2STR(ses_mac), + passwd_id); + } else { + wpa_msg_global(wpa_s, MSG_INFO, + P2P_EVENT_P2PS_PROVISION_START MACSTR + " adv_id=%x conncap=%x" + " adv_mac=" MACSTR + " session=%x mac=" MACSTR + " dev_passwd_id=%d info='%s'", + MAC2STR(dev), adv_id, conncap, + MAC2STR(adv_mac), + ses_id, MAC2STR(ses_mac), + passwd_id, session_info); + } + return; + } + + go_wpa_s = wpas_p2p_get_go_group(wpa_s); + persistent_go = wpas_p2p_get_persistent_go(wpa_s); + + if (status && status != P2P_SC_SUCCESS_DEFERRED) { + if (go_wpa_s && !p2p_group_go_member_count(wpa_s)) + wpas_p2p_group_remove(wpa_s, go_wpa_s->ifname); + + if (persistent_go && !persistent_go->num_p2p_clients) { + /* remove empty persistent GO */ + wpa_config_remove_network(wpa_s->conf, + persistent_go->id); + } + + wpa_msg_global(wpa_s, MSG_INFO, + P2P_EVENT_P2PS_PROVISION_DONE MACSTR + " status=%d" + " adv_id=%x adv_mac=" MACSTR + " session=%x mac=" MACSTR, + MAC2STR(dev), status, + adv_id, MAC2STR(adv_mac), + ses_id, MAC2STR(ses_mac)); + return; + } + + /* Clean up stale persistent groups with this device */ + s = wpas_p2p_get_persistent(wpa_s, dev, persist_ssid, + persist_ssid_size); + for (;;) { + stale = wpas_p2p_get_persistent(wpa_s, dev, NULL, 0); + if (!stale) + break; + + if (s && s->ssid_len == stale->ssid_len && + os_memcmp(stale->bssid, s->bssid, ETH_ALEN) == 0 && + os_memcmp(stale->ssid, s->ssid, s->ssid_len) == 0) + break; + + /* Remove stale persistent group */ + if (stale->mode != WPAS_MODE_P2P_GO || + stale->num_p2p_clients <= 1) { + wpa_config_remove_network(wpa_s->conf, stale->id); + } else { + size_t i; + + for (i = 0; i < stale->num_p2p_clients; i++) { + if (os_memcmp(stale->p2p_client_list + + i * ETH_ALEN, + dev, ETH_ALEN) == 0) { + os_memmove(stale->p2p_client_list + + i * ETH_ALEN, + stale->p2p_client_list + + (i + 1) * ETH_ALEN, + (stale->num_p2p_clients - + i - 1) * ETH_ALEN); + break; + } + } + stale->num_p2p_clients--; + } + save_config = 1; + } + + if (save_config) + p2p_config_write(wpa_s); + + if (s) { + if (go_wpa_s && !p2p_group_go_member_count(wpa_s)) + wpas_p2p_group_remove(wpa_s, go_wpa_s->ifname); + + if (persistent_go && s != persistent_go && + !persistent_go->num_p2p_clients) { + /* remove empty persistent GO */ + wpa_config_remove_network(wpa_s->conf, + persistent_go->id); + /* Save config */ + } + + wpa_msg_global(wpa_s, MSG_INFO, + P2P_EVENT_P2PS_PROVISION_DONE MACSTR + " status=%d" + " adv_id=%x adv_mac=" MACSTR + " session=%x mac=" MACSTR + " persist=%d", + MAC2STR(dev), status, + adv_id, MAC2STR(adv_mac), + ses_id, MAC2STR(ses_mac), s->id); + return; + } + + if (conncap == P2PS_SETUP_GROUP_OWNER) { + const char *go_ifname = NULL; + if (!go_wpa_s) { + wpa_s->global->pending_p2ps_group = 1; + + if (wpa_s->conf->p2p_no_group_iface) + go_ifname = wpa_s->ifname; + else if (wpa_s->pending_interface_name[0]) + go_ifname = wpa_s->pending_interface_name; + + if (!go_ifname) { + wpas_p2ps_prov_complete( + wpa_s, P2P_SC_FAIL_UNKNOWN_GROUP, + dev, adv_mac, ses_mac, + NULL, adv_id, ses_id, 0, 0, + NULL, 0, 0, 0, NULL); + return; + } + + /* If PD Resp complete, start up the GO */ + if (response_done && persistent_go) { + wpas_p2p_group_add_persistent( + wpa_s, persistent_go, + 0, 0, 0, 0, 0, NULL, + persistent_go->mode == + WPAS_MODE_P2P_GO ? + P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : + 0); + } else if (response_done) { + wpas_p2p_group_add(wpa_s, 1, 0, 0, 0); + } + + if (passwd_id == DEV_PW_P2PS_DEFAULT) { + os_memcpy(wpa_s->p2ps_join_addr, dev, ETH_ALEN); + wpa_s->p2ps_join_addr_valid = 1; + wpa_dbg(wpa_s, MSG_DEBUG, + "P2PS: Saving PIN for " MACSTR, + MAC2STR(dev)); + } + } else if (passwd_id == DEV_PW_P2PS_DEFAULT) { + go_ifname = go_wpa_s->ifname; + + wpa_dbg(go_wpa_s, MSG_DEBUG, + "P2P: Setting PIN-1 For " MACSTR, MAC2STR(dev)); + wpa_supplicant_ap_wps_pin(go_wpa_s, dev, "12345670", + NULL, 0, 0); + + os_memcpy(wpa_s->p2ps_join_addr, dev, ETH_ALEN); + wpa_s->p2ps_join_addr_valid = 1; + wpa_dbg(wpa_s, MSG_DEBUG, + "P2PS: Saving PIN for " MACSTR, MAC2STR(dev)); + } + + wpa_msg_global(wpa_s, MSG_INFO, + P2P_EVENT_P2PS_PROVISION_DONE MACSTR + " status=%d conncap=%x" + " adv_id=%x adv_mac=" MACSTR + " session=%x mac=" MACSTR + " dev_passwd_id=%d go=%s", + MAC2STR(dev), status, conncap, + adv_id, MAC2STR(adv_mac), + ses_id, MAC2STR(ses_mac), + passwd_id, go_ifname); + return; + } + + if (go_wpa_s && !p2p_group_go_member_count(wpa_s)) + wpas_p2p_group_remove(wpa_s, go_wpa_s->ifname); + + if (persistent_go && !persistent_go->num_p2p_clients) { + /* remove empty persistent GO */ + wpa_config_remove_network(wpa_s->conf, persistent_go->id); + } + + if (conncap == P2PS_SETUP_CLIENT) { + wpa_msg_global(wpa_s, MSG_INFO, + P2P_EVENT_P2PS_PROVISION_DONE MACSTR + " status=%d conncap=%x" + " adv_id=%x adv_mac=" MACSTR + " session=%x mac=" MACSTR + " dev_passwd_id=%d join=" MACSTR, + MAC2STR(dev), status, conncap, + adv_id, MAC2STR(adv_mac), + ses_id, MAC2STR(ses_mac), + passwd_id, MAC2STR(grp_mac)); + } else { + wpa_msg_global(wpa_s, MSG_INFO, + P2P_EVENT_P2PS_PROVISION_DONE MACSTR + " status=%d conncap=%x" + " adv_id=%x adv_mac=" MACSTR + " session=%x mac=" MACSTR + " dev_passwd_id=%d", + MAC2STR(dev), status, conncap, + adv_id, MAC2STR(adv_mac), + ses_id, MAC2STR(ses_mac), + passwd_id); + } +} + + static int _wpas_p2p_in_progress(void *ctx) { struct wpa_supplicant *wpa_s = ctx; @@ -4019,6 +5028,33 @@ static int _wpas_p2p_in_progress(void *ctx) } +static int wpas_prov_disc_resp_cb(void *ctx) +{ + struct wpa_supplicant *wpa_s = ctx; + struct wpa_ssid *persistent_go; + + if (!wpa_s->global->pending_p2ps_group) + return 0; + + wpa_s->global->pending_p2ps_group = 0; + + if (wpas_p2p_get_go_group(wpa_s)) + return 0; + persistent_go = wpas_p2p_get_persistent_go(wpa_s); + + if (persistent_go) { + wpas_p2p_group_add_persistent( + wpa_s, persistent_go, 0, 0, 0, 0, 0, NULL, + persistent_go->mode == WPAS_MODE_P2P_GO ? + P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0); + } else { + wpas_p2p_group_add(wpa_s, 1, 0, 0, 0); + } + + return 1; +} + + /** * wpas_p2p_init - Initialize P2P module for %wpa_supplicant * @global: Pointer to global data from wpa_supplicant_init() @@ -4066,6 +5102,12 @@ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s) p2p.presence_resp = wpas_presence_resp; p2p.is_concurrent_session_active = wpas_is_concurrent_session_active; p2p.is_p2p_in_progress = _wpas_p2p_in_progress; + p2p.get_persistent_group = wpas_get_persistent_group; + p2p.get_go_info = wpas_get_go_info; + p2p.remove_stale_groups = wpas_remove_stale_groups; + p2p.p2ps_prov_complete = wpas_p2ps_prov_complete; + p2p.prov_disc_resp_cb = wpas_prov_disc_resp_cb; + p2p.p2ps_group_capability = p2ps_group_capability; os_memcpy(wpa_s->global->p2p_dev_addr, wpa_s->own_addr, ETH_ALEN); os_memcpy(p2p.dev_addr, wpa_s->global->p2p_dev_addr, ETH_ALEN); @@ -4485,7 +5527,7 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, wpa_printf(MSG_DEBUG, "P2P: Auto PD with " MACSTR " join=%d", MAC2STR(wpa_s->pending_join_dev_addr), join); if (p2p_prov_disc_req(wpa_s->global->p2p, - wpa_s->pending_join_dev_addr, + wpa_s->pending_join_dev_addr, NULL, wpa_s->pending_pd_config_methods, join, 0, wpa_s->user_initiated_pd) < 0) { wpa_s->p2p_auto_pd = 0; @@ -4614,7 +5656,8 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, } if (p2p_prov_disc_req(wpa_s->global->p2p, - wpa_s->pending_join_dev_addr, method, 1, + wpa_s->pending_join_dev_addr, + NULL, method, 1, freq, wpa_s->user_initiated_pd) < 0) { wpa_printf(MSG_DEBUG, "P2P: Failed to send Provision " "Discovery Request before joining an " @@ -4971,6 +6014,7 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, wpa_s->global->add_psk = NULL; wpa_s->global->p2p_fail_on_wps_complete = 0; + wpa_s->global->pending_p2ps_group = 0; if (go_intent < 0) go_intent = wpa_s->conf->p2p_go_intent; @@ -5089,7 +6133,11 @@ void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s, { if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return; - if (wpa_s->off_channel_freq == wpa_s->pending_listen_freq) { + wpa_printf(MSG_DEBUG, "P2P: remain-on-channel callback (off_channel_freq=%u pending_listen_freq=%d roc_waiting_drv_freq=%d freq=%u duration=%u)", + wpa_s->off_channel_freq, wpa_s->pending_listen_freq, + wpa_s->roc_waiting_drv_freq, freq, duration); + if (wpa_s->off_channel_freq && + wpa_s->off_channel_freq == wpa_s->pending_listen_freq) { p2p_listen_cb(wpa_s->global->p2p, wpa_s->pending_listen_freq, wpa_s->pending_listen_duration); wpa_s->pending_listen_freq = 0; @@ -5841,13 +6889,25 @@ int wpas_p2p_wps_eapol_cb(struct wpa_supplicant *wpa_s) int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr, const char *config_method, - enum wpas_p2p_prov_disc_use use) + enum wpas_p2p_prov_disc_use use, + struct p2ps_provision *p2ps_prov) { u16 config_methods; + wpa_s->global->pending_p2ps_group = 0; wpa_s->p2p_fallback_to_go_neg = 0; wpa_s->pending_pd_use = NORMAL_PD; - if (os_strncmp(config_method, "display", 7) == 0) + if (p2ps_prov && use == WPAS_P2P_PD_FOR_ASP) { + p2ps_prov->conncap = p2ps_group_capability( + wpa_s, P2PS_SETUP_NONE, p2ps_prov->role); + wpa_printf(MSG_DEBUG, + "P2P: %s conncap: %d - ASP parsed: %x %x %d %s", + __func__, p2ps_prov->conncap, + p2ps_prov->adv_id, p2ps_prov->conncap, + p2ps_prov->status, p2ps_prov->info); + + config_methods = 0; + } else if (os_strncmp(config_method, "display", 7) == 0) config_methods = WPS_CONFIG_DISPLAY; else if (os_strncmp(config_method, "keypad", 6) == 0) config_methods = WPS_CONFIG_KEYPAD; @@ -5856,6 +6916,7 @@ int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr, config_methods = WPS_CONFIG_PUSHBUTTON; else { wpa_printf(MSG_DEBUG, "P2P: Unknown config method"); + os_free(p2ps_prov); return -1; } @@ -5876,10 +6937,12 @@ int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr, return 0; } - if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled) + if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled) { + os_free(p2ps_prov); return -1; + } - return p2p_prov_disc_req(wpa_s->global->p2p, peer_addr, + return p2p_prov_disc_req(wpa_s->global->p2p, peer_addr, p2ps_prov, config_methods, use == WPAS_P2P_PD_FOR_JOIN, 0, 1); } @@ -5908,7 +6971,8 @@ 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, unsigned int search_delay) + const u8 *dev_id, unsigned int search_delay, + u8 seek_cnt, const char **seek_string) { wpas_p2p_clear_pending_action_tx(wpa_s); wpa_s->p2p_long_listen = 0; @@ -5921,7 +6985,7 @@ int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout, return p2p_find(wpa_s->global->p2p, timeout, type, num_req_dev_types, req_dev_types, dev_id, - search_delay); + search_delay, seek_cnt, seek_string); } @@ -5968,7 +7032,8 @@ static void wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s) void wpas_p2p_stop_find(struct wpa_supplicant *wpa_s) { wpas_p2p_stop_find_oper(wpa_s); - wpas_p2p_remove_pending_group_interface(wpa_s); + if (!wpa_s->global->pending_group_iface_for_p2ps) + wpas_p2p_remove_pending_group_interface(wpa_s); } @@ -8102,7 +9167,7 @@ static void wpas_p2p_optimize_listen_channel(struct wpa_supplicant *wpa_s, if (cand) { wpa_dbg(wpa_s, MSG_DEBUG, - "P2P: Update Listen channel to %u baased on operating channel", + "P2P: Update Listen channel to %u based on operating channel", cand); p2p_set_listen_channel(wpa_s->global->p2p, 81, cand, 0); } diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h index 9f5a83bd..1cf15736 100644 --- a/wpa_supplicant/p2p_supplicant.h +++ b/wpa_supplicant/p2p_supplicant.h @@ -15,6 +15,7 @@ enum p2p_send_action_result; struct p2p_peer_info; struct p2p_channels; struct wps_event_fail; +struct p2ps_provision; int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s, const char *conf_p2p_dev); @@ -41,11 +42,13 @@ struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s, enum wpas_p2p_prov_disc_use { WPAS_P2P_PD_FOR_GO_NEG, WPAS_P2P_PD_FOR_JOIN, - WPAS_P2P_PD_AUTO + WPAS_P2P_PD_AUTO, + WPAS_P2P_PD_FOR_ASP }; int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr, const char *config_method, - enum wpas_p2p_prov_disc_use use); + enum wpas_p2p_prov_disc_use use, + struct p2ps_provision *p2ps_prov); void wpas_send_action_tx_status(struct wpa_supplicant *wpa_s, const u8 *dst, const u8 *data, size_t data_len, enum p2p_send_action_result result); @@ -55,7 +58,8 @@ 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, unsigned int search_delay); + const u8 *dev_id, unsigned int search_delay, + u8 seek_cnt, const char **seek_string); 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_listen_start(struct wpa_supplicant *wpa_s, unsigned int timeout); @@ -65,6 +69,8 @@ void wpas_p2p_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ies); void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s); u64 wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst, const struct wpabuf *tlvs); +u64 wpas_p2p_sd_request_asp(struct wpa_supplicant *wpa_s, const u8 *dst, u8 id, + const char *svc_str, const char *info_substr); 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, @@ -83,6 +89,11 @@ int wpas_p2p_service_add_upnp(struct wpa_supplicant *wpa_s, u8 version, const char *service); int wpas_p2p_service_del_upnp(struct wpa_supplicant *wpa_s, u8 version, const char *service); +int wpas_p2p_service_add_asp(struct wpa_supplicant *wpa_s, int auto_accept, + u32 adv_id, const char *adv_str, u8 svc_state, + u16 config_methods, const char *svc_info); +int wpas_p2p_service_del_asp(struct wpa_supplicant *wpa_s, u32 adv_id); +int wpas_p2p_service_p2ps_id_exists(struct wpa_supplicant *wpa_s, u32 adv_id); int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr); int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr, struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq, diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c index 0653cc28..c1f3efc8 100644 --- a/wpa_supplicant/scan.c +++ b/wpa_supplicant/scan.c @@ -1594,11 +1594,12 @@ struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res, */ #define GREAT_SNR 30 +#define IS_5GHZ(n) (n > 4000) + /* Compare function for sorting scan results. Return >0 if @b is considered * better. */ static int wpa_scan_result_compar(const void *a, const void *b) { -#define IS_5GHZ(n) (n > 4000) #define MIN(a,b) a < b ? a : b struct wpa_scan_res **_wa = (void *) a; struct wpa_scan_res **_wb = (void *) b; @@ -1626,18 +1627,18 @@ static int wpa_scan_result_compar(const void *a, const void *b) (wb->caps & IEEE80211_CAP_PRIVACY) == 0) return -1; - if ((wa->flags & wb->flags & WPA_SCAN_LEVEL_DBM) && - !((wa->flags | wb->flags) & WPA_SCAN_NOISE_INVALID)) { + if (wa->flags & wb->flags & WPA_SCAN_LEVEL_DBM) { snr_a = MIN(wa->level - wa->noise, GREAT_SNR); snr_b = MIN(wb->level - wb->noise, GREAT_SNR); } else { - /* Not suitable information to calculate SNR, so use level */ + /* Level is not in dBm, so we can't calculate + * SNR. Just use raw level (units unknown). */ snr_a = wa->level; snr_b = wb->level; } - /* best/max rate preferred if SNR close enough */ - if ((snr_a && snr_b && abs(snr_b - snr_a) < 5) || + /* if SNR is close, decide by max rate or frequency band */ + if ((snr_a && snr_b && abs(snr_b - snr_a) < 5) || (wa->qual && wb->qual && abs(wb->qual - wa->qual) < 10)) { maxrate_a = wpa_scan_get_max_rate(wa); maxrate_b = wpa_scan_get_max_rate(wb); @@ -1647,8 +1648,6 @@ static int wpa_scan_result_compar(const void *a, const void *b) return IS_5GHZ(wa->freq) ? -1 : 1; } - /* use freq for channel preference */ - /* all things being equal, use SNR; if SNRs are * identical, use quality values since some drivers may only report * that value and leave the signal level zero */ @@ -1656,7 +1655,6 @@ static int wpa_scan_result_compar(const void *a, const void *b) return wb->qual - wa->qual; return snr_b - snr_a; #undef MIN -#undef IS_5GHZ } @@ -1721,15 +1719,15 @@ static void dump_scan_res(struct wpa_scan_results *scan_res) for (i = 0; i < scan_res->num; i++) { struct wpa_scan_res *r = scan_res->res[i]; u8 *pos; - if ((r->flags & (WPA_SCAN_LEVEL_DBM | WPA_SCAN_NOISE_INVALID)) - == WPA_SCAN_LEVEL_DBM) { + if (r->flags & WPA_SCAN_LEVEL_DBM) { int snr = r->level - r->noise; + int noise_valid = !(r->flags & WPA_SCAN_NOISE_INVALID); + wpa_printf(MSG_EXCESSIVE, MACSTR " freq=%d qual=%d " - "noise=%d level=%d snr=%d%s flags=0x%x " - "age=%u", + "noise=%d%s level=%d snr=%d%s flags=0x%x age=%u", MAC2STR(r->bssid), r->freq, r->qual, - r->noise, r->level, snr, - snr >= GREAT_SNR ? "*" : "", r->flags, + r->noise, noise_valid ? "" : "~", r->level, + snr, snr >= GREAT_SNR ? "*" : "", r->flags, r->age); } else { wpa_printf(MSG_EXCESSIVE, MACSTR " freq=%d qual=%d " @@ -1802,6 +1800,14 @@ static void filter_scan_res(struct wpa_supplicant *wpa_s, } +/* + * Noise floor values to use when we have signal strength + * measurements, but no noise floor measurments. These values were + * measured in an office environment with many APs. + */ +#define DEFAULT_NOISE_FLOOR_2GHZ (-89) +#define DEFAULT_NOISE_FLOOR_5GHZ (-92) + /** * wpa_supplicant_get_scan_results - Get scan results * @wpa_s: Pointer to wpa_supplicant data @@ -1835,6 +1841,17 @@ wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s, } filter_scan_res(wpa_s, scan_res); + for (i = 0; i < scan_res->num; i++) { + struct wpa_scan_res *scan_res_item = scan_res->res[i]; + + if (scan_res_item->flags & WPA_SCAN_NOISE_INVALID) { + scan_res_item->noise = + IS_5GHZ(scan_res_item->freq) ? + DEFAULT_NOISE_FLOOR_5GHZ : + DEFAULT_NOISE_FLOOR_2GHZ; + } + } + #ifdef CONFIG_WPS if (wpas_wps_searching(wpa_s)) { wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Order scan results with WPS " diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index c2b0990a..6c057072 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -297,7 +297,7 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, wpa_s->current_ssid, try_opportunistic) == 0) - eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 1); + eapol_sm_notify_pmkid_attempt(wpa_s->eapol); wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie); if (wpa_supplicant_set_suites(wpa_s, bss, ssid, wpa_s->sme.assoc_req_ie, diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c index 911effe4..2f06c35a 100644 --- a/wpa_supplicant/wpa_cli.c +++ b/wpa_supplicant/wpa_cli.c @@ -1825,6 +1825,20 @@ static int wpa_cli_cmd_p2p_stop_find(struct wpa_ctrl *ctrl, int argc, } +static int wpa_cli_cmd_p2p_asp_provision(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "P2P_ASP_PROVISION", 3, argc, argv); +} + + +static int wpa_cli_cmd_p2p_asp_provision_resp(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "P2P_ASP_PROVISION_RESP", 2, argc, argv); +} + + static int wpa_cli_cmd_p2p_connect(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -1909,11 +1923,9 @@ static int wpa_cli_cmd_p2p_serv_disc_req(struct wpa_ctrl *ctrl, int argc, { char cmd[4096]; - if (argc != 2 && argc != 4) { + if (argc < 2) { printf("Invalid P2P_SERV_DISC_REQ command: needs two " - "arguments (address and TLVs) or four arguments " - "(address, \"upnp\", version, search target " - "(SSDP ST:)\n"); + "or more arguments (address and TLVs)\n"); return -1; } @@ -1975,27 +1987,25 @@ static int wpa_cli_cmd_p2p_service_flush(struct wpa_ctrl *ctrl, int argc, static int wpa_cli_cmd_p2p_service_add(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[4096]; - int res; + if (argc < 3) { + printf("Invalid P2P_SERVICE_ADD command: needs 3-6 arguments\n"); + return -1; + } - if (argc != 3 && argc != 4) { - printf("Invalid P2P_SERVICE_ADD command: needs three or four " + return wpa_cli_cmd(ctrl, "P2P_SERVICE_ADD", 3, argc, argv); +} + + +static int wpa_cli_cmd_p2p_service_rep(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + if (argc < 5 || argc > 6) { + printf("Invalid P2P_SERVICE_REP command: needs 5-6 " "arguments\n"); return -1; } - if (argc == 4) - res = os_snprintf(cmd, sizeof(cmd), - "P2P_SERVICE_ADD %s %s %s %s", - argv[0], argv[1], argv[2], argv[3]); - else - res = os_snprintf(cmd, sizeof(cmd), - "P2P_SERVICE_ADD %s %s %s", - argv[0], argv[1], argv[2]); - if (os_snprintf_error(sizeof(cmd), res)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "P2P_SERVICE_REP", 5, argc, argv); } @@ -2882,6 +2892,12 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { "[timeout] [type=*] = find P2P Devices for up-to timeout seconds" }, { "p2p_stop_find", wpa_cli_cmd_p2p_stop_find, NULL, cli_cmd_flag_none, "= stop P2P Devices search" }, + { "p2p_asp_provision", wpa_cli_cmd_p2p_asp_provision, NULL, + cli_cmd_flag_none, + "<addr> adv_id=<adv_id> conncap=<conncap> [info=<infodata>] = provision with a P2P ASP Device" }, + { "p2p_asp_provision_resp", wpa_cli_cmd_p2p_asp_provision_resp, NULL, + cli_cmd_flag_none, + "<addr> adv_id=<adv_id> [role<conncap>] [info=<infodata>] = provision with a P2P ASP Device" }, { "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" }, @@ -2918,8 +2934,12 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { "= remove all stored service entries" }, { "p2p_service_add", wpa_cli_cmd_p2p_service_add, NULL, cli_cmd_flag_none, - "<bonjour|upnp> <query|version> <response|service> = add a local " + "<bonjour|upnp|asp> <query|version> <response|service> = add a local " "service" }, + { "p2p_service_rep", wpa_cli_cmd_p2p_service_rep, NULL, + cli_cmd_flag_none, + "asp <auto> <adv_id> <svc_state> <svc_string> [<svc_info>] = replace " + "local ASP service" }, { "p2p_service_del", wpa_cli_cmd_p2p_service_del, NULL, cli_cmd_flag_none, "<bonjour|upnp> <query|version> [|service] = remove a local " diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index 434847d5..6ad09a87 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -1906,7 +1906,7 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) (ssid->proto & WPA_PROTO_RSN); if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, ssid, try_opportunistic) == 0) - eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 1); + eapol_sm_notify_pmkid_attempt(wpa_s->eapol); wpa_ie_len = sizeof(wpa_ie); if (wpa_supplicant_set_suites(wpa_s, bss, ssid, wpa_ie, &wpa_ie_len)) { @@ -3086,11 +3086,9 @@ int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s) if (wpa_s->bridge_ifname[0]) { wpa_dbg(wpa_s, MSG_DEBUG, "Receiving packets from bridge " "interface '%s'", wpa_s->bridge_ifname); - wpa_s->l2_br = l2_packet_init(wpa_s->bridge_ifname, - wpa_s->own_addr, - ETH_P_EAPOL, - wpa_supplicant_rx_eapol_bridge, - wpa_s, 1); + wpa_s->l2_br = l2_packet_init_bridge( + wpa_s->bridge_ifname, wpa_s->ifname, wpa_s->own_addr, + ETH_P_EAPOL, wpa_supplicant_rx_eapol_bridge, wpa_s, 1); if (wpa_s->l2_br == NULL) { wpa_msg(wpa_s, MSG_ERROR, "Failed to open l2_packet " "connection for the bridge interface '%s'", diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf index 23c22998..8964b3f7 100644 --- a/wpa_supplicant/wpa_supplicant.conf +++ b/wpa_supplicant/wpa_supplicant.conf @@ -940,9 +940,20 @@ fast_reauth=1 # * 2 = require cryptobinding # EAP-WSC (WPS) uses following options: pin=<Device Password> or # pbc=1. +# +# For wired IEEE 802.1X authentication, "allow_canned_success=1" can be +# used to configure a mode that allows EAP-Success (and EAP-Failure) +# without going through authentication step. Some switches use such +# sequence when forcing the port to be authorized/unauthorized or as a +# fallback option if the authentication server is unreachable. By default, +# wpa_supplicant discards such frames to protect against potential attacks +# by rogue devices, but this option can be used to disable that protection +# for cases where the server/authenticator does not need to be +# authenticated. # 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) +# "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS). "mschapv2_retry=0" can be +# used to disable MSCHAPv2 password retry in authentication failure cases. # # 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 diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 7d22000a..c80a6209 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -275,6 +275,8 @@ struct wpa_global { unsigned int p2p_per_sta_psk:1; unsigned int p2p_fail_on_wps_complete:1; unsigned int p2p_24ghz_social_channels:1; + unsigned int pending_p2ps_group:1; + unsigned int pending_group_iface_for_p2ps:1; #ifdef CONFIG_WIFI_DISPLAY int wifi_display; @@ -771,7 +773,7 @@ struct wpa_supplicant { int force_long_sd; u16 pending_pd_config_methods; enum { - NORMAL_PD, AUTO_PD_GO_NEG, AUTO_PD_JOIN + NORMAL_PD, AUTO_PD_GO_NEG, AUTO_PD_JOIN, AUTO_PD_ASP } pending_pd_use; /* @@ -810,6 +812,7 @@ struct wpa_supplicant { unsigned int p2p_nfc_tag_enabled:1; unsigned int p2p_peer_oob_pk_hash_known:1; unsigned int p2p_disable_ip_addr_req:1; + unsigned int p2ps_join_addr_valid:1; int p2p_persistent_go_freq; int p2p_persistent_id; int p2p_go_intent; @@ -829,6 +832,7 @@ struct wpa_supplicant { /* group common frequencies */ int *p2p_group_common_freqs; unsigned int p2p_group_common_freqs_num; + u8 p2ps_join_addr[ETH_ALEN]; #endif /* CONFIG_P2P */ struct wpa_ssid *bgscan_ssid; |
