]>
Commit | Line | Data |
---|---|---|
b22128ef JM |
1 | /* |
2 | * Wi-Fi Direct - P2P provision discovery | |
3 | * Copyright (c) 2009-2010, Atheros Communications | |
4 | * | |
e22d4d95 JM |
5 | * This software may be distributed under the terms of the BSD license. |
6 | * See README for more details. | |
b22128ef JM |
7 | */ |
8 | ||
9 | #include "includes.h" | |
10 | ||
11 | #include "common.h" | |
12 | #include "common/ieee802_11_defs.h" | |
86bd36f0 | 13 | #include "common/wpa_ctrl.h" |
b22128ef JM |
14 | #include "wps/wps_defs.h" |
15 | #include "p2p_i.h" | |
16 | #include "p2p.h" | |
17 | ||
18 | ||
6b56cc2d | 19 | /* |
488f4a71 JM |
20 | * Number of retries to attempt for provision discovery requests |
21 | * in case the peer is not listening. | |
6b56cc2d | 22 | */ |
ffa45a13 | 23 | #define MAX_PROV_DISC_REQ_RETRIES 120 |
6b56cc2d JS |
24 | |
25 | ||
b22128ef JM |
26 | static void p2p_build_wps_ie_config_methods(struct wpabuf *buf, |
27 | u16 config_methods) | |
28 | { | |
29 | u8 *len; | |
30 | wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); | |
31 | len = wpabuf_put(buf, 1); | |
32 | wpabuf_put_be32(buf, WPS_DEV_OUI_WFA); | |
33 | ||
34 | /* Config Methods */ | |
35 | wpabuf_put_be16(buf, ATTR_CONFIG_METHODS); | |
36 | wpabuf_put_be16(buf, 2); | |
37 | wpabuf_put_be16(buf, config_methods); | |
38 | ||
39 | p2p_buf_update_ie_hdr(buf, len); | |
40 | } | |
41 | ||
42 | ||
ebd32943 IP |
43 | static void p2ps_add_new_group_info(struct p2p_data *p2p, |
44 | struct p2p_device *dev, | |
45 | struct wpabuf *buf) | |
369678ad KV |
46 | { |
47 | int found; | |
48 | u8 intended_addr[ETH_ALEN]; | |
d9d1b952 | 49 | u8 ssid[SSID_MAX_LEN]; |
369678ad KV |
50 | size_t ssid_len; |
51 | int group_iface; | |
ebd32943 | 52 | unsigned int force_freq; |
369678ad KV |
53 | |
54 | if (!p2p->cfg->get_go_info) | |
55 | return; | |
56 | ||
57 | found = p2p->cfg->get_go_info( | |
58 | p2p->cfg->cb_ctx, intended_addr, ssid, | |
ebd32943 | 59 | &ssid_len, &group_iface, &force_freq); |
369678ad | 60 | if (found) { |
ebd32943 IP |
61 | if (force_freq > 0) { |
62 | p2p->p2ps_prov->force_freq = force_freq; | |
63 | p2p->p2ps_prov->pref_freq = 0; | |
64 | ||
65 | if (dev) | |
66 | p2p_prepare_channel(p2p, dev, force_freq, 0, 0); | |
67 | } | |
369678ad KV |
68 | p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, |
69 | ssid, ssid_len); | |
5cc6ec0f AO |
70 | |
71 | if (group_iface) | |
72 | p2p_buf_add_intended_addr(buf, p2p->intended_addr); | |
73 | else | |
74 | p2p_buf_add_intended_addr(buf, intended_addr); | |
369678ad KV |
75 | } else { |
76 | if (!p2p->ssid_set) { | |
77 | p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len); | |
78 | p2p->ssid_set = 1; | |
79 | } | |
80 | ||
81 | /* Add pre-composed P2P Group ID */ | |
82 | p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, | |
83 | p2p->ssid, p2p->ssid_len); | |
84 | ||
85 | if (group_iface) | |
86 | p2p_buf_add_intended_addr( | |
87 | buf, p2p->intended_addr); | |
88 | else | |
89 | p2p_buf_add_intended_addr( | |
90 | buf, p2p->cfg->dev_addr); | |
91 | } | |
92 | } | |
93 | ||
94 | ||
95 | static void p2ps_add_pd_req_attrs(struct p2p_data *p2p, struct p2p_device *dev, | |
96 | struct wpabuf *buf, u16 config_methods) | |
97 | { | |
98 | struct p2ps_provision *prov = p2p->p2ps_prov; | |
c3ddf2c7 | 99 | struct p2ps_feature_capab fcap = { prov->cpt_mask, 0 }; |
369678ad | 100 | int shared_group = 0; |
d9d1b952 | 101 | u8 ssid[SSID_MAX_LEN]; |
369678ad KV |
102 | size_t ssid_len; |
103 | u8 go_dev_addr[ETH_ALEN]; | |
1f1a08b4 | 104 | u8 intended_addr[ETH_ALEN]; |
369678ad KV |
105 | |
106 | /* If we might be explicite group owner, add GO details */ | |
107 | if (prov->conncap & (P2PS_SETUP_GROUP_OWNER | | |
108 | P2PS_SETUP_NEW)) | |
ebd32943 | 109 | p2ps_add_new_group_info(p2p, dev, buf); |
369678ad KV |
110 | |
111 | if (prov->status >= 0) | |
112 | p2p_buf_add_status(buf, (u8) prov->status); | |
113 | else | |
114 | prov->method = config_methods; | |
115 | ||
116 | if (p2p->cfg->get_persistent_group) { | |
117 | shared_group = p2p->cfg->get_persistent_group( | |
118 | p2p->cfg->cb_ctx, dev->info.p2p_device_addr, NULL, 0, | |
1f1a08b4 | 119 | go_dev_addr, ssid, &ssid_len, intended_addr); |
369678ad KV |
120 | } |
121 | ||
369678ad | 122 | if (shared_group || |
ebd32943 IP |
123 | (prov->conncap & (P2PS_SETUP_CLIENT | P2PS_SETUP_NEW))) |
124 | p2p_buf_add_channel_list(buf, p2p->cfg->country, | |
125 | &p2p->channels); | |
369678ad | 126 | |
ebd32943 IP |
127 | if ((shared_group && !is_zero_ether_addr(intended_addr)) || |
128 | (prov->conncap & (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW))) | |
129 | p2p_buf_add_operating_channel(buf, p2p->cfg->country, | |
130 | p2p->op_reg_class, | |
131 | p2p->op_channel); | |
369678ad KV |
132 | |
133 | if (prov->info[0]) | |
134 | p2p_buf_add_session_info(buf, prov->info); | |
135 | ||
136 | p2p_buf_add_connection_capability(buf, prov->conncap); | |
137 | ||
138 | p2p_buf_add_advertisement_id(buf, prov->adv_id, prov->adv_mac); | |
139 | ||
140 | if (shared_group || prov->conncap == P2PS_SETUP_NEW || | |
141 | prov->conncap == | |
142 | (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW) || | |
143 | prov->conncap == | |
144 | (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT)) { | |
145 | /* Add Config Timeout */ | |
146 | p2p_buf_add_config_timeout(buf, p2p->go_timeout, | |
147 | p2p->client_timeout); | |
148 | } | |
149 | ||
150 | p2p_buf_add_listen_channel(buf, p2p->cfg->country, p2p->cfg->reg_class, | |
151 | p2p->cfg->channel); | |
152 | ||
153 | p2p_buf_add_session_id(buf, prov->session_id, prov->session_mac); | |
154 | ||
c3ddf2c7 | 155 | p2p_buf_add_feature_capability(buf, sizeof(fcap), (const u8 *) &fcap); |
369678ad | 156 | |
1f1a08b4 | 157 | if (shared_group) { |
369678ad KV |
158 | p2p_buf_add_persistent_group_info(buf, go_dev_addr, |
159 | ssid, ssid_len); | |
1f1a08b4 AO |
160 | /* Add intended interface address if it is not added yet */ |
161 | if ((prov->conncap == P2PS_SETUP_NONE || | |
162 | prov->conncap == P2PS_SETUP_CLIENT) && | |
163 | !is_zero_ether_addr(intended_addr)) | |
164 | p2p_buf_add_intended_addr(buf, intended_addr); | |
165 | } | |
369678ad KV |
166 | } |
167 | ||
168 | ||
b22128ef | 169 | static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p, |
369678ad KV |
170 | struct p2p_device *dev, |
171 | int join) | |
b22128ef JM |
172 | { |
173 | struct wpabuf *buf; | |
174 | u8 *len; | |
9675ce35 | 175 | size_t extra = 0; |
369678ad KV |
176 | u8 dialog_token = dev->dialog_token; |
177 | u16 config_methods = dev->req_config_methods; | |
178 | struct p2p_device *go = join ? dev : NULL; | |
1300cc8e | 179 | u8 group_capab; |
b22128ef | 180 | |
9675ce35 JM |
181 | #ifdef CONFIG_WIFI_DISPLAY |
182 | if (p2p->wfd_ie_prov_disc_req) | |
183 | extra = wpabuf_len(p2p->wfd_ie_prov_disc_req); | |
184 | #endif /* CONFIG_WIFI_DISPLAY */ | |
185 | ||
86bd36f0 JM |
186 | if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]) |
187 | extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]); | |
188 | ||
369678ad KV |
189 | if (p2p->p2ps_prov) |
190 | extra += os_strlen(p2p->p2ps_prov->info) + 1 + | |
191 | sizeof(struct p2ps_provision); | |
192 | ||
9675ce35 | 193 | buf = wpabuf_alloc(1000 + extra); |
b22128ef JM |
194 | if (buf == NULL) |
195 | return NULL; | |
196 | ||
197 | p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_REQ, dialog_token); | |
198 | ||
199 | len = p2p_buf_add_ie_hdr(buf); | |
1300cc8e KV |
200 | |
201 | group_capab = 0; | |
202 | if (p2p->p2ps_prov) { | |
203 | group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP; | |
204 | group_capab |= P2P_GROUP_CAPAB_PERSISTENT_RECONN; | |
205 | if (p2p->cross_connect) | |
206 | group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; | |
207 | if (p2p->cfg->p2p_intra_bss) | |
208 | group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; | |
209 | } | |
18485b54 | 210 | p2p_buf_add_capability(buf, p2p->dev_capab & |
1300cc8e KV |
211 | ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, |
212 | group_capab); | |
b22128ef | 213 | p2p_buf_add_device_info(buf, p2p, NULL); |
369678ad KV |
214 | if (p2p->p2ps_prov) { |
215 | p2ps_add_pd_req_attrs(p2p, dev, buf, config_methods); | |
216 | } else if (go) { | |
c5db8e51 KRK |
217 | p2p_buf_add_group_id(buf, go->info.p2p_device_addr, |
218 | go->oper_ssid, go->oper_ssid_len); | |
b22128ef JM |
219 | } |
220 | p2p_buf_update_ie_hdr(buf, len); | |
221 | ||
222 | /* WPS IE with Config Methods attribute */ | |
223 | p2p_build_wps_ie_config_methods(buf, config_methods); | |
224 | ||
9675ce35 JM |
225 | #ifdef CONFIG_WIFI_DISPLAY |
226 | if (p2p->wfd_ie_prov_disc_req) | |
227 | wpabuf_put_buf(buf, p2p->wfd_ie_prov_disc_req); | |
228 | #endif /* CONFIG_WIFI_DISPLAY */ | |
229 | ||
86bd36f0 JM |
230 | if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]) |
231 | wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]); | |
232 | ||
b22128ef JM |
233 | return buf; |
234 | } | |
235 | ||
236 | ||
237 | static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, | |
1300cc8e | 238 | struct p2p_device *dev, |
b22128ef | 239 | u8 dialog_token, |
1300cc8e | 240 | enum p2p_status_code status, |
9675ce35 | 241 | u16 config_methods, |
1300cc8e | 242 | u32 adv_id, |
9675ce35 | 243 | const u8 *group_id, |
1300cc8e KV |
244 | size_t group_id_len, |
245 | const u8 *persist_ssid, | |
c3ddf2c7 MS |
246 | size_t persist_ssid_len, |
247 | const u8 *fcap, | |
248 | u16 fcap_len) | |
b22128ef JM |
249 | { |
250 | struct wpabuf *buf; | |
9675ce35 | 251 | size_t extra = 0; |
1300cc8e | 252 | int persist = 0; |
b22128ef | 253 | |
9675ce35 JM |
254 | #ifdef CONFIG_WIFI_DISPLAY |
255 | struct wpabuf *wfd_ie = p2p->wfd_ie_prov_disc_resp; | |
256 | if (wfd_ie && group_id) { | |
257 | size_t i; | |
258 | for (i = 0; i < p2p->num_groups; i++) { | |
259 | struct p2p_group *g = p2p->groups[i]; | |
260 | struct wpabuf *ie; | |
261 | if (!p2p_group_is_group_id_match(g, group_id, | |
262 | group_id_len)) | |
263 | continue; | |
264 | ie = p2p_group_get_wfd_ie(g); | |
265 | if (ie) { | |
266 | wfd_ie = ie; | |
267 | break; | |
268 | } | |
269 | } | |
270 | } | |
271 | if (wfd_ie) | |
272 | extra = wpabuf_len(wfd_ie); | |
273 | #endif /* CONFIG_WIFI_DISPLAY */ | |
274 | ||
86bd36f0 JM |
275 | if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]) |
276 | extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]); | |
277 | ||
1300cc8e | 278 | buf = wpabuf_alloc(1000 + extra); |
b22128ef JM |
279 | if (buf == NULL) |
280 | return NULL; | |
281 | ||
282 | p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_RESP, dialog_token); | |
283 | ||
1300cc8e KV |
284 | /* Add P2P IE for P2PS */ |
285 | if (p2p->p2ps_prov && p2p->p2ps_prov->adv_id == adv_id) { | |
1300cc8e KV |
286 | u8 *len = p2p_buf_add_ie_hdr(buf); |
287 | struct p2ps_provision *prov = p2p->p2ps_prov; | |
288 | u8 group_capab; | |
289 | ||
290 | if (!status && prov->status != -1) | |
291 | status = prov->status; | |
292 | ||
293 | p2p_buf_add_status(buf, status); | |
294 | group_capab = P2P_GROUP_CAPAB_PERSISTENT_GROUP | | |
295 | P2P_GROUP_CAPAB_PERSISTENT_RECONN; | |
296 | if (p2p->cross_connect) | |
297 | group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; | |
298 | if (p2p->cfg->p2p_intra_bss) | |
299 | group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; | |
300 | p2p_buf_add_capability(buf, p2p->dev_capab & | |
301 | ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, | |
302 | group_capab); | |
303 | p2p_buf_add_device_info(buf, p2p, NULL); | |
304 | ||
572f1ead | 305 | if (persist_ssid && p2p->cfg->get_persistent_group && dev && |
1300cc8e KV |
306 | (status == P2P_SC_SUCCESS || |
307 | status == P2P_SC_SUCCESS_DEFERRED)) { | |
d9d1b952 | 308 | u8 ssid[SSID_MAX_LEN]; |
1300cc8e KV |
309 | size_t ssid_len; |
310 | u8 go_dev_addr[ETH_ALEN]; | |
1f1a08b4 | 311 | u8 intended_addr[ETH_ALEN]; |
1300cc8e KV |
312 | |
313 | persist = p2p->cfg->get_persistent_group( | |
314 | p2p->cfg->cb_ctx, | |
315 | dev->info.p2p_device_addr, | |
316 | persist_ssid, persist_ssid_len, go_dev_addr, | |
1f1a08b4 AO |
317 | ssid, &ssid_len, intended_addr); |
318 | if (persist) { | |
1300cc8e KV |
319 | p2p_buf_add_persistent_group_info( |
320 | buf, go_dev_addr, ssid, ssid_len); | |
1f1a08b4 AO |
321 | if (!is_zero_ether_addr(intended_addr)) |
322 | p2p_buf_add_intended_addr( | |
323 | buf, intended_addr); | |
324 | } | |
1300cc8e KV |
325 | } |
326 | ||
327 | if (!persist && (prov->conncap & P2PS_SETUP_GROUP_OWNER)) | |
ebd32943 | 328 | p2ps_add_new_group_info(p2p, dev, buf); |
1300cc8e KV |
329 | |
330 | /* Add Operating Channel if conncap indicates GO */ | |
331 | if (persist || (prov->conncap & P2PS_SETUP_GROUP_OWNER)) { | |
1300cc8e KV |
332 | if (p2p->op_reg_class && p2p->op_channel) |
333 | p2p_buf_add_operating_channel( | |
334 | buf, p2p->cfg->country, | |
335 | p2p->op_reg_class, | |
336 | p2p->op_channel); | |
337 | else | |
338 | p2p_buf_add_operating_channel( | |
339 | buf, p2p->cfg->country, | |
340 | p2p->cfg->op_reg_class, | |
341 | p2p->cfg->op_channel); | |
342 | } | |
343 | ||
344 | p2p_buf_add_channel_list(buf, p2p->cfg->country, | |
ebd32943 | 345 | &p2p->channels); |
1300cc8e KV |
346 | |
347 | if (!persist && (status == P2P_SC_SUCCESS || | |
348 | status == P2P_SC_SUCCESS_DEFERRED)) | |
349 | p2p_buf_add_connection_capability(buf, prov->conncap); | |
350 | ||
351 | p2p_buf_add_advertisement_id(buf, adv_id, prov->adv_mac); | |
352 | ||
353 | p2p_buf_add_config_timeout(buf, p2p->go_timeout, | |
354 | p2p->client_timeout); | |
355 | ||
356 | p2p_buf_add_session_id(buf, prov->session_id, | |
357 | prov->session_mac); | |
358 | ||
c3ddf2c7 | 359 | p2p_buf_add_feature_capability(buf, fcap_len, fcap); |
1300cc8e KV |
360 | p2p_buf_update_ie_hdr(buf, len); |
361 | } else if (status != P2P_SC_SUCCESS || adv_id) { | |
362 | u8 *len = p2p_buf_add_ie_hdr(buf); | |
363 | ||
364 | p2p_buf_add_status(buf, status); | |
365 | ||
366 | if (p2p->p2ps_prov) | |
367 | p2p_buf_add_advertisement_id(buf, adv_id, | |
368 | p2p->p2ps_prov->adv_mac); | |
369 | ||
370 | p2p_buf_update_ie_hdr(buf, len); | |
371 | } | |
372 | ||
b22128ef JM |
373 | /* WPS IE with Config Methods attribute */ |
374 | p2p_build_wps_ie_config_methods(buf, config_methods); | |
375 | ||
9675ce35 JM |
376 | #ifdef CONFIG_WIFI_DISPLAY |
377 | if (wfd_ie) | |
378 | wpabuf_put_buf(buf, wfd_ie); | |
379 | #endif /* CONFIG_WIFI_DISPLAY */ | |
380 | ||
86bd36f0 JM |
381 | if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]) |
382 | wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]); | |
383 | ||
b22128ef JM |
384 | return buf; |
385 | } | |
386 | ||
387 | ||
1300cc8e KV |
388 | static int p2ps_setup_p2ps_prov(struct p2p_data *p2p, u32 adv_id, |
389 | u32 session_id, u16 method, | |
390 | const u8 *session_mac, const u8 *adv_mac) | |
391 | { | |
392 | struct p2ps_provision *tmp; | |
393 | ||
394 | if (!p2p->p2ps_prov) { | |
395 | p2p->p2ps_prov = os_zalloc(sizeof(struct p2ps_provision) + 1); | |
396 | if (!p2p->p2ps_prov) | |
397 | return -1; | |
398 | } else { | |
399 | os_memset(p2p->p2ps_prov, 0, sizeof(struct p2ps_provision) + 1); | |
400 | } | |
401 | ||
402 | tmp = p2p->p2ps_prov; | |
403 | tmp->adv_id = adv_id; | |
404 | tmp->session_id = session_id; | |
405 | tmp->method = method; | |
406 | os_memcpy(tmp->session_mac, session_mac, ETH_ALEN); | |
407 | os_memcpy(tmp->adv_mac, adv_mac, ETH_ALEN); | |
408 | tmp->info[0] = '\0'; | |
409 | ||
410 | return 0; | |
411 | } | |
412 | ||
413 | ||
c3ddf2c7 MS |
414 | static u8 p2ps_own_preferred_cpt(const u8 *cpt_priority, u8 req_cpt_mask) |
415 | { | |
416 | int i; | |
417 | ||
418 | for (i = 0; cpt_priority[i]; i++) | |
419 | if (req_cpt_mask & cpt_priority[i]) | |
420 | return cpt_priority[i]; | |
421 | ||
422 | return 0; | |
423 | } | |
424 | ||
425 | ||
e4f90a6a IP |
426 | /* Check if the message contains a valid P2PS PD Request */ |
427 | static int p2ps_validate_pd_req(struct p2p_data *p2p, struct p2p_message *msg, | |
428 | const u8 *addr) | |
429 | { | |
430 | u8 group_id = 0; | |
431 | u8 intended_addr = 0; | |
432 | u8 operating_channel = 0; | |
433 | u8 channel_list = 0; | |
434 | u8 config_timeout = 0; | |
435 | u8 listen_channel = 0; | |
436 | ||
437 | #define P2PS_PD_REQ_CHECK(_val, _attr) \ | |
438 | do { \ | |
439 | if ((_val) && !msg->_attr) { \ | |
440 | p2p_dbg(p2p, "Not P2PS PD Request. Missing %s", #_attr); \ | |
441 | return -1; \ | |
442 | } \ | |
443 | } while (0) | |
444 | ||
445 | P2PS_PD_REQ_CHECK(1, adv_id); | |
446 | P2PS_PD_REQ_CHECK(1, session_id); | |
447 | P2PS_PD_REQ_CHECK(1, session_mac); | |
448 | P2PS_PD_REQ_CHECK(1, adv_mac); | |
449 | P2PS_PD_REQ_CHECK(1, capability); | |
450 | P2PS_PD_REQ_CHECK(1, p2p_device_info); | |
451 | P2PS_PD_REQ_CHECK(1, feature_cap); | |
452 | ||
453 | /* | |
454 | * We don't need to check Connection Capability, Persistent Group, | |
455 | * and related attributes for follow-on PD Request with a status | |
456 | * other than SUCCESS_DEFERRED. | |
457 | */ | |
458 | if (msg->status && *msg->status != P2P_SC_SUCCESS_DEFERRED) | |
459 | return 0; | |
460 | ||
461 | P2PS_PD_REQ_CHECK(1, conn_cap); | |
462 | ||
463 | /* | |
464 | * Note 1: A feature capability attribute structure can be changed | |
465 | * in the future. The assumption is that such modifications are | |
466 | * backward compatible, therefore we allow processing of msg.feature_cap | |
467 | * exceeding the size of the p2ps_feature_capab structure. | |
468 | * Note 2: Verification of msg.feature_cap_len below has to be changed | |
469 | * to allow 2 byte feature capability processing if | |
470 | * struct p2ps_feature_capab is extended to include additional fields | |
471 | * and it affects the structure size. | |
472 | */ | |
473 | if (msg->feature_cap_len < sizeof(struct p2ps_feature_capab)) { | |
474 | p2p_dbg(p2p, "P2PS: Invalid feature capability len"); | |
475 | return -1; | |
476 | } | |
477 | ||
478 | switch (*msg->conn_cap) { | |
479 | case P2PS_SETUP_NEW: | |
480 | group_id = 1; | |
481 | intended_addr = 1; | |
482 | operating_channel = 1; | |
483 | channel_list = 1; | |
484 | config_timeout = 1; | |
485 | listen_channel = 1; | |
486 | break; | |
487 | case P2PS_SETUP_CLIENT: | |
488 | channel_list = 1; | |
489 | listen_channel = 1; | |
490 | break; | |
491 | case P2PS_SETUP_GROUP_OWNER: | |
492 | group_id = 1; | |
493 | intended_addr = 1; | |
494 | operating_channel = 1; | |
495 | break; | |
496 | case P2PS_SETUP_NEW | P2PS_SETUP_GROUP_OWNER: | |
497 | group_id = 1; | |
498 | operating_channel = 1; | |
499 | intended_addr = 1; | |
500 | channel_list = 1; | |
501 | config_timeout = 1; | |
502 | break; | |
503 | case P2PS_SETUP_CLIENT | P2PS_SETUP_GROUP_OWNER: | |
504 | group_id = 1; | |
505 | intended_addr = 1; | |
506 | operating_channel = 1; | |
507 | channel_list = 1; | |
508 | config_timeout = 1; | |
509 | break; | |
510 | default: | |
511 | p2p_dbg(p2p, "Invalid P2PS PD connection capability"); | |
512 | return -1; | |
513 | } | |
514 | ||
515 | if (msg->persistent_dev) { | |
516 | channel_list = 1; | |
517 | config_timeout = 1; | |
518 | if (os_memcmp(msg->persistent_dev, addr, ETH_ALEN) == 0) { | |
519 | intended_addr = 1; | |
520 | operating_channel = 1; | |
521 | } | |
522 | } | |
523 | ||
524 | P2PS_PD_REQ_CHECK(group_id, group_id); | |
525 | P2PS_PD_REQ_CHECK(intended_addr, intended_addr); | |
526 | P2PS_PD_REQ_CHECK(operating_channel, operating_channel); | |
527 | P2PS_PD_REQ_CHECK(channel_list, channel_list); | |
528 | P2PS_PD_REQ_CHECK(config_timeout, config_timeout); | |
529 | P2PS_PD_REQ_CHECK(listen_channel, listen_channel); | |
530 | ||
531 | #undef P2PS_PD_REQ_CHECK | |
532 | ||
533 | return 0; | |
534 | } | |
535 | ||
536 | ||
b22128ef JM |
537 | void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, |
538 | const u8 *data, size_t len, int rx_freq) | |
539 | { | |
540 | struct p2p_message msg; | |
541 | struct p2p_device *dev; | |
542 | int freq; | |
1300cc8e | 543 | enum p2p_status_code reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; |
b22128ef | 544 | struct wpabuf *resp; |
1300cc8e KV |
545 | u32 adv_id = 0; |
546 | struct p2ps_advertisement *p2ps_adv = NULL; | |
547 | u8 conncap = P2PS_SETUP_NEW; | |
548 | u8 auto_accept = 0; | |
549 | u32 session_id = 0; | |
550 | u8 session_mac[ETH_ALEN]; | |
551 | u8 adv_mac[ETH_ALEN]; | |
2a098e36 | 552 | const u8 *group_mac; |
1300cc8e KV |
553 | int passwd_id = DEV_PW_DEFAULT; |
554 | u16 config_methods; | |
2fc866d1 | 555 | u16 allowed_config_methods = WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD; |
c3ddf2c7 MS |
556 | struct p2ps_feature_capab resp_fcap = { 0, 0 }; |
557 | struct p2ps_feature_capab *req_fcap; | |
20324d47 IP |
558 | u8 remote_conncap; |
559 | u16 method; | |
b22128ef JM |
560 | |
561 | if (p2p_parse(data, len, &msg)) | |
562 | return; | |
563 | ||
ed496f13 | 564 | p2p_dbg(p2p, "Received Provision Discovery Request from " MACSTR |
b22128ef JM |
565 | " with config methods 0x%x (freq=%d)", |
566 | MAC2STR(sa), msg.wps_config_methods, rx_freq); | |
2a098e36 | 567 | group_mac = msg.intended_addr; |
b22128ef JM |
568 | |
569 | dev = p2p_get_device(p2p, sa); | |
bfe3557a | 570 | if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { |
ed496f13 JM |
571 | p2p_dbg(p2p, "Provision Discovery Request from unknown peer " |
572 | MACSTR, MAC2STR(sa)); | |
3dfd0484 | 573 | |
c5f10e80 | 574 | if (p2p_add_device(p2p, sa, rx_freq, NULL, 0, data + 1, len - 1, |
3dfd0484 | 575 | 0)) { |
ed496f13 JM |
576 | p2p_dbg(p2p, "Provision Discovery Request add device failed " |
577 | MACSTR, MAC2STR(sa)); | |
4acd5ac6 IP |
578 | goto out; |
579 | } | |
580 | ||
581 | if (!dev) { | |
582 | dev = p2p_get_device(p2p, sa); | |
583 | if (!dev) { | |
584 | p2p_dbg(p2p, | |
585 | "Provision Discovery device not found " | |
586 | MACSTR, MAC2STR(sa)); | |
587 | goto out; | |
588 | } | |
17bef1e9 | 589 | } |
9675ce35 JM |
590 | } else if (msg.wfd_subelems) { |
591 | wpabuf_free(dev->info.wfd_subelems); | |
592 | dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); | |
b22128ef JM |
593 | } |
594 | ||
2fc866d1 AO |
595 | if (msg.adv_id) |
596 | allowed_config_methods |= WPS_CONFIG_P2PS; | |
597 | else | |
598 | allowed_config_methods |= WPS_CONFIG_PUSHBUTTON; | |
599 | ||
600 | if (!(msg.wps_config_methods & allowed_config_methods)) { | |
ed496f13 | 601 | p2p_dbg(p2p, "Unsupported Config Methods in Provision Discovery Request"); |
b22128ef JM |
602 | goto out; |
603 | } | |
604 | ||
1300cc8e KV |
605 | /* Legacy (non-P2PS) - Unknown groups allowed for P2PS */ |
606 | if (!msg.adv_id && msg.group_id) { | |
051c7bbd JM |
607 | size_t i; |
608 | for (i = 0; i < p2p->num_groups; i++) { | |
609 | if (p2p_group_is_group_id_match(p2p->groups[i], | |
610 | msg.group_id, | |
611 | msg.group_id_len)) | |
612 | break; | |
613 | } | |
614 | if (i == p2p->num_groups) { | |
ed496f13 | 615 | p2p_dbg(p2p, "PD request for unknown P2P Group ID - reject"); |
051c7bbd JM |
616 | goto out; |
617 | } | |
618 | } | |
619 | ||
4acd5ac6 IP |
620 | dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY | |
621 | P2P_DEV_PD_PEER_KEYPAD | | |
622 | P2P_DEV_PD_PEER_P2PS); | |
623 | ||
624 | /* Remove stale persistent groups */ | |
625 | if (p2p->cfg->remove_stale_groups) { | |
626 | p2p->cfg->remove_stale_groups( | |
627 | p2p->cfg->cb_ctx, dev->info.p2p_device_addr, | |
628 | msg.persistent_dev, | |
629 | msg.persistent_ssid, msg.persistent_ssid_len); | |
1300cc8e | 630 | } |
4acd5ac6 | 631 | |
b22128ef | 632 | if (msg.wps_config_methods & WPS_CONFIG_DISPLAY) { |
ed496f13 | 633 | p2p_dbg(p2p, "Peer " MACSTR |
b22128ef | 634 | " requested us to show a PIN on display", MAC2STR(sa)); |
4acd5ac6 | 635 | dev->flags |= P2P_DEV_PD_PEER_KEYPAD; |
1300cc8e | 636 | passwd_id = DEV_PW_USER_SPECIFIED; |
b22128ef | 637 | } else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) { |
ed496f13 | 638 | p2p_dbg(p2p, "Peer " MACSTR |
b22128ef JM |
639 | " requested us to write its PIN using keypad", |
640 | MAC2STR(sa)); | |
4acd5ac6 | 641 | dev->flags |= P2P_DEV_PD_PEER_DISPLAY; |
1300cc8e KV |
642 | passwd_id = DEV_PW_REGISTRAR_SPECIFIED; |
643 | } else if (msg.wps_config_methods & WPS_CONFIG_P2PS) { | |
644 | p2p_dbg(p2p, "Peer " MACSTR " requesting P2PS PIN", | |
645 | MAC2STR(sa)); | |
4acd5ac6 | 646 | dev->flags |= P2P_DEV_PD_PEER_P2PS; |
1300cc8e | 647 | passwd_id = DEV_PW_P2PS_DEFAULT; |
b22128ef JM |
648 | } |
649 | ||
1300cc8e KV |
650 | reject = P2P_SC_SUCCESS; |
651 | ||
652 | os_memset(session_mac, 0, ETH_ALEN); | |
653 | os_memset(adv_mac, 0, ETH_ALEN); | |
1300cc8e | 654 | |
e4f90a6a IP |
655 | /* |
656 | * Validate a P2PS PD Request and skip P2PS processing if not a valid | |
657 | * P2PS PD. | |
c3ddf2c7 | 658 | */ |
e4f90a6a IP |
659 | if (msg.adv_id) { |
660 | /* | |
661 | * Set adv_id here, so that in case of an error, a P2PS PD | |
662 | * Response will be sent. | |
663 | */ | |
664 | adv_id = WPA_GET_LE32(msg.adv_id); | |
665 | if (p2ps_validate_pd_req(p2p, &msg, sa) < 0) { | |
666 | reject = P2P_SC_FAIL_INVALID_PARAMS; | |
667 | goto out; | |
668 | } | |
669 | } else { | |
20324d47 | 670 | goto out; |
e4f90a6a | 671 | } |
1300cc8e | 672 | |
20324d47 | 673 | req_fcap = (struct p2ps_feature_capab *) msg.feature_cap; |
c3ddf2c7 | 674 | |
20324d47 IP |
675 | os_memcpy(session_mac, msg.session_mac, ETH_ALEN); |
676 | os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN); | |
1300cc8e | 677 | |
20324d47 | 678 | session_id = WPA_GET_LE32(msg.session_id); |
1300cc8e | 679 | |
20324d47 IP |
680 | if (msg.conn_cap) |
681 | conncap = *msg.conn_cap; | |
682 | remote_conncap = conncap; | |
1300cc8e | 683 | |
e4f90a6a IP |
684 | if (!msg.status) { |
685 | if (os_memcmp(p2p->cfg->dev_addr, msg.adv_mac, ETH_ALEN)) { | |
686 | p2p_dbg(p2p, | |
687 | "P2PS PD adv mac does not match the local one"); | |
688 | reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; | |
689 | goto out; | |
690 | } | |
691 | ||
692 | p2ps_adv = p2p_service_p2ps_id(p2p, adv_id); | |
693 | if (!p2ps_adv) { | |
694 | p2p_dbg(p2p, "P2PS PD invalid adv_id=0x%X", adv_id); | |
695 | reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; | |
696 | goto out; | |
697 | } | |
698 | p2p_dbg(p2p, "adv_id: 0x%X, p2ps_adv: %p", adv_id, p2ps_adv); | |
699 | } | |
700 | ||
20324d47 | 701 | if (p2ps_adv) { |
ebd32943 IP |
702 | unsigned int forced_freq, pref_freq; |
703 | ||
20324d47 IP |
704 | auto_accept = p2ps_adv->auto_accept; |
705 | conncap = p2p->cfg->p2ps_group_capability(p2p->cfg->cb_ctx, | |
ebd32943 IP |
706 | conncap, auto_accept, |
707 | &forced_freq, | |
708 | &pref_freq); | |
709 | ||
710 | p2p_prepare_channel(p2p, dev, forced_freq, pref_freq, 0); | |
1300cc8e | 711 | |
20324d47 IP |
712 | p2p_dbg(p2p, "Conncap: local:%d remote:%d result:%d", |
713 | auto_accept, remote_conncap, conncap); | |
1300cc8e | 714 | |
20324d47 | 715 | resp_fcap.cpt = p2ps_own_preferred_cpt(p2ps_adv->cpt_priority, |
c3ddf2c7 MS |
716 | req_fcap->cpt); |
717 | ||
20324d47 IP |
718 | p2p_dbg(p2p, "cpt: service:0x%x remote:0x%x result:0x%x", |
719 | p2ps_adv->cpt_mask, req_fcap->cpt, resp_fcap.cpt); | |
c3ddf2c7 | 720 | |
20324d47 IP |
721 | if (!resp_fcap.cpt) { |
722 | p2p_dbg(p2p, | |
723 | "Incompatible P2PS feature capability CPT bitmask"); | |
724 | reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; | |
725 | } else if (p2ps_adv->config_methods && | |
726 | !(msg.wps_config_methods & | |
727 | p2ps_adv->config_methods)) { | |
728 | p2p_dbg(p2p, | |
729 | "Unsupported config methods in Provision Discovery Request (own=0x%x peer=0x%x)", | |
730 | p2ps_adv->config_methods, | |
731 | msg.wps_config_methods); | |
732 | reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; | |
733 | } else if (!p2ps_adv->state) { | |
734 | p2p_dbg(p2p, "P2PS state unavailable"); | |
735 | reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; | |
736 | } else if (!conncap) { | |
737 | p2p_dbg(p2p, "Conncap resolution failed"); | |
1300cc8e | 738 | reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; |
1300cc8e KV |
739 | } |
740 | ||
20324d47 IP |
741 | if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) { |
742 | p2p_dbg(p2p, "Keypad - always defer"); | |
743 | auto_accept = 0; | |
744 | } | |
745 | ||
23eef570 IP |
746 | if ((remote_conncap & (P2PS_SETUP_NEW | P2PS_SETUP_CLIENT) || |
747 | msg.persistent_dev) && conncap != P2PS_SETUP_NEW && | |
748 | msg.channel_list && msg.channel_list_len && | |
749 | p2p_peer_channels_check(p2p, &p2p->channels, dev, | |
750 | msg.channel_list, | |
751 | msg.channel_list_len) < 0) { | |
752 | p2p_dbg(p2p, | |
753 | "No common channels - force deferred flow"); | |
754 | auto_accept = 0; | |
755 | } | |
756 | ||
757 | if (((remote_conncap & P2PS_SETUP_GROUP_OWNER) || | |
758 | msg.persistent_dev) && msg.operating_channel) { | |
759 | struct p2p_channels intersect; | |
760 | ||
761 | /* | |
762 | * There are cases where only the operating channel is | |
763 | * provided. This requires saving the channel as the | |
764 | * supported channel list, and verifying that it is | |
765 | * supported. | |
766 | */ | |
767 | if (dev->channels.reg_classes == 0 || | |
768 | !p2p_channels_includes(&dev->channels, | |
769 | msg.operating_channel[3], | |
770 | msg.operating_channel[4])) { | |
771 | struct p2p_channels *ch = &dev->channels; | |
772 | ||
773 | os_memset(ch, 0, sizeof(*ch)); | |
774 | ch->reg_class[0].reg_class = | |
775 | msg.operating_channel[3]; | |
776 | ch->reg_class[0].channel[0] = | |
777 | msg.operating_channel[4]; | |
778 | ch->reg_class[0].channels = 1; | |
779 | ch->reg_classes = 1; | |
780 | } | |
781 | ||
782 | p2p_channels_intersect(&p2p->channels, &dev->channels, | |
783 | &intersect); | |
784 | ||
785 | if (intersect.reg_classes == 0) { | |
786 | p2p_dbg(p2p, | |
787 | "No common channels - force deferred flow"); | |
788 | auto_accept = 0; | |
789 | } | |
790 | } | |
791 | ||
20324d47 | 792 | if (auto_accept || reject != P2P_SC_SUCCESS) { |
1300cc8e KV |
793 | struct p2ps_provision *tmp; |
794 | ||
1300cc8e KV |
795 | if (p2ps_setup_p2ps_prov(p2p, adv_id, session_id, |
796 | msg.wps_config_methods, | |
797 | session_mac, adv_mac) < 0) { | |
798 | reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; | |
799 | goto out; | |
800 | } | |
20324d47 | 801 | |
1300cc8e | 802 | tmp = p2p->p2ps_prov; |
ebd32943 IP |
803 | tmp->force_freq = forced_freq; |
804 | tmp->pref_freq = pref_freq; | |
20324d47 IP |
805 | if (conncap) { |
806 | tmp->conncap = conncap; | |
807 | tmp->status = P2P_SC_SUCCESS; | |
808 | } else { | |
809 | tmp->conncap = auto_accept; | |
810 | tmp->status = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; | |
811 | } | |
812 | ||
813 | if (reject != P2P_SC_SUCCESS) | |
814 | goto out; | |
815 | } | |
20324d47 IP |
816 | } |
817 | ||
818 | if (!msg.status && !auto_accept && | |
819 | (!p2p->p2ps_prov || p2p->p2ps_prov->adv_id != adv_id)) { | |
820 | struct p2ps_provision *tmp; | |
821 | ||
822 | if (!conncap) { | |
823 | reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; | |
824 | goto out; | |
1300cc8e KV |
825 | } |
826 | ||
20324d47 IP |
827 | if (p2ps_setup_p2ps_prov(p2p, adv_id, session_id, |
828 | msg.wps_config_methods, | |
829 | session_mac, adv_mac) < 0) { | |
830 | reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; | |
831 | goto out; | |
1300cc8e | 832 | } |
20324d47 IP |
833 | tmp = p2p->p2ps_prov; |
834 | reject = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; | |
835 | tmp->status = reject; | |
836 | } | |
837 | ||
838 | /* Not a P2PS Follow-on PD */ | |
839 | if (!msg.status) | |
840 | goto out; | |
841 | ||
842 | if (*msg.status && *msg.status != P2P_SC_SUCCESS_DEFERRED) { | |
843 | reject = *msg.status; | |
844 | goto out; | |
1300cc8e | 845 | } |
b22128ef | 846 | |
20324d47 IP |
847 | if (*msg.status != P2P_SC_SUCCESS_DEFERRED || !p2p->p2ps_prov) |
848 | goto out; | |
849 | ||
e4f90a6a IP |
850 | if (p2p->p2ps_prov->adv_id != adv_id || |
851 | os_memcmp(p2p->p2ps_prov->adv_mac, msg.adv_mac, ETH_ALEN)) { | |
852 | p2p_dbg(p2p, | |
853 | "P2PS Follow-on PD with mismatch Advertisement ID/MAC"); | |
854 | goto out; | |
855 | } | |
856 | ||
857 | if (p2p->p2ps_prov->session_id != session_id || | |
858 | os_memcmp(p2p->p2ps_prov->session_mac, msg.session_mac, ETH_ALEN)) { | |
859 | p2p_dbg(p2p, "P2PS Follow-on PD with mismatch Session ID/MAC"); | |
860 | goto out; | |
861 | } | |
862 | ||
20324d47 IP |
863 | method = p2p->p2ps_prov->method; |
864 | ||
865 | conncap = p2p->cfg->p2ps_group_capability(p2p->cfg->cb_ctx, | |
866 | remote_conncap, | |
ebd32943 IP |
867 | p2p->p2ps_prov->conncap, |
868 | &p2p->p2ps_prov->force_freq, | |
869 | &p2p->p2ps_prov->pref_freq); | |
20324d47 IP |
870 | |
871 | resp_fcap.cpt = p2ps_own_preferred_cpt(p2p->p2ps_prov->cpt_priority, | |
872 | req_fcap->cpt); | |
873 | ||
874 | p2p_dbg(p2p, "cpt: local:0x%x remote:0x%x result:0x%x", | |
875 | p2p->p2ps_prov->cpt_mask, req_fcap->cpt, resp_fcap.cpt); | |
876 | ||
ebd32943 IP |
877 | p2p_prepare_channel(p2p, dev, p2p->p2ps_prov->force_freq, |
878 | p2p->p2ps_prov->pref_freq, 0); | |
879 | ||
20324d47 IP |
880 | /* |
881 | * Ensure that if we asked for PIN originally, our method is consistent | |
882 | * with original request. | |
883 | */ | |
884 | if (method & WPS_CONFIG_DISPLAY) | |
885 | method = WPS_CONFIG_KEYPAD; | |
886 | else if (method & WPS_CONFIG_KEYPAD) | |
887 | method = WPS_CONFIG_DISPLAY; | |
888 | ||
889 | if (!conncap || !(msg.wps_config_methods & method)) { | |
890 | /* | |
891 | * Reject this "Deferred Accept* | |
892 | * if incompatible conncap or method | |
893 | */ | |
894 | reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; | |
895 | } else if (!resp_fcap.cpt) { | |
896 | p2p_dbg(p2p, | |
897 | "Incompatible P2PS feature capability CPT bitmask"); | |
898 | reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; | |
23eef570 IP |
899 | } else if ((remote_conncap & (P2PS_SETUP_NEW | P2PS_SETUP_CLIENT) || |
900 | msg.persistent_dev) && conncap != P2PS_SETUP_NEW && | |
901 | msg.channel_list && msg.channel_list_len && | |
902 | p2p_peer_channels_check(p2p, &p2p->channels, dev, | |
903 | msg.channel_list, | |
904 | msg.channel_list_len) < 0) { | |
905 | p2p_dbg(p2p, | |
906 | "No common channels in Follow-On Provision Discovery Request"); | |
907 | reject = P2P_SC_FAIL_NO_COMMON_CHANNELS; | |
20324d47 IP |
908 | } else { |
909 | reject = P2P_SC_SUCCESS; | |
910 | } | |
911 | ||
23eef570 IP |
912 | dev->oper_freq = 0; |
913 | if (reject == P2P_SC_SUCCESS || reject == P2P_SC_SUCCESS_DEFERRED) { | |
914 | u8 tmp; | |
915 | ||
916 | if (msg.operating_channel) | |
917 | dev->oper_freq = | |
918 | p2p_channel_to_freq(msg.operating_channel[3], | |
919 | msg.operating_channel[4]); | |
920 | ||
921 | if ((conncap & P2PS_SETUP_GROUP_OWNER) && | |
922 | p2p_go_select_channel(p2p, dev, &tmp) < 0) | |
923 | reject = P2P_SC_FAIL_NO_COMMON_CHANNELS; | |
924 | } | |
925 | ||
20324d47 IP |
926 | p2p->p2ps_prov->status = reject; |
927 | p2p->p2ps_prov->conncap = conncap; | |
928 | ||
b22128ef | 929 | out: |
1300cc8e KV |
930 | if (reject == P2P_SC_SUCCESS || |
931 | reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) | |
932 | config_methods = msg.wps_config_methods; | |
933 | else | |
934 | config_methods = 0; | |
f94e4c20 MS |
935 | |
936 | /* | |
937 | * Send PD Response for an initial PD Request or for follow-on | |
938 | * PD Request with P2P_SC_SUCCESS_DEFERRED status. | |
939 | */ | |
940 | if (!msg.status || *msg.status == P2P_SC_SUCCESS_DEFERRED) { | |
941 | resp = p2p_build_prov_disc_resp(p2p, dev, msg.dialog_token, | |
942 | reject, config_methods, adv_id, | |
943 | msg.group_id, msg.group_id_len, | |
944 | msg.persistent_ssid, | |
945 | msg.persistent_ssid_len, | |
946 | (const u8 *) &resp_fcap, | |
947 | sizeof(resp_fcap)); | |
948 | if (!resp) { | |
949 | p2p_parse_free(&msg); | |
950 | return; | |
951 | } | |
952 | p2p_dbg(p2p, "Sending Provision Discovery Response"); | |
953 | if (rx_freq > 0) | |
954 | freq = rx_freq; | |
955 | else | |
956 | freq = p2p_channel_to_freq(p2p->cfg->reg_class, | |
957 | p2p->cfg->channel); | |
958 | if (freq < 0) { | |
959 | p2p_dbg(p2p, "Unknown regulatory class/channel"); | |
960 | wpabuf_free(resp); | |
961 | p2p_parse_free(&msg); | |
962 | return; | |
963 | } | |
964 | p2p->pending_action_state = P2P_PENDING_PD_RESPONSE; | |
965 | if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, | |
966 | p2p->cfg->dev_addr, | |
967 | wpabuf_head(resp), wpabuf_len(resp), | |
968 | 200) < 0) | |
969 | p2p_dbg(p2p, "Failed to send Action frame"); | |
970 | else | |
971 | p2p->send_action_in_progress = 1; | |
972 | ||
b22128ef | 973 | wpabuf_free(resp); |
b22128ef | 974 | } |
b22128ef | 975 | |
4acd5ac6 IP |
976 | if (!dev) { |
977 | p2p_parse_free(&msg); | |
978 | return; | |
979 | } | |
980 | ||
8bb8e6ed IP |
981 | freq = 0; |
982 | if (reject == P2P_SC_SUCCESS && conncap == P2PS_SETUP_GROUP_OWNER) { | |
983 | freq = p2p_channel_to_freq(p2p->op_reg_class, | |
984 | p2p->op_channel); | |
985 | if (freq < 0) | |
986 | freq = 0; | |
987 | } | |
988 | ||
1300cc8e KV |
989 | if (!p2p->cfg->p2ps_prov_complete) { |
990 | /* Don't emit anything */ | |
991 | } else if (msg.status && *msg.status != P2P_SC_SUCCESS && | |
992 | *msg.status != P2P_SC_SUCCESS_DEFERRED) { | |
993 | reject = *msg.status; | |
994 | p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, reject, | |
995 | sa, adv_mac, session_mac, | |
996 | NULL, adv_id, session_id, | |
997 | 0, 0, msg.persistent_ssid, | |
998 | msg.persistent_ssid_len, | |
8bb8e6ed | 999 | 0, 0, NULL, NULL, 0, freq); |
1300cc8e KV |
1000 | } else if (msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED && |
1001 | p2p->p2ps_prov) { | |
1002 | p2p->p2ps_prov->status = reject; | |
1003 | p2p->p2ps_prov->conncap = conncap; | |
1004 | ||
1005 | if (reject != P2P_SC_SUCCESS) | |
1006 | p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, reject, | |
1007 | sa, adv_mac, session_mac, | |
1008 | NULL, adv_id, | |
1009 | session_id, conncap, 0, | |
1010 | msg.persistent_ssid, | |
1011 | msg.persistent_ssid_len, 0, | |
8bb8e6ed | 1012 | 0, NULL, NULL, 0, freq); |
1300cc8e KV |
1013 | else |
1014 | p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, | |
1015 | *msg.status, | |
1016 | sa, adv_mac, session_mac, | |
1017 | group_mac, adv_id, | |
1018 | session_id, conncap, | |
1019 | passwd_id, | |
1020 | msg.persistent_ssid, | |
1021 | msg.persistent_ssid_len, 0, | |
88f3d7c9 MS |
1022 | 0, NULL, |
1023 | (const u8 *) &resp_fcap, | |
8bb8e6ed | 1024 | sizeof(resp_fcap), freq); |
1300cc8e KV |
1025 | } else if (msg.status && p2p->p2ps_prov) { |
1026 | p2p->p2ps_prov->status = P2P_SC_SUCCESS; | |
1027 | p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, *msg.status, sa, | |
1028 | adv_mac, session_mac, group_mac, | |
1029 | adv_id, session_id, conncap, | |
1030 | passwd_id, | |
1031 | msg.persistent_ssid, | |
1032 | msg.persistent_ssid_len, | |
88f3d7c9 MS |
1033 | 0, 0, NULL, |
1034 | (const u8 *) &resp_fcap, | |
8bb8e6ed | 1035 | sizeof(resp_fcap), freq); |
1300cc8e KV |
1036 | } else if (msg.status) { |
1037 | } else if (auto_accept && reject == P2P_SC_SUCCESS) { | |
1038 | p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, P2P_SC_SUCCESS, | |
1039 | sa, adv_mac, session_mac, | |
1040 | group_mac, adv_id, session_id, | |
1041 | conncap, passwd_id, | |
1042 | msg.persistent_ssid, | |
1043 | msg.persistent_ssid_len, | |
88f3d7c9 MS |
1044 | 0, 0, NULL, |
1045 | (const u8 *) &resp_fcap, | |
8bb8e6ed | 1046 | sizeof(resp_fcap), freq); |
1300cc8e KV |
1047 | } else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE && |
1048 | (!msg.session_info || !msg.session_info_len)) { | |
1049 | p2p->p2ps_prov->method = msg.wps_config_methods; | |
1050 | ||
1051 | p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, P2P_SC_SUCCESS, | |
1052 | sa, adv_mac, session_mac, | |
1053 | group_mac, adv_id, session_id, | |
1054 | conncap, passwd_id, | |
1055 | msg.persistent_ssid, | |
1056 | msg.persistent_ssid_len, | |
88f3d7c9 MS |
1057 | 0, 1, NULL, |
1058 | (const u8 *) &resp_fcap, | |
8bb8e6ed | 1059 | sizeof(resp_fcap), freq); |
1300cc8e KV |
1060 | } else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) { |
1061 | size_t buf_len = msg.session_info_len; | |
1062 | char *buf = os_malloc(2 * buf_len + 1); | |
1063 | ||
1064 | if (buf) { | |
1065 | p2p->p2ps_prov->method = msg.wps_config_methods; | |
1066 | ||
1067 | utf8_escape((char *) msg.session_info, buf_len, | |
1068 | buf, 2 * buf_len + 1); | |
1069 | ||
1070 | p2p->cfg->p2ps_prov_complete( | |
1071 | p2p->cfg->cb_ctx, P2P_SC_SUCCESS, sa, | |
1072 | adv_mac, session_mac, group_mac, adv_id, | |
1073 | session_id, conncap, passwd_id, | |
1074 | msg.persistent_ssid, msg.persistent_ssid_len, | |
88f3d7c9 | 1075 | 0, 1, buf, |
8bb8e6ed IP |
1076 | (const u8 *) &resp_fcap, sizeof(resp_fcap), |
1077 | freq); | |
1300cc8e KV |
1078 | |
1079 | os_free(buf); | |
1080 | } | |
1081 | } | |
1082 | ||
93f22b45 MS |
1083 | /* |
1084 | * prov_disc_req callback is used to generate P2P-PROV-DISC-ENTER-PIN, | |
1085 | * P2P-PROV-DISC-SHOW-PIN, and P2P-PROV-DISC-PBC-REQ events. | |
1086 | * Call it either on legacy P2P PD or on P2PS PD only if we need to | |
1087 | * enter/show PIN. | |
1088 | * | |
1089 | * The callback is called in the following cases: | |
1090 | * 1. Legacy P2P PD request, response status SUCCESS | |
1091 | * 2. P2PS advertiser, method: DISPLAY, autoaccept: TRUE, | |
1092 | * response status: SUCCESS | |
1093 | * 3. P2PS advertiser, method DISPLAY, autoaccept: FALSE, | |
1094 | * response status: INFO_CURRENTLY_UNAVAILABLE | |
1095 | * 4. P2PS advertiser, method: KEYPAD, autoaccept==any, | |
1096 | * response status: INFO_CURRENTLY_UNAVAILABLE | |
1097 | * 5. P2PS follow-on with SUCCESS_DEFERRED, | |
1098 | * advertiser role: DISPLAY, autoaccept: FALSE, | |
1099 | * seeker: KEYPAD, response status: SUCCESS | |
1100 | */ | |
1101 | if (p2p->cfg->prov_disc_req && | |
1102 | ((reject == P2P_SC_SUCCESS && !msg.adv_id) || | |
1103 | (!msg.status && | |
1104 | (reject == P2P_SC_SUCCESS || | |
1105 | reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) && | |
1106 | passwd_id == DEV_PW_USER_SPECIFIED) || | |
1107 | (!msg.status && | |
1108 | reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE && | |
1109 | passwd_id == DEV_PW_REGISTRAR_SPECIFIED) || | |
1110 | (reject == P2P_SC_SUCCESS && | |
1111 | msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED && | |
1112 | passwd_id == DEV_PW_REGISTRAR_SPECIFIED))) { | |
b22128ef | 1113 | const u8 *dev_addr = sa; |
93f22b45 | 1114 | |
b22128ef JM |
1115 | if (msg.p2p_device_addr) |
1116 | dev_addr = msg.p2p_device_addr; | |
1117 | p2p->cfg->prov_disc_req(p2p->cfg->cb_ctx, sa, | |
1118 | msg.wps_config_methods, | |
1119 | dev_addr, msg.pri_dev_type, | |
1120 | msg.device_name, msg.config_methods, | |
1121 | msg.capability ? msg.capability[0] : 0, | |
1122 | msg.capability ? msg.capability[1] : | |
c3f42784 JM |
1123 | 0, |
1124 | msg.group_id, msg.group_id_len); | |
93f22b45 | 1125 | } |
223ccebf | 1126 | |
4acd5ac6 | 1127 | if (reject == P2P_SC_SUCCESS) { |
93f22b45 MS |
1128 | switch (config_methods) { |
1129 | case WPS_CONFIG_DISPLAY: | |
1130 | dev->wps_prov_info = WPS_CONFIG_KEYPAD; | |
1131 | break; | |
1132 | case WPS_CONFIG_KEYPAD: | |
1133 | dev->wps_prov_info = WPS_CONFIG_DISPLAY; | |
1134 | break; | |
1135 | case WPS_CONFIG_PUSHBUTTON: | |
1136 | dev->wps_prov_info = WPS_CONFIG_PUSHBUTTON; | |
1137 | break; | |
1138 | case WPS_CONFIG_P2PS: | |
1139 | dev->wps_prov_info = WPS_CONFIG_P2PS; | |
1140 | break; | |
1141 | default: | |
1142 | dev->wps_prov_info = 0; | |
1143 | break; | |
223ccebf | 1144 | } |
93f22b45 MS |
1145 | |
1146 | if (msg.intended_addr) | |
1147 | os_memcpy(dev->interface_addr, msg.intended_addr, | |
1148 | ETH_ALEN); | |
b22128ef JM |
1149 | } |
1150 | p2p_parse_free(&msg); | |
1151 | } | |
1152 | ||
1153 | ||
1f14e2bf AO |
1154 | static int p2p_validate_p2ps_pd_resp(struct p2p_data *p2p, |
1155 | struct p2p_message *msg) | |
1156 | { | |
1157 | u8 conn_cap_go = 0; | |
1158 | u8 conn_cap_cli = 0; | |
1159 | u32 session_id; | |
1160 | u32 adv_id; | |
1161 | ||
1162 | #define P2PS_PD_RESP_CHECK(_val, _attr) \ | |
1163 | do { \ | |
1164 | if ((_val) && !msg->_attr) { \ | |
1165 | p2p_dbg(p2p, "P2PS PD Response missing " #_attr); \ | |
1166 | return -1; \ | |
1167 | } \ | |
1168 | } while (0) | |
1169 | ||
1170 | P2PS_PD_RESP_CHECK(1, status); | |
1171 | P2PS_PD_RESP_CHECK(1, adv_id); | |
1172 | P2PS_PD_RESP_CHECK(1, adv_mac); | |
1173 | P2PS_PD_RESP_CHECK(1, capability); | |
1174 | P2PS_PD_RESP_CHECK(1, p2p_device_info); | |
1175 | P2PS_PD_RESP_CHECK(1, session_id); | |
1176 | P2PS_PD_RESP_CHECK(1, session_mac); | |
1177 | P2PS_PD_RESP_CHECK(1, feature_cap); | |
1178 | ||
1179 | session_id = WPA_GET_LE32(msg->session_id); | |
1180 | adv_id = WPA_GET_LE32(msg->adv_id); | |
1181 | ||
1182 | if (p2p->p2ps_prov->session_id != session_id) { | |
1183 | p2p_dbg(p2p, | |
1184 | "Ignore PD Response with unexpected Session ID"); | |
1185 | return -1; | |
1186 | } | |
1187 | ||
1188 | if (os_memcmp(p2p->p2ps_prov->session_mac, msg->session_mac, | |
1189 | ETH_ALEN)) { | |
1190 | p2p_dbg(p2p, | |
1191 | "Ignore PD Response with unexpected Session MAC"); | |
1192 | return -1; | |
1193 | } | |
1194 | ||
1195 | if (p2p->p2ps_prov->adv_id != adv_id) { | |
1196 | p2p_dbg(p2p, | |
1197 | "Ignore PD Response with unexpected Advertisement ID"); | |
1198 | return -1; | |
1199 | } | |
1200 | ||
1201 | if (os_memcmp(p2p->p2ps_prov->adv_mac, msg->adv_mac, ETH_ALEN) != 0) { | |
1202 | p2p_dbg(p2p, | |
1203 | "Ignore PD Response with unexpected Advertisement MAC"); | |
1204 | return -1; | |
1205 | } | |
1206 | ||
1207 | if (msg->listen_channel) { | |
1208 | p2p_dbg(p2p, | |
1209 | "Ignore malformed PD Response - unexpected Listen Channel"); | |
1210 | return -1; | |
1211 | } | |
1212 | ||
1213 | if (*msg->status == P2P_SC_SUCCESS && | |
1214 | !(!!msg->conn_cap ^ !!msg->persistent_dev)) { | |
1215 | p2p_dbg(p2p, | |
1216 | "Ignore malformed PD Response - either conn_cap or persistent group should be present"); | |
1217 | return -1; | |
1218 | } | |
1219 | ||
1220 | if (msg->persistent_dev && *msg->status != P2P_SC_SUCCESS) { | |
1221 | p2p_dbg(p2p, | |
1222 | "Ignore malformed PD Response - persistent group is present, but the status isn't success"); | |
1223 | return -1; | |
1224 | } | |
1225 | ||
1226 | if (msg->conn_cap) { | |
1227 | conn_cap_go = *msg->conn_cap == P2PS_SETUP_GROUP_OWNER; | |
1228 | conn_cap_cli = *msg->conn_cap == P2PS_SETUP_CLIENT; | |
1229 | } | |
1230 | ||
1231 | P2PS_PD_RESP_CHECK(msg->persistent_dev || conn_cap_go || conn_cap_cli, | |
1232 | channel_list); | |
1233 | P2PS_PD_RESP_CHECK(msg->persistent_dev || conn_cap_go || conn_cap_cli, | |
1234 | config_timeout); | |
1235 | ||
1236 | P2PS_PD_RESP_CHECK(conn_cap_go, group_id); | |
1237 | P2PS_PD_RESP_CHECK(conn_cap_go, intended_addr); | |
1238 | P2PS_PD_RESP_CHECK(conn_cap_go, operating_channel); | |
1239 | /* | |
1240 | * TODO: Also validate that operating channel is present if the device | |
1241 | * is a GO in a persistent group. We can't do it here since we don't | |
1242 | * know what is the role of the peer. It should be probably done in | |
1243 | * p2ps_prov_complete callback, but currently operating channel isn't | |
1244 | * passed to it. | |
1245 | */ | |
1246 | ||
1247 | #undef P2PS_PD_RESP_CHECK | |
1248 | ||
1249 | return 0; | |
1250 | } | |
1251 | ||
1252 | ||
b22128ef JM |
1253 | void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, |
1254 | const u8 *data, size_t len) | |
1255 | { | |
1256 | struct p2p_message msg; | |
1257 | struct p2p_device *dev; | |
66f1f751 | 1258 | u16 report_config_methods = 0, req_config_methods; |
9e96e464 | 1259 | u8 status = P2P_SC_SUCCESS; |
9e96e464 KV |
1260 | u32 adv_id = 0; |
1261 | u8 conncap = P2PS_SETUP_NEW; | |
1262 | u8 adv_mac[ETH_ALEN]; | |
2a098e36 | 1263 | const u8 *group_mac; |
9e96e464 | 1264 | int passwd_id = DEV_PW_DEFAULT; |
93f22b45 | 1265 | int p2ps_seeker; |
b22128ef JM |
1266 | |
1267 | if (p2p_parse(data, len, &msg)) | |
1268 | return; | |
1269 | ||
1f14e2bf AO |
1270 | if (p2p->p2ps_prov && p2p_validate_p2ps_pd_resp(p2p, &msg)) { |
1271 | p2p_parse_free(&msg); | |
1272 | return; | |
1273 | } | |
1274 | ||
9e96e464 KV |
1275 | /* Parse the P2PS members present */ |
1276 | if (msg.status) | |
1277 | status = *msg.status; | |
1278 | ||
2a098e36 | 1279 | group_mac = msg.intended_addr; |
9e96e464 KV |
1280 | |
1281 | if (msg.adv_mac) | |
1282 | os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN); | |
1283 | else | |
1284 | os_memset(adv_mac, 0, ETH_ALEN); | |
1285 | ||
1286 | if (msg.adv_id) | |
1287 | adv_id = WPA_GET_LE32(msg.adv_id); | |
1288 | ||
1289 | if (msg.conn_cap) { | |
1290 | conncap = *msg.conn_cap; | |
1291 | ||
1292 | /* Switch bits to local relative */ | |
1293 | switch (conncap) { | |
1294 | case P2PS_SETUP_GROUP_OWNER: | |
1295 | conncap = P2PS_SETUP_CLIENT; | |
1296 | break; | |
1297 | case P2PS_SETUP_CLIENT: | |
1298 | conncap = P2PS_SETUP_GROUP_OWNER; | |
1299 | break; | |
1300 | } | |
1301 | } | |
1302 | ||
ed496f13 | 1303 | p2p_dbg(p2p, "Received Provision Discovery Response from " MACSTR |
b22128ef JM |
1304 | " with config methods 0x%x", |
1305 | MAC2STR(sa), msg.wps_config_methods); | |
1306 | ||
1307 | dev = p2p_get_device(p2p, sa); | |
1308 | if (dev == NULL || !dev->req_config_methods) { | |
ed496f13 JM |
1309 | p2p_dbg(p2p, "Ignore Provision Discovery Response from " MACSTR |
1310 | " with no pending request", MAC2STR(sa)); | |
b22128ef JM |
1311 | p2p_parse_free(&msg); |
1312 | return; | |
1313 | } | |
1314 | ||
1315 | if (dev->dialog_token != msg.dialog_token) { | |
ed496f13 | 1316 | p2p_dbg(p2p, "Ignore Provision Discovery Response with unexpected Dialog Token %u (expected %u)", |
b22128ef JM |
1317 | msg.dialog_token, dev->dialog_token); |
1318 | p2p_parse_free(&msg); | |
1319 | return; | |
1320 | } | |
1321 | ||
213c1fa8 JM |
1322 | if (p2p->pending_action_state == P2P_PENDING_PD) { |
1323 | os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN); | |
1324 | p2p->pending_action_state = P2P_NO_PENDING_ACTION; | |
1325 | } | |
1326 | ||
93f22b45 MS |
1327 | p2ps_seeker = p2p->p2ps_prov && p2p->p2ps_prov->pd_seeker; |
1328 | ||
66f1f751 JM |
1329 | /* |
1330 | * Use a local copy of the requested config methods since | |
1331 | * p2p_reset_pending_pd() can clear this in the peer entry. | |
1332 | */ | |
1333 | req_config_methods = dev->req_config_methods; | |
1334 | ||
6b56cc2d JS |
1335 | /* |
1336 | * If the response is from the peer to whom a user initiated request | |
1337 | * was sent earlier, we reset that state info here. | |
1338 | */ | |
1339 | if (p2p->user_initiated_pd && | |
1340 | os_memcmp(p2p->pending_pd_devaddr, sa, ETH_ALEN) == 0) | |
1341 | p2p_reset_pending_pd(p2p); | |
1342 | ||
66f1f751 | 1343 | if (msg.wps_config_methods != req_config_methods) { |
ed496f13 | 1344 | p2p_dbg(p2p, "Peer rejected our Provision Discovery Request (received config_methods 0x%x expected 0x%x", |
66f1f751 | 1345 | msg.wps_config_methods, req_config_methods); |
349b213c JS |
1346 | if (p2p->cfg->prov_disc_fail) |
1347 | p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa, | |
ab8ee776 | 1348 | P2P_PROV_DISC_REJECTED, |
9e96e464 | 1349 | adv_id, adv_mac, NULL); |
b22128ef | 1350 | p2p_parse_free(&msg); |
3f048aa8 | 1351 | p2ps_prov_free(p2p); |
b22128ef JM |
1352 | goto out; |
1353 | } | |
1354 | ||
66f1f751 | 1355 | report_config_methods = req_config_methods; |
b22128ef | 1356 | dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY | |
9e96e464 KV |
1357 | P2P_DEV_PD_PEER_KEYPAD | |
1358 | P2P_DEV_PD_PEER_P2PS); | |
66f1f751 | 1359 | if (req_config_methods & WPS_CONFIG_DISPLAY) { |
ed496f13 | 1360 | p2p_dbg(p2p, "Peer " MACSTR |
b22128ef JM |
1361 | " accepted to show a PIN on display", MAC2STR(sa)); |
1362 | dev->flags |= P2P_DEV_PD_PEER_DISPLAY; | |
9e96e464 | 1363 | passwd_id = DEV_PW_REGISTRAR_SPECIFIED; |
b22128ef | 1364 | } else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) { |
ed496f13 | 1365 | p2p_dbg(p2p, "Peer " MACSTR |
b22128ef JM |
1366 | " accepted to write our PIN using keypad", |
1367 | MAC2STR(sa)); | |
1368 | dev->flags |= P2P_DEV_PD_PEER_KEYPAD; | |
9e96e464 KV |
1369 | passwd_id = DEV_PW_USER_SPECIFIED; |
1370 | } else if (msg.wps_config_methods & WPS_CONFIG_P2PS) { | |
1371 | p2p_dbg(p2p, "Peer " MACSTR " accepted P2PS PIN", | |
1372 | MAC2STR(sa)); | |
1373 | dev->flags |= P2P_DEV_PD_PEER_P2PS; | |
1374 | passwd_id = DEV_PW_P2PS_DEFAULT; | |
1375 | } | |
1376 | ||
685b2098 | 1377 | if ((status == P2P_SC_SUCCESS || status == P2P_SC_SUCCESS_DEFERRED) && |
9e96e464 | 1378 | p2p->p2ps_prov) { |
685b2098 IP |
1379 | dev->oper_freq = 0; |
1380 | ||
1381 | /* | |
1382 | * Save the reported channel list and operating frequency. | |
1383 | * Note that the specification mandates that the responder | |
1384 | * should include in the channel list only channels reported by | |
1385 | * the initiator, so this is only a sanity check, and if this | |
1386 | * fails the flow would continue, although it would probably | |
1387 | * fail. Same is true for the operating channel. | |
1388 | */ | |
1389 | if (msg.channel_list && msg.channel_list_len && | |
1390 | p2p_peer_channels_check(p2p, &p2p->channels, dev, | |
1391 | msg.channel_list, | |
1392 | msg.channel_list_len) < 0) | |
1393 | p2p_dbg(p2p, "P2PS PD Response - no common channels"); | |
1394 | ||
1395 | if (msg.operating_channel) { | |
1396 | if (p2p_channels_includes(&p2p->channels, | |
1397 | msg.operating_channel[3], | |
1398 | msg.operating_channel[4]) && | |
1399 | p2p_channels_includes(&dev->channels, | |
1400 | msg.operating_channel[3], | |
1401 | msg.operating_channel[4])) { | |
1402 | dev->oper_freq = | |
1403 | p2p_channel_to_freq( | |
1404 | msg.operating_channel[3], | |
1405 | msg.operating_channel[4]); | |
1406 | } else { | |
1407 | p2p_dbg(p2p, | |
1408 | "P2PS PD Response - invalid operating channel"); | |
1409 | } | |
1410 | } | |
1411 | ||
9e96e464 | 1412 | if (p2p->cfg->p2ps_prov_complete) { |
8bb8e6ed IP |
1413 | int freq = 0; |
1414 | ||
685b2098 IP |
1415 | if (conncap == P2PS_SETUP_GROUP_OWNER) { |
1416 | u8 tmp; | |
1417 | ||
1418 | /* | |
1419 | * Re-select the operating channel as it is | |
1420 | * possible that original channel is no longer | |
1421 | * valid. This should not really fail. | |
1422 | */ | |
1423 | if (p2p_go_select_channel(p2p, dev, &tmp) < 0) | |
1424 | p2p_dbg(p2p, | |
1425 | "P2PS PD channel selection failed"); | |
8bb8e6ed IP |
1426 | |
1427 | freq = p2p_channel_to_freq(p2p->op_reg_class, | |
1428 | p2p->op_channel); | |
1429 | if (freq < 0) | |
1430 | freq = 0; | |
685b2098 IP |
1431 | } |
1432 | ||
9e96e464 KV |
1433 | p2p->cfg->p2ps_prov_complete( |
1434 | p2p->cfg->cb_ctx, status, sa, adv_mac, | |
1435 | p2p->p2ps_prov->session_mac, | |
1436 | group_mac, adv_id, p2p->p2ps_prov->session_id, | |
1437 | conncap, passwd_id, msg.persistent_ssid, | |
88f3d7c9 | 1438 | msg.persistent_ssid_len, 1, 0, NULL, |
8bb8e6ed | 1439 | msg.feature_cap, msg.feature_cap_len, freq); |
9e96e464 | 1440 | } |
3f048aa8 | 1441 | p2ps_prov_free(p2p); |
ea210b9f MS |
1442 | } else if (status != P2P_SC_SUCCESS && |
1443 | status != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE && | |
1444 | status != P2P_SC_SUCCESS_DEFERRED && p2p->p2ps_prov) { | |
9e96e464 KV |
1445 | if (p2p->cfg->p2ps_prov_complete) |
1446 | p2p->cfg->p2ps_prov_complete( | |
1447 | p2p->cfg->cb_ctx, status, sa, adv_mac, | |
1448 | p2p->p2ps_prov->session_mac, | |
1449 | group_mac, adv_id, p2p->p2ps_prov->session_id, | |
8bb8e6ed | 1450 | 0, 0, NULL, 0, 1, 0, NULL, NULL, 0, 0); |
3f048aa8 | 1451 | p2ps_prov_free(p2p); |
9e96e464 KV |
1452 | } |
1453 | ||
1454 | if (status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) { | |
1455 | if (p2p->cfg->remove_stale_groups) { | |
1456 | p2p->cfg->remove_stale_groups(p2p->cfg->cb_ctx, | |
1457 | dev->info.p2p_device_addr, | |
1458 | NULL, NULL, 0); | |
1459 | } | |
1460 | ||
1461 | if (msg.session_info && msg.session_info_len) { | |
1462 | size_t info_len = msg.session_info_len; | |
1463 | char *deferred_sess_resp = os_malloc(2 * info_len + 1); | |
1464 | ||
1465 | if (!deferred_sess_resp) { | |
1466 | p2p_parse_free(&msg); | |
3f048aa8 | 1467 | p2ps_prov_free(p2p); |
9e96e464 KV |
1468 | goto out; |
1469 | } | |
1470 | utf8_escape((char *) msg.session_info, info_len, | |
1471 | deferred_sess_resp, 2 * info_len + 1); | |
1472 | ||
1473 | if (p2p->cfg->prov_disc_fail) | |
1474 | p2p->cfg->prov_disc_fail( | |
1475 | p2p->cfg->cb_ctx, sa, | |
1476 | P2P_PROV_DISC_INFO_UNAVAILABLE, | |
1477 | adv_id, adv_mac, | |
1478 | deferred_sess_resp); | |
1479 | os_free(deferred_sess_resp); | |
1480 | } else | |
1481 | if (p2p->cfg->prov_disc_fail) | |
1482 | p2p->cfg->prov_disc_fail( | |
1483 | p2p->cfg->cb_ctx, sa, | |
1484 | P2P_PROV_DISC_INFO_UNAVAILABLE, | |
1485 | adv_id, adv_mac, NULL); | |
20f4c3d7 | 1486 | } else if (status != P2P_SC_SUCCESS) { |
9e96e464 KV |
1487 | p2p_dbg(p2p, "Peer rejected our Provision Discovery Request"); |
1488 | if (p2p->cfg->prov_disc_fail) | |
1489 | p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa, | |
82d61135 MS |
1490 | P2P_PROV_DISC_REJECTED, |
1491 | adv_id, adv_mac, NULL); | |
9e96e464 | 1492 | p2p_parse_free(&msg); |
3f048aa8 | 1493 | p2ps_prov_free(p2p); |
9e96e464 | 1494 | goto out; |
b22128ef | 1495 | } |
ec437d9e JJ |
1496 | |
1497 | /* Store the provisioning info */ | |
1498 | dev->wps_prov_info = msg.wps_config_methods; | |
b843a8b8 AO |
1499 | if (msg.intended_addr) |
1500 | os_memcpy(dev->interface_addr, msg.intended_addr, ETH_ALEN); | |
ec437d9e | 1501 | |
b22128ef JM |
1502 | p2p_parse_free(&msg); |
1503 | ||
1504 | out: | |
1505 | dev->req_config_methods = 0; | |
1506 | p2p->cfg->send_action_done(p2p->cfg->cb_ctx); | |
3bc462cb | 1507 | if (dev->flags & P2P_DEV_PD_BEFORE_GO_NEG) { |
ed496f13 JM |
1508 | p2p_dbg(p2p, "Start GO Neg after the PD-before-GO-Neg workaround with " |
1509 | MACSTR, MAC2STR(dev->info.p2p_device_addr)); | |
3bc462cb JM |
1510 | dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG; |
1511 | p2p_connect_send(p2p, dev); | |
1512 | return; | |
1513 | } | |
93f22b45 MS |
1514 | |
1515 | /* | |
1516 | * prov_disc_resp callback is used to generate P2P-PROV-DISC-ENTER-PIN, | |
1517 | * P2P-PROV-DISC-SHOW-PIN, and P2P-PROV-DISC-PBC-REQ events. | |
1518 | * Call it only for a legacy P2P PD or for P2PS PD scenarios where | |
1519 | * show/enter PIN events are needed. | |
1520 | * | |
1521 | * The callback is called in the following cases: | |
1522 | * 1. Legacy P2P PD response with a status SUCCESS | |
1523 | * 2. P2PS, advertiser method: DISPLAY, autoaccept: true, | |
1524 | * response status: SUCCESS, local method KEYPAD | |
1525 | * 3. P2PS, advertiser method: KEYPAD,Seeker side, | |
1526 | * response status: INFO_CURRENTLY_UNAVAILABLE, | |
1527 | * local method: DISPLAY | |
1528 | */ | |
1529 | if (p2p->cfg->prov_disc_resp && | |
1530 | ((status == P2P_SC_SUCCESS && !adv_id) || | |
1531 | (p2ps_seeker && status == P2P_SC_SUCCESS && | |
1532 | passwd_id == DEV_PW_REGISTRAR_SPECIFIED) || | |
1533 | (p2ps_seeker && | |
1534 | status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE && | |
1535 | passwd_id == DEV_PW_USER_SPECIFIED))) | |
b22128ef JM |
1536 | p2p->cfg->prov_disc_resp(p2p->cfg->cb_ctx, sa, |
1537 | report_config_methods); | |
52728dcd JM |
1538 | |
1539 | if (p2p->state == P2P_PD_DURING_FIND) { | |
1540 | p2p_clear_timeout(p2p); | |
1541 | p2p_continue_find(p2p); | |
1542 | } | |
b22128ef JM |
1543 | } |
1544 | ||
1545 | ||
1546 | int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev, | |
1ef2f7ff | 1547 | int join, int force_freq) |
b22128ef JM |
1548 | { |
1549 | struct wpabuf *req; | |
1550 | int freq; | |
1551 | ||
1ef2f7ff JM |
1552 | if (force_freq > 0) |
1553 | freq = force_freq; | |
1554 | else | |
1555 | freq = dev->listen_freq > 0 ? dev->listen_freq : | |
1556 | dev->oper_freq; | |
b22128ef | 1557 | if (freq <= 0) { |
ed496f13 JM |
1558 | p2p_dbg(p2p, "No Listen/Operating frequency known for the peer " |
1559 | MACSTR " to send Provision Discovery Request", | |
c5db8e51 | 1560 | MAC2STR(dev->info.p2p_device_addr)); |
b22128ef JM |
1561 | return -1; |
1562 | } | |
1563 | ||
1564 | if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) { | |
c5db8e51 KRK |
1565 | if (!(dev->info.dev_capab & |
1566 | P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) { | |
ed496f13 | 1567 | p2p_dbg(p2p, "Cannot use PD with P2P Device " MACSTR |
b22128ef | 1568 | " that is in a group and is not discoverable", |
c5db8e51 | 1569 | MAC2STR(dev->info.p2p_device_addr)); |
b22128ef JM |
1570 | return -1; |
1571 | } | |
1572 | /* TODO: use device discoverability request through GO */ | |
1573 | } | |
1574 | ||
369678ad KV |
1575 | if (p2p->p2ps_prov) { |
1576 | if (p2p->p2ps_prov->status == P2P_SC_SUCCESS_DEFERRED) { | |
1577 | if (p2p->p2ps_prov->method == WPS_CONFIG_DISPLAY) | |
1578 | dev->req_config_methods = WPS_CONFIG_KEYPAD; | |
1579 | else if (p2p->p2ps_prov->method == WPS_CONFIG_KEYPAD) | |
1580 | dev->req_config_methods = WPS_CONFIG_DISPLAY; | |
1581 | else | |
1582 | dev->req_config_methods = WPS_CONFIG_P2PS; | |
1583 | } else { | |
1584 | /* Order of preference, based on peer's capabilities */ | |
1585 | if (p2p->p2ps_prov->method) | |
1586 | dev->req_config_methods = | |
1587 | p2p->p2ps_prov->method; | |
1588 | else if (dev->info.config_methods & WPS_CONFIG_P2PS) | |
1589 | dev->req_config_methods = WPS_CONFIG_P2PS; | |
1590 | else if (dev->info.config_methods & WPS_CONFIG_DISPLAY) | |
1591 | dev->req_config_methods = WPS_CONFIG_DISPLAY; | |
1592 | else | |
1593 | dev->req_config_methods = WPS_CONFIG_KEYPAD; | |
1594 | } | |
1595 | p2p_dbg(p2p, | |
1596 | "Building PD Request based on P2PS config method 0x%x status %d --> req_config_methods 0x%x", | |
1597 | p2p->p2ps_prov->method, p2p->p2ps_prov->status, | |
1598 | dev->req_config_methods); | |
ebd32943 IP |
1599 | |
1600 | if (p2p_prepare_channel(p2p, dev, p2p->p2ps_prov->force_freq, | |
1601 | p2p->p2ps_prov->pref_freq, 1) < 0) | |
1602 | return -1; | |
369678ad KV |
1603 | } |
1604 | ||
1605 | req = p2p_build_prov_disc_req(p2p, dev, join); | |
b22128ef JM |
1606 | if (req == NULL) |
1607 | return -1; | |
1608 | ||
1a9c618d JM |
1609 | if (p2p->state != P2P_IDLE) |
1610 | p2p_stop_listen_for_freq(p2p, freq); | |
b22128ef | 1611 | p2p->pending_action_state = P2P_PENDING_PD; |
c5db8e51 KRK |
1612 | if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr, |
1613 | p2p->cfg->dev_addr, dev->info.p2p_device_addr, | |
3f9285ff | 1614 | wpabuf_head(req), wpabuf_len(req), 200) < 0) { |
ed496f13 | 1615 | p2p_dbg(p2p, "Failed to send Action frame"); |
b22128ef JM |
1616 | wpabuf_free(req); |
1617 | return -1; | |
1618 | } | |
1619 | ||
6b56cc2d JS |
1620 | os_memcpy(p2p->pending_pd_devaddr, dev->info.p2p_device_addr, ETH_ALEN); |
1621 | ||
b22128ef JM |
1622 | wpabuf_free(req); |
1623 | return 0; | |
1624 | } | |
1625 | ||
1626 | ||
1627 | int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr, | |
369678ad | 1628 | struct p2ps_provision *p2ps_prov, |
67527166 SD |
1629 | u16 config_methods, int join, int force_freq, |
1630 | int user_initiated_pd) | |
b22128ef JM |
1631 | { |
1632 | struct p2p_device *dev; | |
1633 | ||
1634 | dev = p2p_get_device(p2p, peer_addr); | |
1635 | if (dev == NULL) | |
1636 | dev = p2p_get_device_interface(p2p, peer_addr); | |
1637 | if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { | |
ed496f13 | 1638 | p2p_dbg(p2p, "Provision Discovery Request destination " MACSTR |
b22128ef | 1639 | " not yet known", MAC2STR(peer_addr)); |
369678ad | 1640 | os_free(p2ps_prov); |
b22128ef JM |
1641 | return -1; |
1642 | } | |
1643 | ||
ed496f13 JM |
1644 | p2p_dbg(p2p, "Provision Discovery Request with " MACSTR |
1645 | " (config methods 0x%x)", | |
b22128ef | 1646 | MAC2STR(peer_addr), config_methods); |
369678ad KV |
1647 | if (config_methods == 0 && !p2ps_prov) { |
1648 | os_free(p2ps_prov); | |
b22128ef | 1649 | return -1; |
369678ad KV |
1650 | } |
1651 | ||
1652 | if (p2ps_prov && p2ps_prov->status == P2P_SC_SUCCESS_DEFERRED && | |
1653 | p2p->p2ps_prov) { | |
1654 | /* Use cached method from deferred provisioning */ | |
1655 | p2ps_prov->method = p2p->p2ps_prov->method; | |
1656 | } | |
b22128ef | 1657 | |
ec437d9e JJ |
1658 | /* Reset provisioning info */ |
1659 | dev->wps_prov_info = 0; | |
3f048aa8 | 1660 | p2ps_prov_free(p2p); |
369678ad | 1661 | p2p->p2ps_prov = p2ps_prov; |
ec437d9e | 1662 | |
b22128ef | 1663 | dev->req_config_methods = config_methods; |
10c4edde JM |
1664 | if (join) |
1665 | dev->flags |= P2P_DEV_PD_FOR_JOIN; | |
1666 | else | |
1667 | dev->flags &= ~P2P_DEV_PD_FOR_JOIN; | |
b22128ef | 1668 | |
e12b85d3 JB |
1669 | if (p2p->state != P2P_IDLE && p2p->state != P2P_SEARCH && |
1670 | p2p->state != P2P_LISTEN_ONLY) { | |
ed496f13 JM |
1671 | p2p_dbg(p2p, "Busy with other operations; postpone Provision Discovery Request with " |
1672 | MACSTR " (config methods 0x%x)", | |
b22128ef JM |
1673 | MAC2STR(peer_addr), config_methods); |
1674 | return 0; | |
1675 | } | |
1676 | ||
67527166 | 1677 | p2p->user_initiated_pd = user_initiated_pd; |
8d82c210 | 1678 | p2p->pd_force_freq = force_freq; |
6b56cc2d | 1679 | |
488f4a71 | 1680 | if (p2p->user_initiated_pd) |
6b56cc2d JS |
1681 | p2p->pd_retries = MAX_PROV_DISC_REQ_RETRIES; |
1682 | ||
c0810ddb SD |
1683 | /* |
1684 | * Assign dialog token here to use the same value in each retry within | |
1685 | * the same PD exchange. | |
1686 | */ | |
1687 | dev->dialog_token++; | |
1688 | if (dev->dialog_token == 0) | |
1689 | dev->dialog_token = 1; | |
1690 | ||
1ef2f7ff | 1691 | return p2p_send_prov_disc_req(p2p, dev, join, force_freq); |
b22128ef | 1692 | } |
6b56cc2d JS |
1693 | |
1694 | ||
1695 | void p2p_reset_pending_pd(struct p2p_data *p2p) | |
1696 | { | |
5aff6fc6 JMB |
1697 | struct p2p_device *dev; |
1698 | ||
1699 | dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { | |
1700 | if (os_memcmp(p2p->pending_pd_devaddr, | |
1701 | dev->info.p2p_device_addr, ETH_ALEN)) | |
1702 | continue; | |
1703 | if (!dev->req_config_methods) | |
1704 | continue; | |
1705 | if (dev->flags & P2P_DEV_PD_FOR_JOIN) | |
1706 | continue; | |
1707 | /* Reset the config methods of the device */ | |
1708 | dev->req_config_methods = 0; | |
1709 | } | |
1710 | ||
6b56cc2d JS |
1711 | p2p->user_initiated_pd = 0; |
1712 | os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN); | |
1713 | p2p->pd_retries = 0; | |
8d82c210 | 1714 | p2p->pd_force_freq = 0; |
6b56cc2d | 1715 | } |
3f048aa8 MS |
1716 | |
1717 | ||
1718 | void p2ps_prov_free(struct p2p_data *p2p) | |
1719 | { | |
1720 | os_free(p2p->p2ps_prov); | |
1721 | p2p->p2ps_prov = NULL; | |
1722 | } |