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