void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len) { struct p2p_message msg; struct p2p_device *dev; u16 report_config_methods = 0, req_config_methods; u8 status = P2P_SC_SUCCESS; u32 adv_id = 0; u8 conncap = P2PS_SETUP_NEW; u8 adv_mac[ETH_ALEN]; const u8 *group_mac; int passwd_id = DEV_PW_DEFAULT; int p2ps_seeker; if (p2p_parse(data, len, &msg)) return; if (p2p->p2ps_prov && p2p_validate_p2ps_pd_resp(p2p, &msg)) { p2p_parse_free(&msg); return; } /* Parse the P2PS members present */ if (msg.status) status = *msg.status; group_mac = msg.intended_addr; 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); dev = p2p_get_device(p2p, sa); if (dev == NULL || !dev->req_config_methods) { p2p_dbg(p2p, "Ignore Provision Discovery Response from " MACSTR " with no pending request", MAC2STR(sa)); p2p_parse_free(&msg); return; } if (dev->dialog_token != msg.dialog_token) { p2p_dbg(p2p, "Ignore Provision Discovery Response with unexpected Dialog Token %u (expected %u)", msg.dialog_token, dev->dialog_token); p2p_parse_free(&msg); return; } if (p2p->pending_action_state == P2P_PENDING_PD) { os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN); p2p->pending_action_state = P2P_NO_PENDING_ACTION; } p2ps_seeker = p2p->p2ps_prov && p2p->p2ps_prov->pd_seeker; /* * Use a local copy of the requested config methods since * p2p_reset_pending_pd() can clear this in the peer entry. */ req_config_methods = dev->req_config_methods; /* * If the response is from the peer to whom a user initiated request * was sent earlier, we reset that state info here. */ if (p2p->user_initiated_pd && os_memcmp(p2p->pending_pd_devaddr, sa, ETH_ALEN) == 0) p2p_reset_pending_pd(p2p); if (msg.wps_config_methods != req_config_methods) { p2p_dbg(p2p, "Peer rejected our Provision Discovery Request (received config_methods 0x%x expected 0x%x", 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, adv_id, adv_mac, NULL); p2p_parse_free(&msg); p2ps_prov_free(p2p); goto out; } report_config_methods = req_config_methods; dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY | 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 ((status == P2P_SC_SUCCESS || status == P2P_SC_SUCCESS_DEFERRED) && p2p->p2ps_prov) { dev->oper_freq = 0; /* * Save the reported channel list and operating frequency. * Note that the specification mandates that the responder * should include in the channel list only channels reported by * the initiator, so this is only a sanity check, and if this * fails the flow would continue, although it would probably * fail. Same is true for the operating channel. */ if (msg.channel_list && msg.channel_list_len && p2p_peer_channels_check(p2p, &p2p->channels, dev, msg.channel_list, msg.channel_list_len) < 0) p2p_dbg(p2p, "P2PS PD Response - no common channels"); if (msg.operating_channel) { if (p2p_channels_includes(&p2p->channels, msg.operating_channel[3], msg.operating_channel[4]) && p2p_channels_includes(&dev->channels, msg.operating_channel[3], msg.operating_channel[4])) { dev->oper_freq = p2p_channel_to_freq( msg.operating_channel[3], msg.operating_channel[4]); } else { p2p_dbg(p2p, "P2PS PD Response - invalid operating channel"); } } if (p2p->cfg->p2ps_prov_complete) { int freq = 0; if (conncap == P2PS_SETUP_GROUP_OWNER) { u8 tmp; /* * Re-select the operating channel as it is * possible that original channel is no longer * valid. This should not really fail. */ if (p2p_go_select_channel(p2p, dev, &tmp) < 0) p2p_dbg(p2p, "P2PS PD channel selection failed"); freq = p2p_channel_to_freq(p2p->op_reg_class, p2p->op_channel); if (freq < 0) freq = 0; } 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, msg.feature_cap, msg.feature_cap_len, freq); } p2ps_prov_free(p2p); } else if (status != P2P_SC_SUCCESS && status != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE && status != P2P_SC_SUCCESS_DEFERRED && p2p->p2ps_prov) { if (p2p->cfg->p2ps_prov_complete) p2p->cfg->p2ps_prov_complete( p2p->cfg->cb_ctx, status, sa, adv_mac, p2p->p2ps_prov->session_mac, group_mac, adv_id, p2p->p2ps_prov->session_id, 0, 0, NULL, 0, 1, 0, NULL, NULL, 0, 0); p2ps_prov_free(p2p); } 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); p2ps_prov_free(p2p); 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 (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, adv_id, adv_mac, NULL); p2p_parse_free(&msg); p2ps_prov_free(p2p); goto out; } /* Store the provisioning info */ dev->wps_prov_info = msg.wps_config_methods; if (msg.intended_addr) os_memcpy(dev->interface_addr, msg.intended_addr, ETH_ALEN); p2p_parse_free(&msg); out: dev->req_config_methods = 0; p2p->cfg->send_action_done(p2p->cfg->cb_ctx); if (dev->flags & P2P_DEV_PD_BEFORE_GO_NEG) { p2p_dbg(p2p, "Start GO Neg after the PD-before-GO-Neg workaround with " MACSTR, MAC2STR(dev->info.p2p_device_addr)); dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG; p2p_connect_send(p2p, dev); return; } /* * prov_disc_resp callback is used to generate P2P-PROV-DISC-ENTER-PIN, * P2P-PROV-DISC-SHOW-PIN, and P2P-PROV-DISC-PBC-REQ events. * Call it only for a legacy P2P PD or for P2PS PD scenarios where * show/enter PIN events are needed. * * The callback is called in the following cases: * 1. Legacy P2P PD response with a status SUCCESS * 2. P2PS, advertiser method: DISPLAY, autoaccept: true, * response status: SUCCESS, local method KEYPAD * 3. P2PS, advertiser method: KEYPAD,Seeker side, * response status: INFO_CURRENTLY_UNAVAILABLE, * local method: DISPLAY */ if (p2p->cfg->prov_disc_resp && ((status == P2P_SC_SUCCESS && !adv_id) || (p2ps_seeker && status == P2P_SC_SUCCESS && passwd_id == DEV_PW_REGISTRAR_SPECIFIED) || (p2ps_seeker && status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE && passwd_id == DEV_PW_USER_SPECIFIED))) p2p->cfg->prov_disc_resp(p2p->cfg->cb_ctx, sa, report_config_methods); if (p2p->state == P2P_PD_DURING_FIND) { p2p_clear_timeout(p2p); p2p_continue_find(p2p); } }
void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len) { 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; if (p2p->p2ps_prov && p2p_validate_p2ps_pd_resp(p2p, &msg)) { p2p_parse_free(&msg); return; } /* Parse the P2PS members present */ if (msg.status) status = *msg.status; if (msg.intended_addr) os_memcpy(group_mac, msg.intended_addr, ETH_ALEN); else os_memset(group_mac, 0, ETH_ALEN); 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); dev = p2p_get_device(p2p, sa); if (dev == NULL || !dev->req_config_methods) { p2p_dbg(p2p, "Ignore Provision Discovery Response from " MACSTR " with no pending request", MAC2STR(sa)); p2p_parse_free(&msg); return; } if (dev->dialog_token != msg.dialog_token) { p2p_dbg(p2p, "Ignore Provision Discovery Response with unexpected Dialog Token %u (expected %u)", msg.dialog_token, dev->dialog_token); p2p_parse_free(&msg); return; } if (p2p->pending_action_state == P2P_PENDING_PD) { os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN); p2p->pending_action_state = P2P_NO_PENDING_ACTION; } /* * Use a local copy of the requested config methods since * p2p_reset_pending_pd() can clear this in the peer entry. */ req_config_methods = dev->req_config_methods; /* * If the response is from the peer to whom a user initiated request * was sent earlier, we reset that state info here. */ if (p2p->user_initiated_pd && os_memcmp(p2p->pending_pd_devaddr, sa, ETH_ALEN) == 0) p2p_reset_pending_pd(p2p); if (msg.wps_config_methods != req_config_methods) { p2p_dbg(p2p, "Peer rejected our Provision Discovery Request (received config_methods 0x%x expected 0x%x", 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, adv_id, adv_mac, NULL); p2p_parse_free(&msg); p2ps_prov_free(p2p); goto out; } report_config_methods = req_config_methods; dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY | 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) && (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); } p2ps_prov_free(p2p); } else if (status != P2P_SC_SUCCESS && status != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE && status != P2P_SC_SUCCESS_DEFERRED && p2p->p2ps_prov) { if (p2p->cfg->p2ps_prov_complete) p2p->cfg->p2ps_prov_complete( p2p->cfg->cb_ctx, status, sa, adv_mac, p2p->p2ps_prov->session_mac, group_mac, adv_id, p2p->p2ps_prov->session_id, 0, 0, NULL, 0, 1, 0, NULL); p2ps_prov_free(p2p); } 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); p2ps_prov_free(p2p); 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 (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, adv_id, adv_mac, NULL); p2p_parse_free(&msg); p2ps_prov_free(p2p); goto out; } /* Store the provisioning info */ dev->wps_prov_info = msg.wps_config_methods; if (msg.intended_addr) os_memcpy(dev->interface_addr, msg.intended_addr, ETH_ALEN); p2p_parse_free(&msg); success = 1; out: dev->req_config_methods = 0; p2p->cfg->send_action_done(p2p->cfg->cb_ctx); if (dev->flags & P2P_DEV_PD_BEFORE_GO_NEG) { p2p_dbg(p2p, "Start GO Neg after the PD-before-GO-Neg workaround with " MACSTR, MAC2STR(dev->info.p2p_device_addr)); dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG; p2p_connect_send(p2p, dev); return; } if (success && p2p->cfg->prov_disc_resp) p2p->cfg->prov_disc_resp(p2p->cfg->cb_ctx, sa, report_config_methods); if (p2p->state == P2P_PD_DURING_FIND) { p2p_clear_timeout(p2p); p2p_continue_find(p2p); } }