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