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	if (os_strstr(phase1, "multi_ap=1"))
259		cfg.multi_ap_backhaul_sta = 1;
260
261	data->wps = wps_init(&cfg);
262	if (data->wps == NULL) {
263		os_free(data);
264		wpa_printf(MSG_DEBUG, "EAP-WSC: wps_init failed");
265		return NULL;
266	}
267	res = eap_get_config_fragment_size(sm);
268	if (res > 0)
269		data->fragment_size = res;
270	else
271		data->fragment_size = WSC_FRAGMENT_SIZE;
272	wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment size limit %u",
273		   (unsigned int) data->fragment_size);
274
275	if (registrar && cfg.pin) {
276		wps_registrar_add_pin(data->wps_ctx->registrar, NULL, NULL,
277				      cfg.pin, cfg.pin_len, 0);
278	}
279
280	/* Use reduced client timeout for WPS to avoid long wait */
281	if (sm->ClientTimeout > 30)
282		sm->ClientTimeout = 30;
283
284	return data;
285}
286
287
288static void eap_wsc_deinit(struct eap_sm *sm, void *priv)
289{
290	struct eap_wsc_data *data = priv;
291	wpabuf_free(data->in_buf);
292	wpabuf_free(data->out_buf);
293	wps_deinit(data->wps);
294	os_free(data->wps_ctx->network_key);
295	data->wps_ctx->network_key = NULL;
296	os_free(data);
297}
298
299
300static struct wpabuf * eap_wsc_build_msg(struct eap_wsc_data *data,
301					 struct eap_method_ret *ret, u8 id)
302{
303	struct wpabuf *resp;
304	u8 flags;
305	size_t send_len, plen;
306
307	ret->ignore = FALSE;
308	wpa_printf(MSG_DEBUG, "EAP-WSC: Generating Response");
309	ret->allowNotifications = TRUE;
310
311	flags = 0;
312	send_len = wpabuf_len(data->out_buf) - data->out_used;
313	if (2 + send_len > data->fragment_size) {
314		send_len = data->fragment_size - 2;
315		flags |= WSC_FLAGS_MF;
316		if (data->out_used == 0) {
317			flags |= WSC_FLAGS_LF;
318			send_len -= 2;
319		}
320	}
321	plen = 2 + send_len;
322	if (flags & WSC_FLAGS_LF)
323		plen += 2;
324	resp = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, plen,
325			     EAP_CODE_RESPONSE, id);
326	if (resp == NULL)
327		return NULL;
328
329	wpabuf_put_u8(resp, data->out_op_code); /* Op-Code */
330	wpabuf_put_u8(resp, flags); /* Flags */
331	if (flags & WSC_FLAGS_LF)
332		wpabuf_put_be16(resp, wpabuf_len(data->out_buf));
333
334	wpabuf_put_data(resp, wpabuf_head_u8(data->out_buf) + data->out_used,
335			send_len);
336	data->out_used += send_len;
337
338	ret->methodState = METHOD_MAY_CONT;
339	ret->decision = DECISION_FAIL;
340
341	if (data->out_used == wpabuf_len(data->out_buf)) {
342		wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
343			   "(message sent completely)",
344			   (unsigned long) send_len);
345		wpabuf_free(data->out_buf);
346		data->out_buf = NULL;
347		data->out_used = 0;
348		if ((data->state == FAIL && data->out_op_code == WSC_ACK) ||
349		    data->out_op_code == WSC_NACK ||
350		    data->out_op_code == WSC_Done) {
351			eap_wsc_state(data, FAIL);
352			ret->methodState = METHOD_DONE;
353		} else
354			eap_wsc_state(data, MESG);
355	} else {
356		wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
357			   "(%lu more to send)", (unsigned long) send_len,
358			   (unsigned long) wpabuf_len(data->out_buf) -
359			   data->out_used);
360		eap_wsc_state(data, WAIT_FRAG_ACK);
361	}
362
363	return resp;
364}
365
366
367static int eap_wsc_process_cont(struct eap_wsc_data *data,
368				const u8 *buf, size_t len, u8 op_code)
369{
370	/* Process continuation of a pending message */
371	if (op_code != data->in_op_code) {
372		wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d in "
373			   "fragment (expected %d)",
374			   op_code, data->in_op_code);
375		return -1;
376	}
377
378	if (len > wpabuf_tailroom(data->in_buf)) {
379		wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment overflow");
380		eap_wsc_state(data, FAIL);
381		return -1;
382	}
383
384	wpabuf_put_data(data->in_buf, buf, len);
385	wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes, waiting "
386		   "for %lu bytes more", (unsigned long) len,
387		   (unsigned long) wpabuf_tailroom(data->in_buf));
388
389	return 0;
390}
391
392
393static struct wpabuf * eap_wsc_process_fragment(struct eap_wsc_data *data,
394						struct eap_method_ret *ret,
395						u8 id, u8 flags, u8 op_code,
396						u16 message_length,
397						const u8 *buf, size_t len)
398{
399	/* Process a fragment that is not the last one of the message */
400	if (data->in_buf == NULL && !(flags & WSC_FLAGS_LF)) {
401		wpa_printf(MSG_DEBUG, "EAP-WSC: No Message Length field in a "
402			   "fragmented packet");
403		ret->ignore = TRUE;
404		return NULL;
405	}
406
407	if (data->in_buf == NULL) {
408		/* First fragment of the message */
409		data->in_buf = wpabuf_alloc(message_length);
410		if (data->in_buf == NULL) {
411			wpa_printf(MSG_DEBUG, "EAP-WSC: No memory for "
412				   "message");
413			ret->ignore = TRUE;
414			return NULL;
415		}
416		data->in_op_code = op_code;
417		wpabuf_put_data(data->in_buf, buf, len);
418		wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes in first "
419			   "fragment, waiting for %lu bytes more",
420			   (unsigned long) len,
421			   (unsigned long) wpabuf_tailroom(data->in_buf));
422	}
423
424	return eap_wsc_build_frag_ack(id, EAP_CODE_RESPONSE);
425}
426
427
428static struct wpabuf * eap_wsc_process(struct eap_sm *sm, void *priv,
429				       struct eap_method_ret *ret,
430				       const struct wpabuf *reqData)
431{
432	struct eap_wsc_data *data = priv;
433	const u8 *start, *pos, *end;
434	size_t len;
435	u8 op_code, flags, id;
436	u16 message_length = 0;
437	enum wps_process_res res;
438	struct wpabuf tmpbuf;
439	struct wpabuf *r;
440
441	pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, reqData,
442			       &len);
443	if (pos == NULL || len < 2) {
444		ret->ignore = TRUE;
445		return NULL;
446	}
447
448	id = eap_get_id(reqData);
449
450	start = pos;
451	end = start + len;
452
453	op_code = *pos++;
454	flags = *pos++;
455	if (flags & WSC_FLAGS_LF) {
456		if (end - pos < 2) {
457			wpa_printf(MSG_DEBUG, "EAP-WSC: Message underflow");
458			ret->ignore = TRUE;
459			return NULL;
460		}
461		message_length = WPA_GET_BE16(pos);
462		pos += 2;
463
464		if (message_length < end - pos || message_length > 50000) {
465			wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message "
466				   "Length");
467			ret->ignore = TRUE;
468			return NULL;
469		}
470	}
471
472	wpa_printf(MSG_DEBUG, "EAP-WSC: Received packet: Op-Code %d "
473		   "Flags 0x%x Message Length %d",
474		   op_code, flags, message_length);
475
476	if (data->state == WAIT_FRAG_ACK) {
477		if (op_code != WSC_FRAG_ACK) {
478			wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d "
479				   "in WAIT_FRAG_ACK state", op_code);
480			ret->ignore = TRUE;
481			return NULL;
482		}
483		wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment acknowledged");
484		eap_wsc_state(data, MESG);
485		return eap_wsc_build_msg(data, ret, id);
486	}
487
488	if (op_code != WSC_ACK && op_code != WSC_NACK && op_code != WSC_MSG &&
489	    op_code != WSC_Done && op_code != WSC_Start) {
490		wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d",
491			   op_code);
492		ret->ignore = TRUE;
493		return NULL;
494	}
495
496	if (data->state == WAIT_START) {
497		if (op_code != WSC_Start) {
498			wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d "
499				   "in WAIT_START state", op_code);
500			ret->ignore = TRUE;
501			return NULL;
502		}
503		wpa_printf(MSG_DEBUG, "EAP-WSC: Received start");
504		eap_wsc_state(data, MESG);
505		/* Start message has empty payload, skip processing */
506		goto send_msg;
507	} else if (op_code == WSC_Start) {
508		wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d",
509			   op_code);
510		ret->ignore = TRUE;
511		return NULL;
512	}
513
514	if (data->in_buf &&
515	    eap_wsc_process_cont(data, pos, end - pos, op_code) < 0) {
516		ret->ignore = TRUE;
517		return NULL;
518	}
519
520	if (flags & WSC_FLAGS_MF) {
521		return eap_wsc_process_fragment(data, ret, id, flags, op_code,
522						message_length, pos,
523						end - pos);
524	}
525
526	if (data->in_buf == NULL) {
527		/* Wrap unfragmented messages as wpabuf without extra copy */
528		wpabuf_set(&tmpbuf, pos, end - pos);
529		data->in_buf = &tmpbuf;
530	}
531
532	res = wps_process_msg(data->wps, op_code, data->in_buf);
533	switch (res) {
534	case WPS_DONE:
535		wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing completed "
536			   "successfully - wait for EAP failure");
537		eap_wsc_state(data, FAIL);
538		break;
539	case WPS_CONTINUE:
540		eap_wsc_state(data, MESG);
541		break;
542	case WPS_FAILURE:
543	case WPS_PENDING:
544		wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing failed");
545		eap_wsc_state(data, FAIL);
546		break;
547	}
548
549	if (data->in_buf != &tmpbuf)
550		wpabuf_free(data->in_buf);
551	data->in_buf = NULL;
552
553send_msg:
554	if (data->out_buf == NULL) {
555		data->out_buf = wps_get_msg(data->wps, &data->out_op_code);
556		if (data->out_buf == NULL) {
557			wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to receive "
558				   "message from WPS");
559			eap_wsc_state(data, FAIL);
560			ret->methodState = METHOD_DONE;
561			ret->decision = DECISION_FAIL;
562			return NULL;
563		}
564		data->out_used = 0;
565	}
566
567	eap_wsc_state(data, MESG);
568	r = eap_wsc_build_msg(data, ret, id);
569	if (data->state == FAIL && ret->methodState == METHOD_DONE) {
570		/* Use reduced client timeout for WPS to avoid long wait */
571		if (sm->ClientTimeout > 2)
572			sm->ClientTimeout = 2;
573	}
574	return r;
575}
576
577
578int eap_peer_wsc_register(void)
579{
580	struct eap_method *eap;
581
582	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
583				    EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC,
584				    "WSC");
585	if (eap == NULL)
586		return -1;
587
588	eap->init = eap_wsc_init;
589	eap->deinit = eap_wsc_deinit;
590	eap->process = eap_wsc_process;
591
592	return eap_peer_method_register(eap);
593}
594