]> git.ipfire.org Git - thirdparty/hostap.git/blob - src/eap_server/eap_server_wsc.c
WPS: Add a workaround for Windows 7 capability discovery for PBC
[thirdparty/hostap.git] / src / eap_server / eap_server_wsc.c
1 /*
2 * EAP-WSC server for Wi-Fi Protected Setup
3 * Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *
9 * Alternatively, this software may be distributed under the terms of BSD
10 * license.
11 *
12 * See README and COPYING for more details.
13 */
14
15 #include "includes.h"
16
17 #include "common.h"
18 #include "eloop.h"
19 #include "eap_i.h"
20 #include "eap_common/eap_wsc_common.h"
21 #include "p2p/p2p.h"
22 #include "wps/wps.h"
23
24
25 struct eap_wsc_data {
26 enum { START, MESG, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state;
27 int registrar;
28 struct wpabuf *in_buf;
29 struct wpabuf *out_buf;
30 enum wsc_op_code in_op_code, out_op_code;
31 size_t out_used;
32 size_t fragment_size;
33 struct wps_data *wps;
34 int ext_reg_timeout;
35 };
36
37
38 #ifndef CONFIG_NO_STDOUT_DEBUG
39 static const char * eap_wsc_state_txt(int state)
40 {
41 switch (state) {
42 case START:
43 return "START";
44 case MESG:
45 return "MESG";
46 case FRAG_ACK:
47 return "FRAG_ACK";
48 case WAIT_FRAG_ACK:
49 return "WAIT_FRAG_ACK";
50 case DONE:
51 return "DONE";
52 case FAIL:
53 return "FAIL";
54 default:
55 return "?";
56 }
57 }
58 #endif /* CONFIG_NO_STDOUT_DEBUG */
59
60
61 static void eap_wsc_state(struct eap_wsc_data *data, int state)
62 {
63 wpa_printf(MSG_DEBUG, "EAP-WSC: %s -> %s",
64 eap_wsc_state_txt(data->state),
65 eap_wsc_state_txt(state));
66 data->state = state;
67 }
68
69
70 static void eap_wsc_ext_reg_timeout(void *eloop_ctx, void *timeout_ctx)
71 {
72 struct eap_sm *sm = eloop_ctx;
73 struct eap_wsc_data *data = timeout_ctx;
74
75 if (sm->method_pending != METHOD_PENDING_WAIT)
76 return;
77
78 wpa_printf(MSG_DEBUG, "EAP-WSC: Timeout while waiting for an External "
79 "Registrar");
80 data->ext_reg_timeout = 1;
81 eap_sm_pending_cb(sm);
82 }
83
84
85 static void * eap_wsc_init(struct eap_sm *sm)
86 {
87 struct eap_wsc_data *data;
88 int registrar;
89 struct wps_config cfg;
90
91 if (sm->identity && sm->identity_len == WSC_ID_REGISTRAR_LEN &&
92 os_memcmp(sm->identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) ==
93 0)
94 registrar = 0; /* Supplicant is Registrar */
95 else if (sm->identity && sm->identity_len == WSC_ID_ENROLLEE_LEN &&
96 os_memcmp(sm->identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN)
97 == 0)
98 registrar = 1; /* Supplicant is Enrollee */
99 else {
100 wpa_hexdump_ascii(MSG_INFO, "EAP-WSC: Unexpected identity",
101 sm->identity, sm->identity_len);
102 return NULL;
103 }
104
105 data = os_zalloc(sizeof(*data));
106 if (data == NULL)
107 return NULL;
108 data->state = registrar ? START : MESG;
109 data->registrar = registrar;
110
111 os_memset(&cfg, 0, sizeof(cfg));
112 cfg.wps = sm->wps;
113 cfg.registrar = registrar;
114 if (registrar) {
115 if (sm->wps == NULL || sm->wps->registrar == NULL) {
116 wpa_printf(MSG_INFO, "EAP-WSC: WPS Registrar not "
117 "initialized");
118 os_free(data);
119 return NULL;
120 }
121 } else {
122 if (sm->user == NULL || sm->user->password == NULL) {
123 /*
124 * In theory, this should not really be needed, but
125 * Windows 7 uses Registrar mode to probe AP's WPS
126 * capabilities before trying to use Enrollee and fails
127 * if the AP does not allow that probing to happen..
128 */
129 wpa_printf(MSG_DEBUG, "EAP-WSC: No AP PIN (password) "
130 "configured for Enrollee functionality - "
131 "allow for probing capabilities (M1)");
132 } else {
133 cfg.pin = sm->user->password;
134 cfg.pin_len = sm->user->password_len;
135 }
136 }
137 cfg.assoc_wps_ie = sm->assoc_wps_ie;
138 cfg.peer_addr = sm->peer_addr;
139 #ifdef CONFIG_P2P
140 if (sm->assoc_p2p_ie) {
141 wpa_printf(MSG_DEBUG, "EAP-WSC: Prefer PSK format for P2P "
142 "client");
143 cfg.use_psk_key = 1;
144 cfg.p2p_dev_addr = p2p_get_go_dev_addr(sm->assoc_p2p_ie);
145 }
146 #endif /* CONFIG_P2P */
147 cfg.pbc_in_m1 = sm->pbc_in_m1;
148 data->wps = wps_init(&cfg);
149 if (data->wps == NULL) {
150 os_free(data);
151 return NULL;
152 }
153 data->fragment_size = sm->fragment_size > 0 ? sm->fragment_size :
154 WSC_FRAGMENT_SIZE;
155
156 return data;
157 }
158
159
160 static void eap_wsc_reset(struct eap_sm *sm, void *priv)
161 {
162 struct eap_wsc_data *data = priv;
163 eloop_cancel_timeout(eap_wsc_ext_reg_timeout, sm, data);
164 wpabuf_free(data->in_buf);
165 wpabuf_free(data->out_buf);
166 wps_deinit(data->wps);
167 os_free(data);
168 }
169
170
171 static struct wpabuf * eap_wsc_build_start(struct eap_sm *sm,
172 struct eap_wsc_data *data, u8 id)
173 {
174 struct wpabuf *req;
175
176 req = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, 2,
177 EAP_CODE_REQUEST, id);
178 if (req == NULL) {
179 wpa_printf(MSG_ERROR, "EAP-WSC: Failed to allocate memory for "
180 "request");
181 return NULL;
182 }
183
184 wpa_printf(MSG_DEBUG, "EAP-WSC: Send WSC/Start");
185 wpabuf_put_u8(req, WSC_Start); /* Op-Code */
186 wpabuf_put_u8(req, 0); /* Flags */
187
188 return req;
189 }
190
191
192 static struct wpabuf * eap_wsc_build_msg(struct eap_wsc_data *data, u8 id)
193 {
194 struct wpabuf *req;
195 u8 flags;
196 size_t send_len, plen;
197
198 flags = 0;
199 send_len = wpabuf_len(data->out_buf) - data->out_used;
200 if (2 + send_len > data->fragment_size) {
201 send_len = data->fragment_size - 2;
202 flags |= WSC_FLAGS_MF;
203 if (data->out_used == 0) {
204 flags |= WSC_FLAGS_LF;
205 send_len -= 2;
206 }
207 }
208 plen = 2 + send_len;
209 if (flags & WSC_FLAGS_LF)
210 plen += 2;
211 req = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, plen,
212 EAP_CODE_REQUEST, id);
213 if (req == NULL) {
214 wpa_printf(MSG_ERROR, "EAP-WSC: Failed to allocate memory for "
215 "request");
216 return NULL;
217 }
218
219 wpabuf_put_u8(req, data->out_op_code); /* Op-Code */
220 wpabuf_put_u8(req, flags); /* Flags */
221 if (flags & WSC_FLAGS_LF)
222 wpabuf_put_be16(req, wpabuf_len(data->out_buf));
223
224 wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used,
225 send_len);
226 data->out_used += send_len;
227
228 if (data->out_used == wpabuf_len(data->out_buf)) {
229 wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
230 "(message sent completely)",
231 (unsigned long) send_len);
232 wpabuf_free(data->out_buf);
233 data->out_buf = NULL;
234 data->out_used = 0;
235 eap_wsc_state(data, MESG);
236 } else {
237 wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
238 "(%lu more to send)", (unsigned long) send_len,
239 (unsigned long) wpabuf_len(data->out_buf) -
240 data->out_used);
241 eap_wsc_state(data, WAIT_FRAG_ACK);
242 }
243
244 return req;
245 }
246
247
248 static struct wpabuf * eap_wsc_buildReq(struct eap_sm *sm, void *priv, u8 id)
249 {
250 struct eap_wsc_data *data = priv;
251
252 switch (data->state) {
253 case START:
254 return eap_wsc_build_start(sm, data, id);
255 case MESG:
256 if (data->out_buf == NULL) {
257 data->out_buf = wps_get_msg(data->wps,
258 &data->out_op_code);
259 if (data->out_buf == NULL) {
260 wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to "
261 "receive message from WPS");
262 return NULL;
263 }
264 data->out_used = 0;
265 }
266 /* pass through */
267 case WAIT_FRAG_ACK:
268 return eap_wsc_build_msg(data, id);
269 case FRAG_ACK:
270 return eap_wsc_build_frag_ack(id, EAP_CODE_REQUEST);
271 default:
272 wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected state %d in "
273 "buildReq", data->state);
274 return NULL;
275 }
276 }
277
278
279 static Boolean eap_wsc_check(struct eap_sm *sm, void *priv,
280 struct wpabuf *respData)
281 {
282 const u8 *pos;
283 size_t len;
284
285 pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC,
286 respData, &len);
287 if (pos == NULL || len < 2) {
288 wpa_printf(MSG_INFO, "EAP-WSC: Invalid frame");
289 return TRUE;
290 }
291
292 return FALSE;
293 }
294
295
296 static int eap_wsc_process_cont(struct eap_wsc_data *data,
297 const u8 *buf, size_t len, u8 op_code)
298 {
299 /* Process continuation of a pending message */
300 if (op_code != data->in_op_code) {
301 wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d in "
302 "fragment (expected %d)",
303 op_code, data->in_op_code);
304 eap_wsc_state(data, FAIL);
305 return -1;
306 }
307
308 if (len > wpabuf_tailroom(data->in_buf)) {
309 wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment overflow");
310 eap_wsc_state(data, FAIL);
311 return -1;
312 }
313
314 wpabuf_put_data(data->in_buf, buf, len);
315 wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes, waiting for %lu "
316 "bytes more", (unsigned long) len,
317 (unsigned long) wpabuf_tailroom(data->in_buf));
318
319 return 0;
320 }
321
322
323 static int eap_wsc_process_fragment(struct eap_wsc_data *data,
324 u8 flags, u8 op_code, u16 message_length,
325 const u8 *buf, size_t len)
326 {
327 /* Process a fragment that is not the last one of the message */
328 if (data->in_buf == NULL && !(flags & WSC_FLAGS_LF)) {
329 wpa_printf(MSG_DEBUG, "EAP-WSC: No Message Length "
330 "field in a fragmented packet");
331 return -1;
332 }
333
334 if (data->in_buf == NULL) {
335 /* First fragment of the message */
336 data->in_buf = wpabuf_alloc(message_length);
337 if (data->in_buf == NULL) {
338 wpa_printf(MSG_DEBUG, "EAP-WSC: No memory for "
339 "message");
340 return -1;
341 }
342 data->in_op_code = op_code;
343 wpabuf_put_data(data->in_buf, buf, len);
344 wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes in "
345 "first fragment, waiting for %lu bytes more",
346 (unsigned long) len,
347 (unsigned long) wpabuf_tailroom(data->in_buf));
348 }
349
350 return 0;
351 }
352
353
354 static void eap_wsc_process(struct eap_sm *sm, void *priv,
355 struct wpabuf *respData)
356 {
357 struct eap_wsc_data *data = priv;
358 const u8 *start, *pos, *end;
359 size_t len;
360 u8 op_code, flags;
361 u16 message_length = 0;
362 enum wps_process_res res;
363 struct wpabuf tmpbuf;
364
365 eloop_cancel_timeout(eap_wsc_ext_reg_timeout, sm, data);
366 if (data->ext_reg_timeout) {
367 eap_wsc_state(data, FAIL);
368 return;
369 }
370
371 pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC,
372 respData, &len);
373 if (pos == NULL || len < 2)
374 return; /* Should not happen; message already verified */
375
376 start = pos;
377 end = start + len;
378
379 op_code = *pos++;
380 flags = *pos++;
381 if (flags & WSC_FLAGS_LF) {
382 if (end - pos < 2) {
383 wpa_printf(MSG_DEBUG, "EAP-WSC: Message underflow");
384 return;
385 }
386 message_length = WPA_GET_BE16(pos);
387 pos += 2;
388
389 if (message_length < end - pos) {
390 wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message "
391 "Length");
392 return;
393 }
394 }
395
396 wpa_printf(MSG_DEBUG, "EAP-WSC: Received packet: Op-Code %d "
397 "Flags 0x%x Message Length %d",
398 op_code, flags, message_length);
399
400 if (data->state == WAIT_FRAG_ACK) {
401 if (op_code != WSC_FRAG_ACK) {
402 wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d "
403 "in WAIT_FRAG_ACK state", op_code);
404 eap_wsc_state(data, FAIL);
405 return;
406 }
407 wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment acknowledged");
408 eap_wsc_state(data, MESG);
409 return;
410 }
411
412 if (op_code != WSC_ACK && op_code != WSC_NACK && op_code != WSC_MSG &&
413 op_code != WSC_Done) {
414 wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d",
415 op_code);
416 eap_wsc_state(data, FAIL);
417 return;
418 }
419
420 if (data->in_buf &&
421 eap_wsc_process_cont(data, pos, end - pos, op_code) < 0) {
422 eap_wsc_state(data, FAIL);
423 return;
424 }
425
426 if (flags & WSC_FLAGS_MF) {
427 if (eap_wsc_process_fragment(data, flags, op_code,
428 message_length, pos, end - pos) <
429 0)
430 eap_wsc_state(data, FAIL);
431 else
432 eap_wsc_state(data, FRAG_ACK);
433 return;
434 }
435
436 if (data->in_buf == NULL) {
437 /* Wrap unfragmented messages as wpabuf without extra copy */
438 wpabuf_set(&tmpbuf, pos, end - pos);
439 data->in_buf = &tmpbuf;
440 }
441
442 res = wps_process_msg(data->wps, op_code, data->in_buf);
443 switch (res) {
444 case WPS_DONE:
445 wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing completed "
446 "successfully - report EAP failure");
447 eap_wsc_state(data, FAIL);
448 break;
449 case WPS_CONTINUE:
450 eap_wsc_state(data, MESG);
451 break;
452 case WPS_FAILURE:
453 wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing failed");
454 eap_wsc_state(data, FAIL);
455 break;
456 case WPS_PENDING:
457 eap_wsc_state(data, MESG);
458 sm->method_pending = METHOD_PENDING_WAIT;
459 eloop_cancel_timeout(eap_wsc_ext_reg_timeout, sm, data);
460 eloop_register_timeout(5, 0, eap_wsc_ext_reg_timeout,
461 sm, data);
462 break;
463 }
464
465 if (data->in_buf != &tmpbuf)
466 wpabuf_free(data->in_buf);
467 data->in_buf = NULL;
468 }
469
470
471 static Boolean eap_wsc_isDone(struct eap_sm *sm, void *priv)
472 {
473 struct eap_wsc_data *data = priv;
474 return data->state == FAIL;
475 }
476
477
478 static Boolean eap_wsc_isSuccess(struct eap_sm *sm, void *priv)
479 {
480 /* EAP-WSC will always result in EAP-Failure */
481 return FALSE;
482 }
483
484
485 static int eap_wsc_getTimeout(struct eap_sm *sm, void *priv)
486 {
487 /* Recommended retransmit times: retransmit timeout 5 seconds,
488 * per-message timeout 15 seconds, i.e., 3 tries. */
489 sm->MaxRetrans = 2; /* total 3 attempts */
490 return 5;
491 }
492
493
494 int eap_server_wsc_register(void)
495 {
496 struct eap_method *eap;
497 int ret;
498
499 eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
500 EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC,
501 "WSC");
502 if (eap == NULL)
503 return -1;
504
505 eap->init = eap_wsc_init;
506 eap->reset = eap_wsc_reset;
507 eap->buildReq = eap_wsc_buildReq;
508 eap->check = eap_wsc_check;
509 eap->process = eap_wsc_process;
510 eap->isDone = eap_wsc_isDone;
511 eap->isSuccess = eap_wsc_isSuccess;
512 eap->getTimeout = eap_wsc_getTimeout;
513
514 ret = eap_server_method_register(eap);
515 if (ret)
516 eap_server_method_free(eap);
517 return ret;
518 }