From db6ae69e6b71d34bb03c8e75f78c6dc74985cda5 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Sat, 27 Apr 2013 18:50:14 +0300 Subject: [PATCH] P2P NFC: Report connection handover as trigger for P2P "NFC_REPORT_HANDOVER {INIT,RESP} P2P " can now be used to report completed NFC negotiated connection handover in which the P2P alternative carrier was selected. Signed-hostap: Jouni Malinen --- src/p2p/p2p.c | 102 +++++++++++ src/p2p/p2p.h | 21 +++ src/p2p/p2p_go_neg.c | 41 ++++- src/p2p/p2p_i.h | 3 + src/wps/wps.c | 1 - src/wps/wps_registrar.c | 30 +++- wpa_supplicant/ap.c | 46 +++++ wpa_supplicant/ap.h | 2 + wpa_supplicant/ctrl_iface.c | 6 + wpa_supplicant/p2p_supplicant.c | 280 +++++++++++++++++++++++++++++- wpa_supplicant/p2p_supplicant.h | 3 + wpa_supplicant/wpa_supplicant_i.h | 7 + 12 files changed, 531 insertions(+), 11 deletions(-) diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c index ae3ba85e6..67bd859ca 100644 --- a/src/p2p/p2p.c +++ b/src/p2p/p2p.c @@ -4421,4 +4421,106 @@ struct wpabuf * p2p_build_nfc_handover_sel(struct p2p_data *p2p) return p2p_build_nfc_handover(p2p); } + +int p2p_process_nfc_connection_handover(struct p2p_data *p2p, + struct p2p_nfc_params *params) +{ + struct p2p_message msg; + struct p2p_device *dev; + const u8 *p2p_dev_addr; + int peer_go = 0; + + params->next_step = NO_ACTION; + + if (p2p_parse_ies_separate(params->wsc_attr, params->wsc_len, + params->p2p_attr, params->p2p_len, &msg)) { + p2p_dbg(p2p, "Failed to parse WSC/P2P attributes from NFC"); + p2p_parse_free(&msg); + return -1; + } + + if (msg.p2p_device_addr) + p2p_dev_addr = msg.p2p_device_addr; + else if (msg.device_id) + p2p_dev_addr = msg.device_id; + else { + p2p_dbg(p2p, "Ignore scan data without P2P Device Info or P2P Device Id"); + p2p_parse_free(&msg); + return -1; + } + + if (msg.oob_dev_password) { + os_memcpy(params->oob_dev_pw, msg.oob_dev_password, + msg.oob_dev_password_len); + params->oob_dev_pw_len = msg.oob_dev_password_len; + } + + dev = p2p_create_device(p2p, p2p_dev_addr); + if (dev == NULL) { + p2p_parse_free(&msg); + return -1; + } + + params->peer = &dev->info; + + os_get_reltime(&dev->last_seen); + dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY); + p2p_copy_wps_info(p2p, dev, 0, &msg); + + if (msg.oob_go_neg_channel) { + int freq; + if (msg.oob_go_neg_channel[3] == 0 && + msg.oob_go_neg_channel[4] == 0) + freq = 0; + else + freq = p2p_channel_to_freq(msg.oob_go_neg_channel[3], + msg.oob_go_neg_channel[4]); + if (freq < 0) { + p2p_dbg(p2p, "Unknown peer OOB GO Neg channel"); + } else { + p2p_dbg(p2p, "Peer OOB GO Neg channel: %u MHz", freq); + dev->oob_go_neg_freq = freq; + } + + if (!params->sel) { + freq = p2p_channel_to_freq(p2p->cfg->reg_class, + p2p->cfg->channel); + if (freq < 0) { + p2p_dbg(p2p, "Own listen channel not known"); + return -1; + } + p2p_dbg(p2p, "Use own Listen channel as OOB GO Neg channel: %u MHz", + freq); + dev->oob_go_neg_freq = freq; + } + + if (msg.oob_go_neg_channel[5] == P2P_GO_IN_A_GROUP) + peer_go = 1; + } + + p2p_parse_free(&msg); + + if (dev->flags & P2P_DEV_USER_REJECTED) { + p2p_dbg(p2p, "Do not report rejected device"); + return 0; + } + + if (!(dev->flags & P2P_DEV_REPORTED)) { + p2p->cfg->dev_found(p2p->cfg->cb_ctx, p2p_dev_addr, &dev->info, + !(dev->flags & P2P_DEV_REPORTED_ONCE)); + dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE; + } + + if (peer_go) + params->next_step = JOIN_GROUP; + else if (p2p->num_groups > 0) + params->next_step = AUTH_JOIN; + else if (params->sel) + params->next_step = INIT_GO_NEG; + else + params->next_step = RESP_GO_NEG; + + return 0; +} + #endif /* CONFIG_WPS_NFC */ diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h index 1dc71fa3a..2a64ca5cf 100644 --- a/src/p2p/p2p.h +++ b/src/p2p/p2p.h @@ -9,6 +9,8 @@ #ifndef P2P_H #define P2P_H +#include "wps/wps_defs.h" + /** * P2P_MAX_REG_CLASSES - Maximum number of regulatory classes */ @@ -1900,4 +1902,23 @@ const char * p2p_get_state_txt(struct p2p_data *p2p); struct wpabuf * p2p_build_nfc_handover_req(struct p2p_data *p2p); struct wpabuf * p2p_build_nfc_handover_sel(struct p2p_data *p2p); +struct p2p_nfc_params { + int sel; + const u8 *wsc_attr; + size_t wsc_len; + const u8 *p2p_attr; + size_t p2p_len; + + enum { + NO_ACTION, JOIN_GROUP, AUTH_JOIN, INIT_GO_NEG, RESP_GO_NEG + } next_step; + struct p2p_peer_info *peer; + u8 oob_dev_pw[WPS_OOB_PUBKEY_HASH_LEN + 2 + + WPS_OOB_DEVICE_PASSWORD_LEN]; + size_t oob_dev_pw_len; +}; + +int p2p_process_nfc_connection_handover(struct p2p_data *p2p, + struct p2p_nfc_params *params); + #endif /* P2P_H */ diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c index f23cff633..76436f5d0 100644 --- a/src/p2p/p2p_go_neg.c +++ b/src/p2p/p2p_go_neg.c @@ -217,6 +217,8 @@ int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev) } freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq; + if (dev->oob_go_neg_freq > 0) + freq = dev->oob_go_neg_freq; if (freq <= 0) { p2p_dbg(p2p, "No Listen/Operating frequency known for the peer " MACSTR " to send GO Negotiation Request", @@ -614,7 +616,11 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, if (dev && dev->flags & P2P_DEV_USER_REJECTED) { p2p_dbg(p2p, "User has rejected this peer"); status = P2P_SC_FAIL_REJECTED_BY_USER; - } else if (dev == NULL || dev->wps_method == WPS_NOT_READY) { + } else if (dev == NULL || + (dev->wps_method == WPS_NOT_READY && + (p2p->authorized_oob_dev_pw_id == 0 || + p2p->authorized_oob_dev_pw_id != + msg.dev_password_id))) { p2p_dbg(p2p, "Not ready for GO negotiation with " MACSTR, MAC2STR(sa)); status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; @@ -701,6 +707,28 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, } break; default: + if (msg.dev_password_id && + msg.dev_password_id == dev->oob_pw_id) { + p2p_dbg(p2p, "Peer using NFC"); + if (dev->wps_method != WPS_NFC) { + 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; + } +#ifdef CONFIG_WPS_NFC + if (p2p->authorized_oob_dev_pw_id && + msg.dev_password_id == + p2p->authorized_oob_dev_pw_id) { + p2p_dbg(p2p, "Using static handover with our device password from NFC Tag"); + dev->wps_method = WPS_NFC; + dev->oob_pw_id = p2p->authorized_oob_dev_pw_id; + break; + } +#endif /* CONFIG_WPS_NFC */ p2p_dbg(p2p, "Unsupported Device Password ID %d", msg.dev_password_id); status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; @@ -1026,6 +1054,17 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, } break; default: + if (msg.dev_password_id && + msg.dev_password_id == dev->oob_pw_id) { + p2p_dbg(p2p, "Peer using NFC"); + if (dev->wps_method != WPS_NFC) { + 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; + } p2p_dbg(p2p, "Unsupported Device Password ID %d", msg.dev_password_id); status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h index 210184105..1150d4051 100644 --- a/src/p2p/p2p_i.h +++ b/src/p2p/p2p_i.h @@ -27,6 +27,7 @@ struct p2p_device { struct dl_list list; struct os_reltime last_seen; int listen_freq; + int oob_go_neg_freq; enum p2p_wps_method wps_method; u16 oob_pw_id; @@ -465,6 +466,8 @@ struct p2p_data { struct wpabuf *wfd_assoc_bssid; struct wpabuf *wfd_coupled_sink_info; #endif /* CONFIG_WIFI_DISPLAY */ + + u16 authorized_oob_dev_pw_id; }; /** diff --git a/src/wps/wps.c b/src/wps/wps.c index a38a6dbee..b40a68aeb 100644 --- a/src/wps/wps.c +++ b/src/wps/wps.c @@ -178,7 +178,6 @@ void wps_deinit(struct wps_data *data) wps_device_data_free(&data->peer_dev); os_free(data->new_ap_settings); dh5_free(data->dh_ctx); - os_free(data->nfc_pw_token); os_free(data); } diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c index f8f2ae19a..56b8e23b4 100644 --- a/src/wps/wps_registrar.c +++ b/src/wps/wps_registrar.c @@ -31,6 +31,7 @@ struct wps_nfc_pw_token { struct dl_list list; u8 pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN]; + unsigned int peer_pk_hash_known:1; u16 pw_id; u8 dev_pw[WPS_OOB_DEVICE_PASSWORD_LEN * 2 + 1]; size_t dev_pw_len; @@ -1822,7 +1823,8 @@ static struct wpabuf * wps_build_m2(struct wps_data *wps) } #ifdef CONFIG_WPS_NFC - if (wps->nfc_pw_token && wps->nfc_pw_token->pk_hash_provided_oob) { + if (wps->nfc_pw_token && wps->nfc_pw_token->pk_hash_provided_oob && + wps->nfc_pw_token->pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) { /* * Use abbreviated handshake since public key hash allowed * Enrollee to validate our public key similarly to how Enrollee @@ -2585,7 +2587,7 @@ static enum wps_process_res wps_process_m1(struct wps_data *wps, wps->dev_pw_id, wps->wps, wps->wps->registrar); token = wps_get_nfc_pw_token( &wps->wps->registrar->nfc_pw_tokens, wps->dev_pw_id); - if (token) { + if (token && token->peer_pk_hash_known) { wpa_printf(MSG_DEBUG, "WPS: Found matching NFC " "Password Token"); dl_list_del(&token->list); @@ -2602,6 +2604,10 @@ static enum wps_process_res wps_process_m1(struct wps_data *wps, WPS_CFG_PUBLIC_KEY_HASH_MISMATCH; return WPS_CONTINUE; } + } else if (token) { + wpa_printf(MSG_DEBUG, "WPS: Found matching NFC " + "Password Token (no peer PK hash)"); + wps->nfc_pw_token = token; } } #endif /* CONFIG_WPS_NFC */ @@ -3543,13 +3549,23 @@ int wps_registrar_add_nfc_pw_token(struct wps_registrar *reg, if (dev_pw_len > WPS_OOB_DEVICE_PASSWORD_LEN) return -1; + if (pw_id == DEV_PW_NFC_CONNECTION_HANDOVER && + (pubkey_hash == NULL || !pk_hash_provided_oob)) { + wpa_printf(MSG_DEBUG, "WPS: Unexpected NFC Password Token " + "addition - missing public key hash"); + return -1; + } + wps_free_nfc_pw_tokens(®->nfc_pw_tokens, pw_id); token = os_zalloc(sizeof(*token)); if (token == NULL) return -1; - os_memcpy(token->pubkey_hash, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN); + token->peer_pk_hash_known = pubkey_hash != NULL; + if (pubkey_hash) + os_memcpy(token->pubkey_hash, pubkey_hash, + WPS_OOB_PUBKEY_HASH_LEN); token->pw_id = pw_id; token->pk_hash_provided_oob = pk_hash_provided_oob; if (dev_pw) { @@ -3615,6 +3631,14 @@ void wps_registrar_remove_nfc_pw_token(struct wps_registrar *reg, wps_registrar_remove_authorized_mac(reg, (u8 *) "\xff\xff\xff\xff\xff\xff"); wps_registrar_selected_registrar_changed(reg, 0); + + /* + * Free the NFC password token if it was used only for a single protocol + * run. The static handover case uses the same password token multiple + * times, so do not free that case here. + */ + if (token->peer_pk_hash_known) + os_free(token); } #endif /* CONFIG_WPS_NFC */ diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c index 0f8024949..9a09e3ea5 100644 --- a/wpa_supplicant/ap.c +++ b/wpa_supplicant/ap.c @@ -15,6 +15,7 @@ #include "common/ieee802_11_defs.h" #include "common/wpa_ctrl.h" #include "eapol_supp/eapol_supp_sm.h" +#include "crypto/dh_group5.h" #include "ap/hostapd.h" #include "ap/ap_config.h" #include "ap/ap_drv_ops.h" @@ -1152,3 +1153,48 @@ int wpa_supplicant_ap_mac_addr_filter(struct wpa_supplicant *wpa_s, return 0; } + + +#ifdef CONFIG_WPS_NFC +int wpas_ap_wps_add_nfc_pw(struct wpa_supplicant *wpa_s, u16 pw_id, + const struct wpabuf *pw, const u8 *pubkey_hash) +{ + struct hostapd_data *hapd; + struct wps_context *wps; + + if (!wpa_s->ap_iface) + return -1; + hapd = wpa_s->ap_iface->bss[0]; + wps = hapd->wps; + + if (wpa_s->parent->conf->wps_nfc_dh_pubkey == NULL || + wpa_s->parent->conf->wps_nfc_dh_privkey == NULL) { + wpa_printf(MSG_DEBUG, "P2P: No NFC DH key known"); + return -1; + } + + dh5_free(wps->dh_ctx); + wpabuf_free(wps->dh_pubkey); + wpabuf_free(wps->dh_privkey); + wps->dh_privkey = wpabuf_dup( + wpa_s->parent->conf->wps_nfc_dh_privkey); + wps->dh_pubkey = wpabuf_dup( + wpa_s->parent->conf->wps_nfc_dh_pubkey); + if (wps->dh_privkey == NULL || wps->dh_pubkey == NULL) { + wps->dh_ctx = NULL; + wpabuf_free(wps->dh_pubkey); + wps->dh_pubkey = NULL; + wpabuf_free(wps->dh_privkey); + wps->dh_privkey = NULL; + return -1; + } + wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, wps->dh_pubkey); + if (wps->dh_ctx == NULL) + return -1; + + return wps_registrar_add_nfc_pw_token(hapd->wps->registrar, pubkey_hash, + pw_id, + pw ? wpabuf_head(pw) : NULL, + pw ? wpabuf_len(pw) : 0, 1); +} +#endif /* CONFIG_WPS_NFC */ diff --git a/wpa_supplicant/ap.h b/wpa_supplicant/ap.h index 9138717fd..8aa5ffa29 100644 --- a/wpa_supplicant/ap.h +++ b/wpa_supplicant/ap.h @@ -72,5 +72,7 @@ wpas_ap_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s, int wpas_ap_wps_nfc_report_handover(struct wpa_supplicant *wpa_s, const struct wpabuf *req, const struct wpabuf *sel); +int wpas_ap_wps_add_nfc_pw(struct wpa_supplicant *wpa_s, u16 pw_id, + const struct wpabuf *pw, const u8 *pubkey_hash); #endif /* AP_H */ diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index cde9c461e..3bfcc909a 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -1193,6 +1193,12 @@ static int wpas_ctrl_nfc_report_handover(struct wpa_supplicant *wpa_s, ret = wpas_ap_wps_nfc_report_handover(wpa_s, req, sel); if (ret < 0) ret = wpas_er_wps_nfc_report_handover(wpa_s, req, sel); + } else if (os_strcmp(role, "INIT") == 0 && os_strcmp(type, "P2P") == 0) + { + ret = wpas_p2p_nfc_report_handover(wpa_s, 1, req, sel); + } else if (os_strcmp(role, "RESP") == 0 && os_strcmp(type, "P2P") == 0) + { + ret = wpas_p2p_nfc_report_handover(wpa_s, 0, req, sel); } else { wpa_printf(MSG_DEBUG, "NFC: Unsupported connection handover " "reported: role=%s type=%s", role, type); diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c index e61d5ceea..4e9dcc342 100644 --- a/wpa_supplicant/p2p_supplicant.c +++ b/wpa_supplicant/p2p_supplicant.c @@ -1107,9 +1107,20 @@ static void wpas_start_wps_enrollee(struct wpa_supplicant *wpa_s, res->ssid, res->ssid_len); wpa_supplicant_ap_deinit(wpa_s); wpas_copy_go_neg_results(wpa_s, res); - if (res->wps_method == WPS_PBC) + if (res->wps_method == WPS_PBC) { wpas_wps_start_pbc(wpa_s, res->peer_interface_addr, 1); - else { +#ifdef CONFIG_WPS_NFC + } else if (res->wps_method == WPS_NFC) { + wpas_wps_start_nfc(wpa_s, res->peer_interface_addr, + wpa_s->parent->p2p_oob_dev_pw, + wpa_s->parent->p2p_oob_dev_pw_id, 1, + wpa_s->parent->p2p_oob_dev_pw_id == + DEV_PW_NFC_CONNECTION_HANDOVER ? + wpa_s->parent->p2p_peer_oob_pubkey_hash : + NULL, + NULL, 0); +#endif /* CONFIG_WPS_NFC */ + } else { u16 dev_pw_id = DEV_PW_DEFAULT; if (wpa_s->p2p_wps_method == WPS_PIN_KEYPAD) dev_pw_id = DEV_PW_REGISTRAR_SPECIFIED; @@ -1233,10 +1244,24 @@ static void p2p_go_configured(void *ctx, void *data) "filtering"); return; } - if (params->wps_method == WPS_PBC) + if (params->wps_method == WPS_PBC) { wpa_supplicant_ap_wps_pbc(wpa_s, params->peer_interface_addr, params->peer_device_addr); - else if (wpa_s->p2p_pin[0]) +#ifdef CONFIG_WPS_NFC + } else if (params->wps_method == WPS_NFC) { + if (wpa_s->parent->p2p_oob_dev_pw_id != + DEV_PW_NFC_CONNECTION_HANDOVER && + !wpa_s->parent->p2p_oob_dev_pw) { + wpa_printf(MSG_DEBUG, "P2P: No NFC Dev Pw known"); + return; + } + wpas_ap_wps_add_nfc_pw( + wpa_s, wpa_s->parent->p2p_oob_dev_pw_id, + wpa_s->parent->p2p_oob_dev_pw, + wpa_s->parent->p2p_peer_oob_pk_hash_known ? + wpa_s->parent->p2p_peer_oob_pubkey_hash : NULL); +#endif /* CONFIG_WPS_NFC */ + } else if (wpa_s->p2p_pin[0]) wpa_supplicant_ap_wps_pin(wpa_s, params->peer_interface_addr, wpa_s->p2p_pin, NULL, 0, 0); os_free(wpa_s->go_params); @@ -3848,6 +3873,9 @@ void wpas_p2p_deinit(struct wpa_supplicant *wpa_s) } eloop_cancel_timeout(wpas_p2p_send_action_work_timeout, wpa_s, NULL); + wpabuf_free(wpa_s->p2p_oob_dev_pw); + wpa_s->p2p_oob_dev_pw = NULL; + /* TODO: remove group interface from the driver if this wpa_s instance * is on top of a P2P group interface */ } @@ -3942,7 +3970,9 @@ static int wpas_p2p_start_go_neg(struct wpa_supplicant *wpa_s, go_intent, own_interface_addr, force_freq, persistent_group, ssid ? ssid->ssid : NULL, ssid ? ssid->ssid_len : 0, - wpa_s->p2p_pd_before_go_neg, pref_freq, 0); + wpa_s->p2p_pd_before_go_neg, pref_freq, + wps_method == WPS_NFC ? wpa_s->p2p_oob_dev_pw_id : + 0); } @@ -3959,7 +3989,9 @@ static int wpas_p2p_auth_go_neg(struct wpa_supplicant *wpa_s, return p2p_authorize(wpa_s->global->p2p, peer_addr, wps_method, go_intent, own_interface_addr, force_freq, persistent_group, ssid ? ssid->ssid : NULL, - ssid ? ssid->ssid_len : 0, pref_freq, 0); + ssid ? ssid->ssid_len : 0, pref_freq, + wps_method == WPS_NFC ? wpa_s->p2p_oob_dev_pw_id : + 0); } @@ -7025,4 +7057,240 @@ struct wpabuf * wpas_p2p_nfc_handover_sel(struct wpa_supplicant *wpa_s, return wpas_p2p_nfc_handover(ndef, wsc, p2p); } + +static int wpas_p2p_nfc_join_group(struct wpa_supplicant *wpa_s, + struct p2p_nfc_params *params) +{ + wpa_printf(MSG_DEBUG, "P2P: Initiate join-group based on NFC " + "connection handover"); + return wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL, + WPS_NFC, 0, 0, 1, 0, wpa_s->conf->p2p_go_intent, + 0, -1, 0, 1, 1); +} + + +static int wpas_p2p_nfc_auth_join(struct wpa_supplicant *wpa_s, + struct p2p_nfc_params *params) +{ + wpa_printf(MSG_DEBUG, "P2P: Authorize join-group based on NFC " + "connection handover"); + for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) { + struct wpa_ssid *ssid = wpa_s->current_ssid; + if (ssid == NULL) + continue; + if (ssid->mode != WPAS_MODE_P2P_GO) + continue; + if (wpa_s->ap_iface == NULL) + continue; + break; + } + if (wpa_s == NULL) { + wpa_printf(MSG_DEBUG, "P2P: Could not find GO interface"); + return -1; + } + + if (wpa_s->parent->p2p_oob_dev_pw_id != + DEV_PW_NFC_CONNECTION_HANDOVER && + !wpa_s->parent->p2p_oob_dev_pw) { + wpa_printf(MSG_DEBUG, "P2P: No NFC Dev Pw known"); + return -1; + } + return wpas_ap_wps_add_nfc_pw( + wpa_s, wpa_s->parent->p2p_oob_dev_pw_id, + wpa_s->parent->p2p_oob_dev_pw, + wpa_s->parent->p2p_peer_oob_pk_hash_known ? + wpa_s->parent->p2p_peer_oob_pubkey_hash : NULL); +} + + +static int wpas_p2p_nfc_init_go_neg(struct wpa_supplicant *wpa_s, + struct p2p_nfc_params *params) +{ + wpa_printf(MSG_DEBUG, "P2P: Initiate GO Negotiation based on NFC " + "connection handover"); + return wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL, + WPS_NFC, 0, 0, 0, 0, wpa_s->conf->p2p_go_intent, + 0, -1, 0, 1, 1); +} + + +static int wpas_p2p_nfc_resp_go_neg(struct wpa_supplicant *wpa_s, + struct p2p_nfc_params *params) +{ + int res; + + wpa_printf(MSG_DEBUG, "P2P: Authorize GO Negotiation based on NFC " + "connection handover"); + res = wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL, + WPS_NFC, 0, 0, 0, 1, wpa_s->conf->p2p_go_intent, + 0, -1, 0, 1, 1); + if (res) + return res; + + res = wpas_p2p_listen(wpa_s, 60); + if (res) { + p2p_unauthorize(wpa_s->global->p2p, + params->peer->p2p_device_addr); + } + + return res; +} + + +static int wpas_p2p_nfc_connection_handover(struct wpa_supplicant *wpa_s, + const struct wpabuf *data, + int sel, int tag) +{ + const u8 *pos, *end; + u16 len, id; + struct p2p_nfc_params params; + int res; + + os_memset(¶ms, 0, sizeof(params)); + params.sel = sel; + + wpa_hexdump_buf(MSG_DEBUG, "P2P: Received NFC tag payload", data); + + pos = wpabuf_head(data); + end = pos + wpabuf_len(data); + + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "P2P: Not enough data for Length of WSC " + "attributes"); + return -1; + } + len = WPA_GET_BE16(pos); + pos += 2; + if (pos + len > end) { + wpa_printf(MSG_DEBUG, "P2P: Not enough data for WSC " + "attributes"); + return -1; + } + params.wsc_attr = pos; + params.wsc_len = len; + pos += len; + + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "P2P: Not enough data for Length of P2P " + "attributes"); + return -1; + } + len = WPA_GET_BE16(pos); + pos += 2; + if (pos + len > end) { + wpa_printf(MSG_DEBUG, "P2P: Not enough data for P2P " + "attributes"); + return -1; + } + params.p2p_attr = pos; + params.p2p_len = len; + pos += len; + + wpa_hexdump(MSG_DEBUG, "P2P: WSC attributes", + params.wsc_attr, params.wsc_len); + wpa_hexdump(MSG_DEBUG, "P2P: P2P attributes", + params.p2p_attr, params.p2p_len); + if (pos < end) { + wpa_hexdump(MSG_DEBUG, + "P2P: Ignored extra data after P2P attributes", + pos, end - pos); + } + + res = p2p_process_nfc_connection_handover(wpa_s->global->p2p, ¶ms); + if (res) + return res; + + wpabuf_free(wpa_s->p2p_oob_dev_pw); + wpa_s->p2p_oob_dev_pw = NULL; + + if (params.oob_dev_pw_len < WPS_OOB_PUBKEY_HASH_LEN + 2) { + wpa_printf(MSG_DEBUG, "P2P: No peer OOB Dev Pw " + "received"); + return -1; + } + + id = WPA_GET_BE16(params.oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN); + wpa_printf(MSG_DEBUG, "P2P: Peer OOB Dev Pw %u", id); + wpa_hexdump(MSG_DEBUG, "P2P: Peer OOB Public Key hash", + params.oob_dev_pw, WPS_OOB_PUBKEY_HASH_LEN); + os_memcpy(wpa_s->p2p_peer_oob_pubkey_hash, + params.oob_dev_pw, WPS_OOB_PUBKEY_HASH_LEN); + wpa_s->p2p_peer_oob_pk_hash_known = 1; + + if (tag) { + if (id < 0x10) { + wpa_printf(MSG_DEBUG, "P2P: Static handover - invalid " + "peer OOB Device Password Id %u", id); + return -1; + } + wpa_printf(MSG_DEBUG, "P2P: Static handover - use peer OOB " + "Device Password Id %u", id); + wpa_hexdump_key(MSG_DEBUG, "P2P: Peer OOB Device Password", + params.oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN + 2, + params.oob_dev_pw_len - + WPS_OOB_PUBKEY_HASH_LEN - 2); + wpa_s->p2p_oob_dev_pw_id = id; + wpa_s->p2p_oob_dev_pw = wpabuf_alloc_copy( + params.oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN + 2, + params.oob_dev_pw_len - + WPS_OOB_PUBKEY_HASH_LEN - 2); + if (wpa_s->p2p_oob_dev_pw == NULL) + return -1; + + if (wpa_s->conf->wps_nfc_dh_pubkey == NULL && + wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey, + &wpa_s->conf->wps_nfc_dh_privkey) < 0) + return -1; + } else { + wpa_printf(MSG_DEBUG, "P2P: Using abbreviated WPS handshake " + "without Device Password"); + wpa_s->p2p_oob_dev_pw_id = DEV_PW_NFC_CONNECTION_HANDOVER; + } + + switch (params.next_step) { + case NO_ACTION: + return 0; + case JOIN_GROUP: + return wpas_p2p_nfc_join_group(wpa_s, ¶ms); + case AUTH_JOIN: + return wpas_p2p_nfc_auth_join(wpa_s, ¶ms); + case INIT_GO_NEG: + return wpas_p2p_nfc_init_go_neg(wpa_s, ¶ms); + case RESP_GO_NEG: + /* TODO: use own OOB Dev Pw */ + return wpas_p2p_nfc_resp_go_neg(wpa_s, ¶ms); + } + + return -1; +} + + +int wpas_p2p_nfc_report_handover(struct wpa_supplicant *wpa_s, int init, + const struct wpabuf *req, + const struct wpabuf *sel) +{ + struct wpabuf *tmp; + int ret; + + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return -1; + + wpa_printf(MSG_DEBUG, "NFC: P2P connection handover reported"); + + wpa_hexdump_ascii(MSG_DEBUG, "NFC: Req", + wpabuf_head(req), wpabuf_len(req)); + wpa_hexdump_ascii(MSG_DEBUG, "NFC: Sel", + wpabuf_head(sel), wpabuf_len(sel)); + tmp = ndef_parse_p2p(init ? sel : req); + if (tmp == NULL) { + wpa_printf(MSG_DEBUG, "P2P: Could not parse NDEF"); + return -1; + } + + ret = wpas_p2p_nfc_connection_handover(wpa_s, tmp, init, 0); + wpabuf_free(tmp); + + return ret; +} + #endif /* CONFIG_WPS_NFC */ diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h index 17e7bae58..0f09d17ee 100644 --- a/wpa_supplicant/p2p_supplicant.h +++ b/wpa_supplicant/p2p_supplicant.h @@ -152,6 +152,9 @@ struct wpabuf * wpas_p2p_nfc_handover_req(struct wpa_supplicant *wpa_s, int ndef); struct wpabuf * wpas_p2p_nfc_handover_sel(struct wpa_supplicant *wpa_s, int ndef, int tag); +int wpas_p2p_nfc_report_handover(struct wpa_supplicant *wpa_s, int init, + const struct wpabuf *req, + const struct wpabuf *sel); #ifdef CONFIG_P2P int wpas_p2p_4way_hs_failed(struct wpa_supplicant *wpa_s); diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 68995fc74..df07f466f 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -12,6 +12,7 @@ #include "utils/list.h" #include "common/defs.h" #include "common/sae.h" +#include "wps/wps_defs.h" #include "config_ssid.h" extern const char *wpa_supplicant_version; @@ -713,6 +714,7 @@ struct wpa_supplicant { unsigned int p2p_go_group_formation_completed:1; unsigned int waiting_presence_resp; int p2p_first_connection_timeout; + unsigned int p2p_peer_oob_pk_hash_known:1; int p2p_persistent_go_freq; int p2p_persistent_id; int p2p_go_intent; @@ -722,6 +724,11 @@ struct wpa_supplicant { struct wpa_radio_work *p2p_scan_work; struct wpa_radio_work *p2p_listen_work; struct wpa_radio_work *p2p_send_action_work; + + u16 p2p_oob_dev_pw_id; /* OOB Device Password Id for group formation */ + struct wpabuf *p2p_oob_dev_pw; /* OOB Device Password for group + * formation */ + u8 p2p_peer_oob_pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN]; #endif /* CONFIG_P2P */ struct wpa_ssid *bgscan_ssid;