diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h index 3e1cbbf35..b1c89d71b 100644 --- a/src/p2p/p2p.h +++ b/src/p2p/p2p.h @@ -348,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 { diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c index 5c40c1c61..328b1e029 100644 --- a/src/p2p/p2p_pd.c +++ b/src/p2p/p2p_pd.c @@ -793,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); @@ -842,23 +879,108 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, if (p2p->cfg->prov_disc_fail) p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa, P2P_PROV_DISC_REJECTED, - 0, NULL, NULL); + 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 */