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