xref: /freebsd/contrib/wpa/src/eap_server/eap_server_wsc.c (revision c1d255d3ffdbe447de3ab875bf4e7d7accc5bfc5)
1e28a4053SRui Paulo /*
2e28a4053SRui Paulo  * EAP-WSC server for Wi-Fi Protected Setup
3e28a4053SRui Paulo  * Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi>
4e28a4053SRui Paulo  *
5f05cddf9SRui Paulo  * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui Paulo  * See README for more details.
7e28a4053SRui Paulo  */
8e28a4053SRui Paulo 
9e28a4053SRui Paulo #include "includes.h"
10e28a4053SRui Paulo 
11e28a4053SRui Paulo #include "common.h"
12e28a4053SRui Paulo #include "eloop.h"
13e28a4053SRui Paulo #include "eap_i.h"
14e28a4053SRui Paulo #include "eap_common/eap_wsc_common.h"
15f05cddf9SRui Paulo #include "p2p/p2p.h"
16e28a4053SRui Paulo #include "wps/wps.h"
17e28a4053SRui Paulo 
18e28a4053SRui Paulo 
19e28a4053SRui Paulo struct eap_wsc_data {
20e28a4053SRui Paulo 	enum { START, MESG, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state;
21e28a4053SRui Paulo 	int registrar;
22e28a4053SRui Paulo 	struct wpabuf *in_buf;
23e28a4053SRui Paulo 	struct wpabuf *out_buf;
24e28a4053SRui Paulo 	enum wsc_op_code in_op_code, out_op_code;
25e28a4053SRui Paulo 	size_t out_used;
26e28a4053SRui Paulo 	size_t fragment_size;
27e28a4053SRui Paulo 	struct wps_data *wps;
28e28a4053SRui Paulo 	int ext_reg_timeout;
29e28a4053SRui Paulo };
30e28a4053SRui Paulo 
31e28a4053SRui Paulo 
32e28a4053SRui Paulo #ifndef CONFIG_NO_STDOUT_DEBUG
eap_wsc_state_txt(int state)33e28a4053SRui Paulo static const char * eap_wsc_state_txt(int state)
34e28a4053SRui Paulo {
35e28a4053SRui Paulo 	switch (state) {
36e28a4053SRui Paulo 	case START:
37e28a4053SRui Paulo 		return "START";
38e28a4053SRui Paulo 	case MESG:
39e28a4053SRui Paulo 		return "MESG";
40e28a4053SRui Paulo 	case FRAG_ACK:
41e28a4053SRui Paulo 		return "FRAG_ACK";
42e28a4053SRui Paulo 	case WAIT_FRAG_ACK:
43e28a4053SRui Paulo 		return "WAIT_FRAG_ACK";
44e28a4053SRui Paulo 	case DONE:
45e28a4053SRui Paulo 		return "DONE";
46e28a4053SRui Paulo 	case FAIL:
47e28a4053SRui Paulo 		return "FAIL";
48e28a4053SRui Paulo 	default:
49e28a4053SRui Paulo 		return "?";
50e28a4053SRui Paulo 	}
51e28a4053SRui Paulo }
52e28a4053SRui Paulo #endif /* CONFIG_NO_STDOUT_DEBUG */
53e28a4053SRui Paulo 
54e28a4053SRui Paulo 
eap_wsc_state(struct eap_wsc_data * data,int state)55e28a4053SRui Paulo static void eap_wsc_state(struct eap_wsc_data *data, int state)
56e28a4053SRui Paulo {
57e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-WSC: %s -> %s",
58e28a4053SRui Paulo 		   eap_wsc_state_txt(data->state),
59e28a4053SRui Paulo 		   eap_wsc_state_txt(state));
60e28a4053SRui Paulo 	data->state = state;
61e28a4053SRui Paulo }
62e28a4053SRui Paulo 
63e28a4053SRui Paulo 
eap_wsc_ext_reg_timeout(void * eloop_ctx,void * timeout_ctx)64e28a4053SRui Paulo static void eap_wsc_ext_reg_timeout(void *eloop_ctx, void *timeout_ctx)
65e28a4053SRui Paulo {
66e28a4053SRui Paulo 	struct eap_sm *sm = eloop_ctx;
67e28a4053SRui Paulo 	struct eap_wsc_data *data = timeout_ctx;
68e28a4053SRui Paulo 
69e28a4053SRui Paulo 	if (sm->method_pending != METHOD_PENDING_WAIT)
70e28a4053SRui Paulo 		return;
71e28a4053SRui Paulo 
72e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-WSC: Timeout while waiting for an External "
73e28a4053SRui Paulo 		   "Registrar");
74e28a4053SRui Paulo 	data->ext_reg_timeout = 1;
75e28a4053SRui Paulo 	eap_sm_pending_cb(sm);
76e28a4053SRui Paulo }
77e28a4053SRui Paulo 
78e28a4053SRui Paulo 
eap_wsc_init(struct eap_sm * sm)79e28a4053SRui Paulo static void * eap_wsc_init(struct eap_sm *sm)
80e28a4053SRui Paulo {
81e28a4053SRui Paulo 	struct eap_wsc_data *data;
82e28a4053SRui Paulo 	int registrar;
83e28a4053SRui Paulo 	struct wps_config cfg;
84e28a4053SRui Paulo 
85e28a4053SRui Paulo 	if (sm->identity && sm->identity_len == WSC_ID_REGISTRAR_LEN &&
86e28a4053SRui Paulo 	    os_memcmp(sm->identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) ==
87e28a4053SRui Paulo 	    0)
88e28a4053SRui Paulo 		registrar = 0; /* Supplicant is Registrar */
89e28a4053SRui Paulo 	else if (sm->identity && sm->identity_len == WSC_ID_ENROLLEE_LEN &&
90e28a4053SRui Paulo 		 os_memcmp(sm->identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN)
91e28a4053SRui Paulo 		 == 0)
92e28a4053SRui Paulo 		registrar = 1; /* Supplicant is Enrollee */
93e28a4053SRui Paulo 	else {
94e28a4053SRui Paulo 		wpa_hexdump_ascii(MSG_INFO, "EAP-WSC: Unexpected identity",
95e28a4053SRui Paulo 				  sm->identity, sm->identity_len);
96e28a4053SRui Paulo 		return NULL;
97e28a4053SRui Paulo 	}
98e28a4053SRui Paulo 
99e28a4053SRui Paulo 	data = os_zalloc(sizeof(*data));
100e28a4053SRui Paulo 	if (data == NULL)
101e28a4053SRui Paulo 		return NULL;
102e28a4053SRui Paulo 	data->state = registrar ? START : MESG;
103e28a4053SRui Paulo 	data->registrar = registrar;
104e28a4053SRui Paulo 
105e28a4053SRui Paulo 	os_memset(&cfg, 0, sizeof(cfg));
106*c1d255d3SCy Schubert 	cfg.wps = sm->cfg->wps;
107e28a4053SRui Paulo 	cfg.registrar = registrar;
108e28a4053SRui Paulo 	if (registrar) {
109*c1d255d3SCy Schubert 		if (!sm->cfg->wps || !sm->cfg->wps->registrar) {
110e28a4053SRui Paulo 			wpa_printf(MSG_INFO, "EAP-WSC: WPS Registrar not "
111e28a4053SRui Paulo 				   "initialized");
112e28a4053SRui Paulo 			os_free(data);
113e28a4053SRui Paulo 			return NULL;
114e28a4053SRui Paulo 		}
115e28a4053SRui Paulo 	} else {
116e28a4053SRui Paulo 		if (sm->user == NULL || sm->user->password == NULL) {
117e28a4053SRui Paulo 			/*
118e28a4053SRui Paulo 			 * In theory, this should not really be needed, but
119e28a4053SRui Paulo 			 * Windows 7 uses Registrar mode to probe AP's WPS
120e28a4053SRui Paulo 			 * capabilities before trying to use Enrollee and fails
121e28a4053SRui Paulo 			 * if the AP does not allow that probing to happen..
122e28a4053SRui Paulo 			 */
123e28a4053SRui Paulo 			wpa_printf(MSG_DEBUG, "EAP-WSC: No AP PIN (password) "
124e28a4053SRui Paulo 				   "configured for Enrollee functionality - "
125e28a4053SRui Paulo 				   "allow for probing capabilities (M1)");
126e28a4053SRui Paulo 		} else {
127e28a4053SRui Paulo 			cfg.pin = sm->user->password;
128e28a4053SRui Paulo 			cfg.pin_len = sm->user->password_len;
129e28a4053SRui Paulo 		}
130e28a4053SRui Paulo 	}
131e28a4053SRui Paulo 	cfg.assoc_wps_ie = sm->assoc_wps_ie;
132e28a4053SRui Paulo 	cfg.peer_addr = sm->peer_addr;
133f05cddf9SRui Paulo #ifdef CONFIG_P2P
134f05cddf9SRui Paulo 	if (sm->assoc_p2p_ie) {
135*c1d255d3SCy Schubert 		if (!sm->cfg->wps->use_passphrase) {
136*c1d255d3SCy Schubert 			wpa_printf(MSG_DEBUG,
137*c1d255d3SCy Schubert 				   "EAP-WSC: Prefer PSK format for non-6 GHz P2P client");
138e28a4053SRui Paulo 			cfg.use_psk_key = 1;
139*c1d255d3SCy Schubert 		}
140f05cddf9SRui Paulo 		cfg.p2p_dev_addr = p2p_get_go_dev_addr(sm->assoc_p2p_ie);
141f05cddf9SRui Paulo 	}
142f05cddf9SRui Paulo #endif /* CONFIG_P2P */
143*c1d255d3SCy Schubert 	cfg.pbc_in_m1 = sm->cfg->pbc_in_m1;
144e28a4053SRui Paulo 	data->wps = wps_init(&cfg);
145e28a4053SRui Paulo 	if (data->wps == NULL) {
146e28a4053SRui Paulo 		os_free(data);
147e28a4053SRui Paulo 		return NULL;
148e28a4053SRui Paulo 	}
149*c1d255d3SCy Schubert 	data->fragment_size = sm->cfg->fragment_size > 0 ?
150*c1d255d3SCy Schubert 		sm->cfg->fragment_size : WSC_FRAGMENT_SIZE;
151e28a4053SRui Paulo 
152e28a4053SRui Paulo 	return data;
153e28a4053SRui Paulo }
154e28a4053SRui Paulo 
155e28a4053SRui Paulo 
eap_wsc_reset(struct eap_sm * sm,void * priv)156e28a4053SRui Paulo static void eap_wsc_reset(struct eap_sm *sm, void *priv)
157e28a4053SRui Paulo {
158e28a4053SRui Paulo 	struct eap_wsc_data *data = priv;
159e28a4053SRui Paulo 	eloop_cancel_timeout(eap_wsc_ext_reg_timeout, sm, data);
160e28a4053SRui Paulo 	wpabuf_free(data->in_buf);
161e28a4053SRui Paulo 	wpabuf_free(data->out_buf);
162e28a4053SRui Paulo 	wps_deinit(data->wps);
163e28a4053SRui Paulo 	os_free(data);
164e28a4053SRui Paulo }
165e28a4053SRui Paulo 
166e28a4053SRui Paulo 
eap_wsc_build_start(struct eap_sm * sm,struct eap_wsc_data * data,u8 id)167e28a4053SRui Paulo static struct wpabuf * eap_wsc_build_start(struct eap_sm *sm,
168e28a4053SRui Paulo 					   struct eap_wsc_data *data, u8 id)
169e28a4053SRui Paulo {
170e28a4053SRui Paulo 	struct wpabuf *req;
171e28a4053SRui Paulo 
172e28a4053SRui Paulo 	req = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, 2,
173e28a4053SRui Paulo 			    EAP_CODE_REQUEST, id);
174e28a4053SRui Paulo 	if (req == NULL) {
175e28a4053SRui Paulo 		wpa_printf(MSG_ERROR, "EAP-WSC: Failed to allocate memory for "
176e28a4053SRui Paulo 			   "request");
177e28a4053SRui Paulo 		return NULL;
178e28a4053SRui Paulo 	}
179e28a4053SRui Paulo 
180e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-WSC: Send WSC/Start");
181e28a4053SRui Paulo 	wpabuf_put_u8(req, WSC_Start); /* Op-Code */
182e28a4053SRui Paulo 	wpabuf_put_u8(req, 0); /* Flags */
183e28a4053SRui Paulo 
184e28a4053SRui Paulo 	return req;
185e28a4053SRui Paulo }
186e28a4053SRui Paulo 
187e28a4053SRui Paulo 
eap_wsc_build_msg(struct eap_wsc_data * data,u8 id)188e28a4053SRui Paulo static struct wpabuf * eap_wsc_build_msg(struct eap_wsc_data *data, u8 id)
189e28a4053SRui Paulo {
190e28a4053SRui Paulo 	struct wpabuf *req;
191e28a4053SRui Paulo 	u8 flags;
192e28a4053SRui Paulo 	size_t send_len, plen;
193e28a4053SRui Paulo 
194e28a4053SRui Paulo 	flags = 0;
195e28a4053SRui Paulo 	send_len = wpabuf_len(data->out_buf) - data->out_used;
196e28a4053SRui Paulo 	if (2 + send_len > data->fragment_size) {
197e28a4053SRui Paulo 		send_len = data->fragment_size - 2;
198e28a4053SRui Paulo 		flags |= WSC_FLAGS_MF;
199e28a4053SRui Paulo 		if (data->out_used == 0) {
200e28a4053SRui Paulo 			flags |= WSC_FLAGS_LF;
201e28a4053SRui Paulo 			send_len -= 2;
202e28a4053SRui Paulo 		}
203e28a4053SRui Paulo 	}
204e28a4053SRui Paulo 	plen = 2 + send_len;
205e28a4053SRui Paulo 	if (flags & WSC_FLAGS_LF)
206e28a4053SRui Paulo 		plen += 2;
207e28a4053SRui Paulo 	req = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, plen,
208e28a4053SRui Paulo 			    EAP_CODE_REQUEST, id);
209e28a4053SRui Paulo 	if (req == NULL) {
210e28a4053SRui Paulo 		wpa_printf(MSG_ERROR, "EAP-WSC: Failed to allocate memory for "
211e28a4053SRui Paulo 			   "request");
212e28a4053SRui Paulo 		return NULL;
213e28a4053SRui Paulo 	}
214e28a4053SRui Paulo 
215e28a4053SRui Paulo 	wpabuf_put_u8(req, data->out_op_code); /* Op-Code */
216e28a4053SRui Paulo 	wpabuf_put_u8(req, flags); /* Flags */
217e28a4053SRui Paulo 	if (flags & WSC_FLAGS_LF)
218e28a4053SRui Paulo 		wpabuf_put_be16(req, wpabuf_len(data->out_buf));
219e28a4053SRui Paulo 
220e28a4053SRui Paulo 	wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used,
221e28a4053SRui Paulo 			send_len);
222e28a4053SRui Paulo 	data->out_used += send_len;
223e28a4053SRui Paulo 
224e28a4053SRui Paulo 	if (data->out_used == wpabuf_len(data->out_buf)) {
225e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
226e28a4053SRui Paulo 			   "(message sent completely)",
227e28a4053SRui Paulo 			   (unsigned long) send_len);
228e28a4053SRui Paulo 		wpabuf_free(data->out_buf);
229e28a4053SRui Paulo 		data->out_buf = NULL;
230e28a4053SRui Paulo 		data->out_used = 0;
231e28a4053SRui Paulo 		eap_wsc_state(data, MESG);
232e28a4053SRui Paulo 	} else {
233e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
234e28a4053SRui Paulo 			   "(%lu more to send)", (unsigned long) send_len,
235e28a4053SRui Paulo 			   (unsigned long) wpabuf_len(data->out_buf) -
236e28a4053SRui Paulo 			   data->out_used);
237e28a4053SRui Paulo 		eap_wsc_state(data, WAIT_FRAG_ACK);
238e28a4053SRui Paulo 	}
239e28a4053SRui Paulo 
240e28a4053SRui Paulo 	return req;
241e28a4053SRui Paulo }
242e28a4053SRui Paulo 
243e28a4053SRui Paulo 
eap_wsc_buildReq(struct eap_sm * sm,void * priv,u8 id)244e28a4053SRui Paulo static struct wpabuf * eap_wsc_buildReq(struct eap_sm *sm, void *priv, u8 id)
245e28a4053SRui Paulo {
246e28a4053SRui Paulo 	struct eap_wsc_data *data = priv;
247e28a4053SRui Paulo 
248e28a4053SRui Paulo 	switch (data->state) {
249e28a4053SRui Paulo 	case START:
250e28a4053SRui Paulo 		return eap_wsc_build_start(sm, data, id);
251e28a4053SRui Paulo 	case MESG:
252e28a4053SRui Paulo 		if (data->out_buf == NULL) {
253e28a4053SRui Paulo 			data->out_buf = wps_get_msg(data->wps,
254e28a4053SRui Paulo 						    &data->out_op_code);
255e28a4053SRui Paulo 			if (data->out_buf == NULL) {
256e28a4053SRui Paulo 				wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to "
257e28a4053SRui Paulo 					   "receive message from WPS");
258e28a4053SRui Paulo 				return NULL;
259e28a4053SRui Paulo 			}
260e28a4053SRui Paulo 			data->out_used = 0;
261e28a4053SRui Paulo 		}
26285732ac8SCy Schubert 		/* fall through */
263e28a4053SRui Paulo 	case WAIT_FRAG_ACK:
264e28a4053SRui Paulo 		return eap_wsc_build_msg(data, id);
265e28a4053SRui Paulo 	case FRAG_ACK:
266e28a4053SRui Paulo 		return eap_wsc_build_frag_ack(id, EAP_CODE_REQUEST);
267e28a4053SRui Paulo 	default:
268e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected state %d in "
269e28a4053SRui Paulo 			   "buildReq", data->state);
270e28a4053SRui Paulo 		return NULL;
271e28a4053SRui Paulo 	}
272e28a4053SRui Paulo }
273e28a4053SRui Paulo 
274e28a4053SRui Paulo 
eap_wsc_check(struct eap_sm * sm,void * priv,struct wpabuf * respData)275*c1d255d3SCy Schubert static bool eap_wsc_check(struct eap_sm *sm, void *priv,
276e28a4053SRui Paulo 			  struct wpabuf *respData)
277e28a4053SRui Paulo {
278e28a4053SRui Paulo 	const u8 *pos;
279e28a4053SRui Paulo 	size_t len;
280e28a4053SRui Paulo 
281e28a4053SRui Paulo 	pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC,
282e28a4053SRui Paulo 			       respData, &len);
283e28a4053SRui Paulo 	if (pos == NULL || len < 2) {
284e28a4053SRui Paulo 		wpa_printf(MSG_INFO, "EAP-WSC: Invalid frame");
285*c1d255d3SCy Schubert 		return true;
286e28a4053SRui Paulo 	}
287e28a4053SRui Paulo 
288*c1d255d3SCy Schubert 	return false;
289e28a4053SRui Paulo }
290e28a4053SRui Paulo 
291e28a4053SRui Paulo 
eap_wsc_process_cont(struct eap_wsc_data * data,const u8 * buf,size_t len,u8 op_code)292e28a4053SRui Paulo static int eap_wsc_process_cont(struct eap_wsc_data *data,
293e28a4053SRui Paulo 				const u8 *buf, size_t len, u8 op_code)
294e28a4053SRui Paulo {
295e28a4053SRui Paulo 	/* Process continuation of a pending message */
296e28a4053SRui Paulo 	if (op_code != data->in_op_code) {
297e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d in "
298e28a4053SRui Paulo 			   "fragment (expected %d)",
299e28a4053SRui Paulo 			   op_code, data->in_op_code);
300e28a4053SRui Paulo 		eap_wsc_state(data, FAIL);
301e28a4053SRui Paulo 		return -1;
302e28a4053SRui Paulo 	}
303e28a4053SRui Paulo 
304e28a4053SRui Paulo 	if (len > wpabuf_tailroom(data->in_buf)) {
305e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment overflow");
306e28a4053SRui Paulo 		eap_wsc_state(data, FAIL);
307e28a4053SRui Paulo 		return -1;
308e28a4053SRui Paulo 	}
309e28a4053SRui Paulo 
310e28a4053SRui Paulo 	wpabuf_put_data(data->in_buf, buf, len);
311e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes, waiting for %lu "
312e28a4053SRui Paulo 		   "bytes more", (unsigned long) len,
313e28a4053SRui Paulo 		   (unsigned long) wpabuf_tailroom(data->in_buf));
314e28a4053SRui Paulo 
315e28a4053SRui Paulo 	return 0;
316e28a4053SRui Paulo }
317e28a4053SRui Paulo 
318e28a4053SRui Paulo 
eap_wsc_process_fragment(struct eap_wsc_data * data,u8 flags,u8 op_code,u16 message_length,const u8 * buf,size_t len)319e28a4053SRui Paulo static int eap_wsc_process_fragment(struct eap_wsc_data *data,
320e28a4053SRui Paulo 				    u8 flags, u8 op_code, u16 message_length,
321e28a4053SRui Paulo 				    const u8 *buf, size_t len)
322e28a4053SRui Paulo {
323e28a4053SRui Paulo 	/* Process a fragment that is not the last one of the message */
324e28a4053SRui Paulo 	if (data->in_buf == NULL && !(flags & WSC_FLAGS_LF)) {
325e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-WSC: No Message Length "
326e28a4053SRui Paulo 			   "field in a fragmented packet");
327e28a4053SRui Paulo 		return -1;
328e28a4053SRui Paulo 	}
329e28a4053SRui Paulo 
330e28a4053SRui Paulo 	if (data->in_buf == NULL) {
331e28a4053SRui Paulo 		/* First fragment of the message */
332e28a4053SRui Paulo 		data->in_buf = wpabuf_alloc(message_length);
333e28a4053SRui Paulo 		if (data->in_buf == NULL) {
334e28a4053SRui Paulo 			wpa_printf(MSG_DEBUG, "EAP-WSC: No memory for "
335e28a4053SRui Paulo 				   "message");
336e28a4053SRui Paulo 			return -1;
337e28a4053SRui Paulo 		}
338e28a4053SRui Paulo 		data->in_op_code = op_code;
339e28a4053SRui Paulo 		wpabuf_put_data(data->in_buf, buf, len);
340e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes in "
341e28a4053SRui Paulo 			   "first fragment, waiting for %lu bytes more",
342e28a4053SRui Paulo 			   (unsigned long) len,
343e28a4053SRui Paulo 			   (unsigned long) wpabuf_tailroom(data->in_buf));
344e28a4053SRui Paulo 	}
345e28a4053SRui Paulo 
346e28a4053SRui Paulo 	return 0;
347e28a4053SRui Paulo }
348e28a4053SRui Paulo 
349e28a4053SRui Paulo 
eap_wsc_process(struct eap_sm * sm,void * priv,struct wpabuf * respData)350e28a4053SRui Paulo static void eap_wsc_process(struct eap_sm *sm, void *priv,
351e28a4053SRui Paulo 			    struct wpabuf *respData)
352e28a4053SRui Paulo {
353e28a4053SRui Paulo 	struct eap_wsc_data *data = priv;
354e28a4053SRui Paulo 	const u8 *start, *pos, *end;
355e28a4053SRui Paulo 	size_t len;
356e28a4053SRui Paulo 	u8 op_code, flags;
357e28a4053SRui Paulo 	u16 message_length = 0;
358e28a4053SRui Paulo 	enum wps_process_res res;
359e28a4053SRui Paulo 	struct wpabuf tmpbuf;
360e28a4053SRui Paulo 
361e28a4053SRui Paulo 	eloop_cancel_timeout(eap_wsc_ext_reg_timeout, sm, data);
362e28a4053SRui Paulo 	if (data->ext_reg_timeout) {
363e28a4053SRui Paulo 		eap_wsc_state(data, FAIL);
364e28a4053SRui Paulo 		return;
365e28a4053SRui Paulo 	}
366e28a4053SRui Paulo 
367e28a4053SRui Paulo 	pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC,
368e28a4053SRui Paulo 			       respData, &len);
369e28a4053SRui Paulo 	if (pos == NULL || len < 2)
370e28a4053SRui Paulo 		return; /* Should not happen; message already verified */
371e28a4053SRui Paulo 
372e28a4053SRui Paulo 	start = pos;
373e28a4053SRui Paulo 	end = start + len;
374e28a4053SRui Paulo 
375e28a4053SRui Paulo 	op_code = *pos++;
376e28a4053SRui Paulo 	flags = *pos++;
377e28a4053SRui Paulo 	if (flags & WSC_FLAGS_LF) {
378e28a4053SRui Paulo 		if (end - pos < 2) {
379e28a4053SRui Paulo 			wpa_printf(MSG_DEBUG, "EAP-WSC: Message underflow");
380e28a4053SRui Paulo 			return;
381e28a4053SRui Paulo 		}
382e28a4053SRui Paulo 		message_length = WPA_GET_BE16(pos);
383e28a4053SRui Paulo 		pos += 2;
384e28a4053SRui Paulo 
3855b9c547cSRui Paulo 		if (message_length < end - pos || message_length > 50000) {
386e28a4053SRui Paulo 			wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message "
387e28a4053SRui Paulo 				   "Length");
388e28a4053SRui Paulo 			return;
389e28a4053SRui Paulo 		}
390e28a4053SRui Paulo 	}
391e28a4053SRui Paulo 
392e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-WSC: Received packet: Op-Code %d "
393e28a4053SRui Paulo 		   "Flags 0x%x Message Length %d",
394e28a4053SRui Paulo 		   op_code, flags, message_length);
395e28a4053SRui Paulo 
396e28a4053SRui Paulo 	if (data->state == WAIT_FRAG_ACK) {
397e28a4053SRui Paulo 		if (op_code != WSC_FRAG_ACK) {
398e28a4053SRui Paulo 			wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d "
399e28a4053SRui Paulo 				   "in WAIT_FRAG_ACK state", op_code);
400e28a4053SRui Paulo 			eap_wsc_state(data, FAIL);
401e28a4053SRui Paulo 			return;
402e28a4053SRui Paulo 		}
403e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment acknowledged");
404e28a4053SRui Paulo 		eap_wsc_state(data, MESG);
405e28a4053SRui Paulo 		return;
406e28a4053SRui Paulo 	}
407e28a4053SRui Paulo 
408e28a4053SRui Paulo 	if (op_code != WSC_ACK && op_code != WSC_NACK && op_code != WSC_MSG &&
409e28a4053SRui Paulo 	    op_code != WSC_Done) {
410e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d",
411e28a4053SRui Paulo 			   op_code);
412e28a4053SRui Paulo 		eap_wsc_state(data, FAIL);
413e28a4053SRui Paulo 		return;
414e28a4053SRui Paulo 	}
415e28a4053SRui Paulo 
416e28a4053SRui Paulo 	if (data->in_buf &&
417e28a4053SRui Paulo 	    eap_wsc_process_cont(data, pos, end - pos, op_code) < 0) {
418e28a4053SRui Paulo 		eap_wsc_state(data, FAIL);
419e28a4053SRui Paulo 		return;
420e28a4053SRui Paulo 	}
421e28a4053SRui Paulo 
422e28a4053SRui Paulo 	if (flags & WSC_FLAGS_MF) {
423e28a4053SRui Paulo 		if (eap_wsc_process_fragment(data, flags, op_code,
424e28a4053SRui Paulo 					     message_length, pos, end - pos) <
425e28a4053SRui Paulo 		    0)
426e28a4053SRui Paulo 			eap_wsc_state(data, FAIL);
427e28a4053SRui Paulo 		else
428e28a4053SRui Paulo 			eap_wsc_state(data, FRAG_ACK);
429e28a4053SRui Paulo 		return;
430e28a4053SRui Paulo 	}
431e28a4053SRui Paulo 
432e28a4053SRui Paulo 	if (data->in_buf == NULL) {
433e28a4053SRui Paulo 		/* Wrap unfragmented messages as wpabuf without extra copy */
434e28a4053SRui Paulo 		wpabuf_set(&tmpbuf, pos, end - pos);
435e28a4053SRui Paulo 		data->in_buf = &tmpbuf;
436e28a4053SRui Paulo 	}
437e28a4053SRui Paulo 
438e28a4053SRui Paulo 	res = wps_process_msg(data->wps, op_code, data->in_buf);
439e28a4053SRui Paulo 	switch (res) {
440e28a4053SRui Paulo 	case WPS_DONE:
441e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing completed "
442e28a4053SRui Paulo 			   "successfully - report EAP failure");
443e28a4053SRui Paulo 		eap_wsc_state(data, FAIL);
444e28a4053SRui Paulo 		break;
445e28a4053SRui Paulo 	case WPS_CONTINUE:
446e28a4053SRui Paulo 		eap_wsc_state(data, MESG);
447e28a4053SRui Paulo 		break;
448e28a4053SRui Paulo 	case WPS_FAILURE:
449e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing failed");
450e28a4053SRui Paulo 		eap_wsc_state(data, FAIL);
451e28a4053SRui Paulo 		break;
452e28a4053SRui Paulo 	case WPS_PENDING:
453e28a4053SRui Paulo 		eap_wsc_state(data, MESG);
454e28a4053SRui Paulo 		sm->method_pending = METHOD_PENDING_WAIT;
455e28a4053SRui Paulo 		eloop_cancel_timeout(eap_wsc_ext_reg_timeout, sm, data);
456e28a4053SRui Paulo 		eloop_register_timeout(5, 0, eap_wsc_ext_reg_timeout,
457e28a4053SRui Paulo 				       sm, data);
458e28a4053SRui Paulo 		break;
459e28a4053SRui Paulo 	}
460e28a4053SRui Paulo 
461e28a4053SRui Paulo 	if (data->in_buf != &tmpbuf)
462e28a4053SRui Paulo 		wpabuf_free(data->in_buf);
463e28a4053SRui Paulo 	data->in_buf = NULL;
464e28a4053SRui Paulo }
465e28a4053SRui Paulo 
466e28a4053SRui Paulo 
eap_wsc_isDone(struct eap_sm * sm,void * priv)467*c1d255d3SCy Schubert static bool eap_wsc_isDone(struct eap_sm *sm, void *priv)
468e28a4053SRui Paulo {
469e28a4053SRui Paulo 	struct eap_wsc_data *data = priv;
470e28a4053SRui Paulo 	return data->state == FAIL;
471e28a4053SRui Paulo }
472e28a4053SRui Paulo 
473e28a4053SRui Paulo 
eap_wsc_isSuccess(struct eap_sm * sm,void * priv)474*c1d255d3SCy Schubert static bool eap_wsc_isSuccess(struct eap_sm *sm, void *priv)
475e28a4053SRui Paulo {
476e28a4053SRui Paulo 	/* EAP-WSC will always result in EAP-Failure */
477*c1d255d3SCy Schubert 	return false;
478e28a4053SRui Paulo }
479e28a4053SRui Paulo 
480e28a4053SRui Paulo 
eap_wsc_getTimeout(struct eap_sm * sm,void * priv)481e28a4053SRui Paulo static int eap_wsc_getTimeout(struct eap_sm *sm, void *priv)
482e28a4053SRui Paulo {
483e28a4053SRui Paulo 	/* Recommended retransmit times: retransmit timeout 5 seconds,
484e28a4053SRui Paulo 	 * per-message timeout 15 seconds, i.e., 3 tries. */
485e28a4053SRui Paulo 	sm->MaxRetrans = 2; /* total 3 attempts */
486e28a4053SRui Paulo 	return 5;
487e28a4053SRui Paulo }
488e28a4053SRui Paulo 
489e28a4053SRui Paulo 
eap_server_wsc_register(void)490e28a4053SRui Paulo int eap_server_wsc_register(void)
491e28a4053SRui Paulo {
492e28a4053SRui Paulo 	struct eap_method *eap;
493e28a4053SRui Paulo 
494e28a4053SRui Paulo 	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
495e28a4053SRui Paulo 				      EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC,
496e28a4053SRui Paulo 				      "WSC");
497e28a4053SRui Paulo 	if (eap == NULL)
498e28a4053SRui Paulo 		return -1;
499e28a4053SRui Paulo 
500e28a4053SRui Paulo 	eap->init = eap_wsc_init;
501e28a4053SRui Paulo 	eap->reset = eap_wsc_reset;
502e28a4053SRui Paulo 	eap->buildReq = eap_wsc_buildReq;
503e28a4053SRui Paulo 	eap->check = eap_wsc_check;
504e28a4053SRui Paulo 	eap->process = eap_wsc_process;
505e28a4053SRui Paulo 	eap->isDone = eap_wsc_isDone;
506e28a4053SRui Paulo 	eap->isSuccess = eap_wsc_isSuccess;
507e28a4053SRui Paulo 	eap->getTimeout = eap_wsc_getTimeout;
508e28a4053SRui Paulo 
509780fb4a2SCy Schubert 	return eap_server_method_register(eap);
510e28a4053SRui Paulo }
511