eap_wsc.c revision cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50e
1/*
2 * EAP-WSC peer for Wi-Fi Protected Setup
3 * Copyright (c) 2007-2009, 2012, 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 "uuid.h"
13#include "eap_i.h"
14#include "eap_common/eap_wsc_common.h"
15#include "wps/wps.h"
16#include "wps/wps_defs.h"
17
18
19struct eap_wsc_data {
20	enum { WAIT_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	struct wps_context *wps_ctx;
29};
30
31
32static const char * eap_wsc_state_txt(int state)
33{
34	switch (state) {
35	case WAIT_START:
36		return "WAIT_START";
37	case MESG:
38		return "MESG";
39	case FRAG_ACK:
40		return "FRAG_ACK";
41	case WAIT_FRAG_ACK:
42		return "WAIT_FRAG_ACK";
43	case DONE:
44		return "DONE";
45	case FAIL:
46		return "FAIL";
47	default:
48		return "?";
49	}
50}
51
52
53static void eap_wsc_state(struct eap_wsc_data *data, int state)
54{
55	wpa_printf(MSG_DEBUG, "EAP-WSC: %s -> %s",
56		   eap_wsc_state_txt(data->state),
57		   eap_wsc_state_txt(state));
58	data->state = state;
59}
60
61
62static int eap_wsc_new_ap_settings(struct wps_credential *cred,
63				   const char *params)
64{
65	const char *pos, *end;
66	size_t len;
67
68	os_memset(cred, 0, sizeof(*cred));
69
70	pos = os_strstr(params, "new_ssid=");
71	if (pos == NULL)
72		return 0;
73	pos += 9;
74	end = os_strchr(pos, ' ');
75	if (end == NULL)
76		len = os_strlen(pos);
77	else
78		len = end - pos;
79	if ((len & 1) || len > 2 * sizeof(cred->ssid) ||
80	    hexstr2bin(pos, cred->ssid, len / 2)) {
81		wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid new_ssid");
82		return -1;
83	}
84	cred->ssid_len = len / 2;
85
86	pos = os_strstr(params, "new_auth=");
87	if (pos == NULL) {
88		wpa_printf(MSG_DEBUG, "EAP-WSC: Missing new_auth");
89		return -1;
90	}
91	if (os_strncmp(pos + 9, "OPEN", 4) == 0)
92		cred->auth_type = WPS_AUTH_OPEN;
93	else if (os_strncmp(pos + 9, "WPAPSK", 6) == 0)
94		cred->auth_type = WPS_AUTH_WPAPSK;
95	else if (os_strncmp(pos + 9, "WPA2PSK", 7) == 0)
96		cred->auth_type = WPS_AUTH_WPA2PSK;
97	else {
98		wpa_printf(MSG_DEBUG, "EAP-WSC: Unknown new_auth");
99		return -1;
100	}
101
102	pos = os_strstr(params, "new_encr=");
103	if (pos == NULL) {
104		wpa_printf(MSG_DEBUG, "EAP-WSC: Missing new_encr");
105		return -1;
106	}
107	if (os_strncmp(pos + 9, "NONE", 4) == 0)
108		cred->encr_type = WPS_ENCR_NONE;
109	else if (os_strncmp(pos + 9, "WEP", 3) == 0)
110		cred->encr_type = WPS_ENCR_WEP;
111	else if (os_strncmp(pos + 9, "TKIP", 4) == 0)
112		cred->encr_type = WPS_ENCR_TKIP;
113	else if (os_strncmp(pos + 9, "CCMP", 4) == 0)
114		cred->encr_type = WPS_ENCR_AES;
115	else {
116		wpa_printf(MSG_DEBUG, "EAP-WSC: Unknown new_encr");
117		return -1;
118	}
119
120	pos = os_strstr(params, "new_key=");
121	if (pos == NULL)
122		return 0;
123	pos += 8;
124	end = os_strchr(pos, ' ');
125	if (end == NULL)
126		len = os_strlen(pos);
127	else
128		len = end - pos;
129	if ((len & 1) || len > 2 * sizeof(cred->key) ||
130	    hexstr2bin(pos, cred->key, len / 2)) {
131		wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid new_key");
132		return -1;
133	}
134	cred->key_len = len / 2;
135
136	return 1;
137}
138
139
140static void * eap_wsc_init(struct eap_sm *sm)
141{
142	struct eap_wsc_data *data;
143	const u8 *identity;
144	size_t identity_len;
145	int registrar;
146	struct wps_config cfg;
147	const char *pos, *end;
148	const char *phase1;
149	struct wps_context *wps;
150	struct wps_credential new_ap_settings;
151	int res;
152	int nfc = 0;
153	u8 pkhash[WPS_OOB_PUBKEY_HASH_LEN];
154
155	wps = sm->wps;
156	if (wps == NULL) {
157		wpa_printf(MSG_ERROR, "EAP-WSC: WPS context not available");
158		return NULL;
159	}
160
161	identity = eap_get_config_identity(sm, &identity_len);
162
163	if (identity && identity_len == WSC_ID_REGISTRAR_LEN &&
164	    os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0)
165		registrar = 1; /* Supplicant is Registrar */
166	else if (identity && identity_len == WSC_ID_ENROLLEE_LEN &&
167	    os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0)
168		registrar = 0; /* Supplicant is Enrollee */
169	else {
170		wpa_hexdump_ascii(MSG_INFO, "EAP-WSC: Unexpected identity",
171				  identity, identity_len);
172		return NULL;
173	}
174
175	data = os_zalloc(sizeof(*data));
176	if (data == NULL)
177		return NULL;
178	data->state = registrar ? MESG : WAIT_START;
179	data->registrar = registrar;
180	data->wps_ctx = wps;
181
182	os_memset(&cfg, 0, sizeof(cfg));
183	cfg.wps = wps;
184	cfg.registrar = registrar;
185
186	phase1 = eap_get_config_phase1(sm);
187	if (phase1 == NULL) {
188		wpa_printf(MSG_INFO, "EAP-WSC: phase1 configuration data not "
189			   "set");
190		os_free(data);
191		return NULL;
192	}
193
194	pos = os_strstr(phase1, "pin=");
195	if (pos) {
196		pos += 4;
197		cfg.pin = (const u8 *) pos;
198		while (*pos != '\0' && *pos != ' ')
199			pos++;
200		cfg.pin_len = pos - (const char *) cfg.pin;
201		if (cfg.pin_len == 6 &&
202		    os_strncmp((const char *) cfg.pin, "nfc-pw", 6) == 0) {
203			cfg.pin = NULL;
204			cfg.pin_len = 0;
205			nfc = 1;
206		}
207	} else {
208		pos = os_strstr(phase1, "pbc=1");
209		if (pos)
210			cfg.pbc = 1;
211	}
212
213	pos = os_strstr(phase1, "dev_pw_id=");
214	if (pos) {
215		u16 id = atoi(pos + 10);
216		if (id == DEV_PW_NFC_CONNECTION_HANDOVER)
217			nfc = 1;
218		if (cfg.pin || id == DEV_PW_NFC_CONNECTION_HANDOVER)
219			cfg.dev_pw_id = id;
220	}
221
222	if (cfg.pin == NULL && !cfg.pbc && !nfc) {
223		wpa_printf(MSG_INFO, "EAP-WSC: PIN or PBC not set in phase1 "
224			   "configuration data");
225		os_free(data);
226		return NULL;
227	}
228
229	pos = os_strstr(phase1, " pkhash=");
230	if (pos) {
231		size_t len;
232		pos += 8;
233		end = os_strchr(pos, ' ');
234		if (end)
235			len = end - pos;
236		else
237			len = os_strlen(pos);
238		if (len != 2 * WPS_OOB_PUBKEY_HASH_LEN ||
239		    hexstr2bin(pos, pkhash, WPS_OOB_PUBKEY_HASH_LEN)) {
240			wpa_printf(MSG_INFO, "EAP-WSC: Invalid pkhash");
241			os_free(data);
242			return NULL;
243		}
244		cfg.peer_pubkey_hash = pkhash;
245	}
246
247	res = eap_wsc_new_ap_settings(&new_ap_settings, phase1);
248	if (res < 0) {
249		os_free(data);
250		wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to parse new AP "
251			   "settings");
252		return NULL;
253	}
254	if (res == 1) {
255		wpa_printf(MSG_DEBUG, "EAP-WSC: Provide new AP settings for "
256			   "WPS");
257		cfg.new_ap_settings = &new_ap_settings;
258	}
259
260	data->wps = wps_init(&cfg);
261	if (data->wps == NULL) {
262		os_free(data);
263		wpa_printf(MSG_DEBUG, "EAP-WSC: wps_init failed");
264		return NULL;
265	}
266	res = eap_get_config_fragment_size(sm);
267	if (res > 0)
268		data->fragment_size = res;
269	else
270		data->fragment_size = WSC_FRAGMENT_SIZE;
271	wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment size limit %u",
272		   (unsigned int) data->fragment_size);
273
274	if (registrar && cfg.pin) {
275		wps_registrar_add_pin(data->wps_ctx->registrar, NULL, NULL,
276				      cfg.pin, cfg.pin_len, 0);
277	}
278
279	/* Use reduced client timeout for WPS to avoid long wait */
280	if (sm->ClientTimeout > 30)
281		sm->ClientTimeout = 30;
282
283	return data;
284}
285
286
287static void eap_wsc_deinit(struct eap_sm *sm, void *priv)
288{
289	struct eap_wsc_data *data = priv;
290	wpabuf_free(data->in_buf);
291	wpabuf_free(data->out_buf);
292	wps_deinit(data->wps);
293	os_free(data->wps_ctx->network_key);
294	data->wps_ctx->network_key = NULL;
295	os_free(data);
296}
297
298
299static struct wpabuf * eap_wsc_build_msg(struct eap_wsc_data *data,
300					 struct eap_method_ret *ret, u8 id)
301{
302	struct wpabuf *resp;
303	u8 flags;
304	size_t send_len, plen;
305
306	ret->ignore = FALSE;
307	wpa_printf(MSG_DEBUG, "EAP-WSC: Generating Response");
308	ret->allowNotifications = TRUE;
309
310	flags = 0;
311	send_len = wpabuf_len(data->out_buf) - data->out_used;
312	if (2 + send_len > data->fragment_size) {
313		send_len = data->fragment_size - 2;
314		flags |= WSC_FLAGS_MF;
315		if (data->out_used == 0) {
316			flags |= WSC_FLAGS_LF;
317			send_len -= 2;
318		}
319	}
320	plen = 2 + send_len;
321	if (flags & WSC_FLAGS_LF)
322		plen += 2;
323	resp = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, plen,
324			     EAP_CODE_RESPONSE, id);
325	if (resp == NULL)
326		return NULL;
327
328	wpabuf_put_u8(resp, data->out_op_code); /* Op-Code */
329	wpabuf_put_u8(resp, flags); /* Flags */
330	if (flags & WSC_FLAGS_LF)
331		wpabuf_put_be16(resp, wpabuf_len(data->out_buf));
332
333	wpabuf_put_data(resp, wpabuf_head_u8(data->out_buf) + data->out_used,
334			send_len);
335	data->out_used += send_len;
336
337	ret->methodState = METHOD_MAY_CONT;
338	ret->decision = DECISION_FAIL;
339
340	if (data->out_used == wpabuf_len(data->out_buf)) {
341		wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
342			   "(message sent completely)",
343			   (unsigned long) send_len);
344		wpabuf_free(data->out_buf);
345		data->out_buf = NULL;
346		data->out_used = 0;
347		if ((data->state == FAIL && data->out_op_code == WSC_ACK) ||
348		    data->out_op_code == WSC_NACK ||
349		    data->out_op_code == WSC_Done) {
350			eap_wsc_state(data, FAIL);
351			ret->methodState = METHOD_DONE;
352		} else
353			eap_wsc_state(data, MESG);
354	} else {
355		wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
356			   "(%lu more to send)", (unsigned long) send_len,
357			   (unsigned long) wpabuf_len(data->out_buf) -
358			   data->out_used);
359		eap_wsc_state(data, WAIT_FRAG_ACK);
360	}
361
362	return resp;
363}
364
365
366static int eap_wsc_process_cont(struct eap_wsc_data *data,
367				const u8 *buf, size_t len, u8 op_code)
368{
369	/* Process continuation of a pending message */
370	if (op_code != data->in_op_code) {
371		wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d in "
372			   "fragment (expected %d)",
373			   op_code, data->in_op_code);
374		return -1;
375	}
376
377	if (len > wpabuf_tailroom(data->in_buf)) {
378		wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment overflow");
379		eap_wsc_state(data, FAIL);
380		return -1;
381	}
382
383	wpabuf_put_data(data->in_buf, buf, len);
384	wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes, waiting "
385		   "for %lu bytes more", (unsigned long) len,
386		   (unsigned long) wpabuf_tailroom(data->in_buf));
387
388	return 0;
389}
390
391
392static struct wpabuf * eap_wsc_process_fragment(struct eap_wsc_data *data,
393						struct eap_method_ret *ret,
394						u8 id, u8 flags, u8 op_code,
395						u16 message_length,
396						const u8 *buf, size_t len)
397{
398	/* Process a fragment that is not the last one of the message */
399	if (data->in_buf == NULL && !(flags & WSC_FLAGS_LF)) {
400		wpa_printf(MSG_DEBUG, "EAP-WSC: No Message Length field in a "
401			   "fragmented packet");
402		ret->ignore = TRUE;
403		return NULL;
404	}
405
406	if (data->in_buf == NULL) {
407		/* First fragment of the message */
408		data->in_buf = wpabuf_alloc(message_length);
409		if (data->in_buf == NULL) {
410			wpa_printf(MSG_DEBUG, "EAP-WSC: No memory for "
411				   "message");
412			ret->ignore = TRUE;
413			return NULL;
414		}
415		data->in_op_code = op_code;
416		wpabuf_put_data(data->in_buf, buf, len);
417		wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes in first "
418			   "fragment, waiting for %lu bytes more",
419			   (unsigned long) len,
420			   (unsigned long) wpabuf_tailroom(data->in_buf));
421	}
422
423	return eap_wsc_build_frag_ack(id, EAP_CODE_RESPONSE);
424}
425
426
427static struct wpabuf * eap_wsc_process(struct eap_sm *sm, void *priv,
428				       struct eap_method_ret *ret,
429				       const struct wpabuf *reqData)
430{
431	struct eap_wsc_data *data = priv;
432	const u8 *start, *pos, *end;
433	size_t len;
434	u8 op_code, flags, id;
435	u16 message_length = 0;
436	enum wps_process_res res;
437	struct wpabuf tmpbuf;
438	struct wpabuf *r;
439
440	pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, reqData,
441			       &len);
442	if (pos == NULL || len < 2) {
443		ret->ignore = TRUE;
444		return NULL;
445	}
446
447	id = eap_get_id(reqData);
448
449	start = pos;
450	end = start + len;
451
452	op_code = *pos++;
453	flags = *pos++;
454	if (flags & WSC_FLAGS_LF) {
455		if (end - pos < 2) {
456			wpa_printf(MSG_DEBUG, "EAP-WSC: Message underflow");
457			ret->ignore = TRUE;
458			return NULL;
459		}
460		message_length = WPA_GET_BE16(pos);
461		pos += 2;
462
463		if (message_length < end - pos) {
464			wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message "
465				   "Length");
466			ret->ignore = TRUE;
467			return NULL;
468		}
469	}
470
471	wpa_printf(MSG_DEBUG, "EAP-WSC: Received packet: Op-Code %d "
472		   "Flags 0x%x Message Length %d",
473		   op_code, flags, message_length);
474
475	if (data->state == WAIT_FRAG_ACK) {
476		if (op_code != WSC_FRAG_ACK) {
477			wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d "
478				   "in WAIT_FRAG_ACK state", op_code);
479			ret->ignore = TRUE;
480			return NULL;
481		}
482		wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment acknowledged");
483		eap_wsc_state(data, MESG);
484		return eap_wsc_build_msg(data, ret, id);
485	}
486
487	if (op_code != WSC_ACK && op_code != WSC_NACK && op_code != WSC_MSG &&
488	    op_code != WSC_Done && op_code != WSC_Start) {
489		wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d",
490			   op_code);
491		ret->ignore = TRUE;
492		return NULL;
493	}
494
495	if (data->state == WAIT_START) {
496		if (op_code != WSC_Start) {
497			wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d "
498				   "in WAIT_START state", op_code);
499			ret->ignore = TRUE;
500			return NULL;
501		}
502		wpa_printf(MSG_DEBUG, "EAP-WSC: Received start");
503		eap_wsc_state(data, MESG);
504		/* Start message has empty payload, skip processing */
505		goto send_msg;
506	} else if (op_code == WSC_Start) {
507		wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d",
508			   op_code);
509		ret->ignore = TRUE;
510		return NULL;
511	}
512
513	if (data->in_buf &&
514	    eap_wsc_process_cont(data, pos, end - pos, op_code) < 0) {
515		ret->ignore = TRUE;
516		return NULL;
517	}
518
519	if (flags & WSC_FLAGS_MF) {
520		return eap_wsc_process_fragment(data, ret, id, flags, op_code,
521						message_length, pos,
522						end - pos);
523	}
524
525	if (data->in_buf == NULL) {
526		/* Wrap unfragmented messages as wpabuf without extra copy */
527		wpabuf_set(&tmpbuf, pos, end - pos);
528		data->in_buf = &tmpbuf;
529	}
530
531	res = wps_process_msg(data->wps, op_code, data->in_buf);
532	switch (res) {
533	case WPS_DONE:
534		wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing completed "
535			   "successfully - wait for EAP failure");
536		eap_wsc_state(data, FAIL);
537		break;
538	case WPS_CONTINUE:
539		eap_wsc_state(data, MESG);
540		break;
541	case WPS_FAILURE:
542	case WPS_PENDING:
543		wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing failed");
544		eap_wsc_state(data, FAIL);
545		break;
546	}
547
548	if (data->in_buf != &tmpbuf)
549		wpabuf_free(data->in_buf);
550	data->in_buf = NULL;
551
552send_msg:
553	if (data->out_buf == NULL) {
554		data->out_buf = wps_get_msg(data->wps, &data->out_op_code);
555		if (data->out_buf == NULL) {
556			wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to receive "
557				   "message from WPS");
558			return NULL;
559		}
560		data->out_used = 0;
561	}
562
563	eap_wsc_state(data, MESG);
564	r = eap_wsc_build_msg(data, ret, id);
565	if (data->state == FAIL && ret->methodState == METHOD_DONE) {
566		/* Use reduced client timeout for WPS to avoid long wait */
567		if (sm->ClientTimeout > 2)
568			sm->ClientTimeout = 2;
569	}
570	return r;
571}
572
573
574int eap_peer_wsc_register(void)
575{
576	struct eap_method *eap;
577	int ret;
578
579	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
580				    EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC,
581				    "WSC");
582	if (eap == NULL)
583		return -1;
584
585	eap->init = eap_wsc_init;
586	eap->deinit = eap_wsc_deinit;
587	eap->process = eap_wsc_process;
588
589	ret = eap_peer_method_register(eap);
590	if (ret)
591		eap_peer_method_free(eap);
592	return ret;
593}
594