eap_server_wsc.c revision 87fd279308af3f806848c8f2ab65ef18c6ac4c30
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
25struct 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
39static 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
61static 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
70static 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
85static 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
160static 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
171static 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
192static 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
248static 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
279static 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
296static 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
323static 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
354static 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
471static 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
478static 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
485static 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
494int 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}
519