1/*
2 * hostapd / EAP-SAKE (RFC 4763) server
3 * Copyright (c) 2006-2007, 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 "crypto/random.h"
13#include "eap_server/eap_i.h"
14#include "eap_common/eap_sake_common.h"
15
16
17struct eap_sake_data {
18	enum { IDENTITY, CHALLENGE, CONFIRM, SUCCESS, FAILURE } state;
19	u8 rand_s[EAP_SAKE_RAND_LEN];
20	u8 rand_p[EAP_SAKE_RAND_LEN];
21	struct {
22		u8 auth[EAP_SAKE_TEK_AUTH_LEN];
23		u8 cipher[EAP_SAKE_TEK_CIPHER_LEN];
24	} tek;
25	u8 msk[EAP_MSK_LEN];
26	u8 emsk[EAP_EMSK_LEN];
27	u8 session_id;
28	u8 *peerid;
29	size_t peerid_len;
30};
31
32
33static const char * eap_sake_state_txt(int state)
34{
35	switch (state) {
36	case IDENTITY:
37		return "IDENTITY";
38	case CHALLENGE:
39		return "CHALLENGE";
40	case CONFIRM:
41		return "CONFIRM";
42	case SUCCESS:
43		return "SUCCESS";
44	case FAILURE:
45		return "FAILURE";
46	default:
47		return "?";
48	}
49}
50
51
52static void eap_sake_state(struct eap_sake_data *data, int state)
53{
54	wpa_printf(MSG_DEBUG, "EAP-SAKE: %s -> %s",
55		   eap_sake_state_txt(data->state),
56		   eap_sake_state_txt(state));
57	data->state = state;
58}
59
60
61static void * eap_sake_init(struct eap_sm *sm)
62{
63	struct eap_sake_data *data;
64
65	data = os_zalloc(sizeof(*data));
66	if (data == NULL)
67		return NULL;
68	data->state = CHALLENGE;
69
70	if (os_get_random(&data->session_id, 1)) {
71		wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data");
72		os_free(data);
73		return NULL;
74	}
75	wpa_printf(MSG_DEBUG, "EAP-SAKE: Initialized Session ID %d",
76		   data->session_id);
77
78	return data;
79}
80
81
82static void eap_sake_reset(struct eap_sm *sm, void *priv)
83{
84	struct eap_sake_data *data = priv;
85	os_free(data->peerid);
86	bin_clear_free(data, sizeof(*data));
87}
88
89
90static struct wpabuf * eap_sake_build_msg(struct eap_sake_data *data,
91					  u8 id, size_t length, u8 subtype)
92{
93	struct eap_sake_hdr *sake;
94	struct wpabuf *msg;
95	size_t plen;
96
97	plen = sizeof(struct eap_sake_hdr) + length;
98
99	msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_SAKE, plen,
100			    EAP_CODE_REQUEST, id);
101	if (msg == NULL) {
102		wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to allocate memory "
103			   "request");
104		return NULL;
105	}
106
107	sake = wpabuf_put(msg, sizeof(*sake));
108	sake->version = EAP_SAKE_VERSION;
109	sake->session_id = data->session_id;
110	sake->subtype = subtype;
111
112	return msg;
113}
114
115
116static struct wpabuf * eap_sake_build_identity(struct eap_sm *sm,
117					       struct eap_sake_data *data,
118					       u8 id)
119{
120	struct wpabuf *msg;
121	size_t plen;
122
123	wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Identity");
124
125	plen = 4;
126	plen += 2 + sm->server_id_len;
127	msg = eap_sake_build_msg(data, id, plen, EAP_SAKE_SUBTYPE_IDENTITY);
128	if (msg == NULL) {
129		data->state = FAILURE;
130		return NULL;
131	}
132
133	wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PERM_ID_REQ");
134	eap_sake_add_attr(msg, EAP_SAKE_AT_PERM_ID_REQ, NULL, 2);
135
136	wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID");
137	eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID,
138			  sm->server_id, sm->server_id_len);
139
140	return msg;
141}
142
143
144static struct wpabuf * eap_sake_build_challenge(struct eap_sm *sm,
145						struct eap_sake_data *data,
146						u8 id)
147{
148	struct wpabuf *msg;
149	size_t plen;
150
151	wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Challenge");
152
153	if (random_get_bytes(data->rand_s, EAP_SAKE_RAND_LEN)) {
154		wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data");
155		data->state = FAILURE;
156		return NULL;
157	}
158	wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_S (server rand)",
159		    data->rand_s, EAP_SAKE_RAND_LEN);
160
161	plen = 2 + EAP_SAKE_RAND_LEN + 2 + sm->server_id_len;
162	msg = eap_sake_build_msg(data, id, plen, EAP_SAKE_SUBTYPE_CHALLENGE);
163	if (msg == NULL) {
164		data->state = FAILURE;
165		return NULL;
166	}
167
168	wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_RAND_S");
169	eap_sake_add_attr(msg, EAP_SAKE_AT_RAND_S,
170			  data->rand_s, EAP_SAKE_RAND_LEN);
171
172	wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID");
173	eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID,
174			  sm->server_id, sm->server_id_len);
175
176	return msg;
177}
178
179
180static struct wpabuf * eap_sake_build_confirm(struct eap_sm *sm,
181					      struct eap_sake_data *data,
182					      u8 id)
183{
184	struct wpabuf *msg;
185	u8 *mic;
186
187	wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Confirm");
188
189	msg = eap_sake_build_msg(data, id, 2 + EAP_SAKE_MIC_LEN,
190				 EAP_SAKE_SUBTYPE_CONFIRM);
191	if (msg == NULL) {
192		data->state = FAILURE;
193		return NULL;
194	}
195
196	wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_MIC_S");
197	wpabuf_put_u8(msg, EAP_SAKE_AT_MIC_S);
198	wpabuf_put_u8(msg, 2 + EAP_SAKE_MIC_LEN);
199	mic = wpabuf_put(msg, EAP_SAKE_MIC_LEN);
200	if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
201				 sm->server_id, sm->server_id_len,
202				 data->peerid, data->peerid_len, 0,
203				 wpabuf_head(msg), wpabuf_len(msg), mic, mic))
204	{
205		wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
206		data->state = FAILURE;
207		wpabuf_free(msg);
208		return NULL;
209	}
210
211	return msg;
212}
213
214
215static struct wpabuf * eap_sake_buildReq(struct eap_sm *sm, void *priv, u8 id)
216{
217	struct eap_sake_data *data = priv;
218
219	switch (data->state) {
220	case IDENTITY:
221		return eap_sake_build_identity(sm, data, id);
222	case CHALLENGE:
223		return eap_sake_build_challenge(sm, data, id);
224	case CONFIRM:
225		return eap_sake_build_confirm(sm, data, id);
226	default:
227		wpa_printf(MSG_DEBUG, "EAP-SAKE: Unknown state %d in buildReq",
228			   data->state);
229		break;
230	}
231	return NULL;
232}
233
234
235static Boolean eap_sake_check(struct eap_sm *sm, void *priv,
236			      struct wpabuf *respData)
237{
238	struct eap_sake_data *data = priv;
239	struct eap_sake_hdr *resp;
240	size_t len;
241	u8 version, session_id, subtype;
242	const u8 *pos;
243
244	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, respData, &len);
245	if (pos == NULL || len < sizeof(struct eap_sake_hdr)) {
246		wpa_printf(MSG_INFO, "EAP-SAKE: Invalid frame");
247		return TRUE;
248	}
249
250	resp = (struct eap_sake_hdr *) pos;
251	version = resp->version;
252	session_id = resp->session_id;
253	subtype = resp->subtype;
254
255	if (version != EAP_SAKE_VERSION) {
256		wpa_printf(MSG_INFO, "EAP-SAKE: Unknown version %d", version);
257		return TRUE;
258	}
259
260	if (session_id != data->session_id) {
261		wpa_printf(MSG_INFO, "EAP-SAKE: Session ID mismatch (%d,%d)",
262			   session_id, data->session_id);
263		return TRUE;
264	}
265
266	wpa_printf(MSG_DEBUG, "EAP-SAKE: Received frame: subtype=%d", subtype);
267
268	if (data->state == IDENTITY && subtype == EAP_SAKE_SUBTYPE_IDENTITY)
269		return FALSE;
270
271	if (data->state == CHALLENGE && subtype == EAP_SAKE_SUBTYPE_CHALLENGE)
272		return FALSE;
273
274	if (data->state == CONFIRM && subtype == EAP_SAKE_SUBTYPE_CONFIRM)
275		return FALSE;
276
277	if (subtype == EAP_SAKE_SUBTYPE_AUTH_REJECT)
278		return FALSE;
279
280	wpa_printf(MSG_INFO, "EAP-SAKE: Unexpected subtype=%d in state=%d",
281		   subtype, data->state);
282
283	return TRUE;
284}
285
286
287static void eap_sake_process_identity(struct eap_sm *sm,
288				      struct eap_sake_data *data,
289				      const struct wpabuf *respData,
290				      const u8 *payload, size_t payloadlen)
291{
292	if (data->state != IDENTITY)
293		return;
294
295	wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Identity");
296	/* TODO: update identity and select new user data */
297	eap_sake_state(data, CHALLENGE);
298}
299
300
301static void eap_sake_process_challenge(struct eap_sm *sm,
302				       struct eap_sake_data *data,
303				       const struct wpabuf *respData,
304				       const u8 *payload, size_t payloadlen)
305{
306	struct eap_sake_parse_attr attr;
307	u8 mic_p[EAP_SAKE_MIC_LEN];
308
309	if (data->state != CHALLENGE)
310		return;
311
312	wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Challenge");
313
314	if (eap_sake_parse_attributes(payload, payloadlen, &attr))
315		return;
316
317	if (!attr.rand_p || !attr.mic_p) {
318		wpa_printf(MSG_INFO, "EAP-SAKE: Response/Challenge did not "
319			   "include AT_RAND_P or AT_MIC_P");
320		return;
321	}
322
323	os_memcpy(data->rand_p, attr.rand_p, EAP_SAKE_RAND_LEN);
324
325	os_free(data->peerid);
326	data->peerid = NULL;
327	data->peerid_len = 0;
328	if (attr.peerid) {
329		data->peerid = os_memdup(attr.peerid, attr.peerid_len);
330		if (data->peerid == NULL)
331			return;
332		data->peerid_len = attr.peerid_len;
333	}
334
335	if (sm->user == NULL || sm->user->password == NULL ||
336	    sm->user->password_len != 2 * EAP_SAKE_ROOT_SECRET_LEN) {
337		wpa_printf(MSG_INFO, "EAP-SAKE: Plaintext password with "
338			   "%d-byte key not configured",
339			   2 * EAP_SAKE_ROOT_SECRET_LEN);
340		data->state = FAILURE;
341		return;
342	}
343	if (eap_sake_derive_keys(sm->user->password,
344				 sm->user->password + EAP_SAKE_ROOT_SECRET_LEN,
345				 data->rand_s, data->rand_p,
346				 (u8 *) &data->tek, data->msk,
347				 data->emsk) < 0) {
348		wpa_printf(MSG_INFO, "EAP-SAKE: Failed to derive keys");
349		data->state = FAILURE;
350		return;
351	}
352
353	if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
354				 sm->server_id, sm->server_id_len,
355				 data->peerid, data->peerid_len, 1,
356				 wpabuf_head(respData), wpabuf_len(respData),
357				 attr.mic_p, mic_p) < 0) {
358		wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
359		data->state = FAILURE;
360		return;
361	}
362	if (os_memcmp_const(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) {
363		wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P");
364		eap_sake_state(data, FAILURE);
365		return;
366	}
367
368	eap_sake_state(data, CONFIRM);
369}
370
371
372static void eap_sake_process_confirm(struct eap_sm *sm,
373				     struct eap_sake_data *data,
374				     const struct wpabuf *respData,
375				     const u8 *payload, size_t payloadlen)
376{
377	struct eap_sake_parse_attr attr;
378	u8 mic_p[EAP_SAKE_MIC_LEN];
379
380	if (data->state != CONFIRM)
381		return;
382
383	wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Confirm");
384
385	if (eap_sake_parse_attributes(payload, payloadlen, &attr))
386		return;
387
388	if (!attr.mic_p) {
389		wpa_printf(MSG_INFO, "EAP-SAKE: Response/Confirm did not "
390			   "include AT_MIC_P");
391		return;
392	}
393
394	if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
395				 sm->server_id, sm->server_id_len,
396				 data->peerid, data->peerid_len, 1,
397				 wpabuf_head(respData), wpabuf_len(respData),
398				 attr.mic_p, mic_p) < 0) {
399		wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
400		return;
401	}
402	if (os_memcmp_const(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) {
403		wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P");
404		eap_sake_state(data, FAILURE);
405	} else
406		eap_sake_state(data, SUCCESS);
407}
408
409
410static void eap_sake_process_auth_reject(struct eap_sm *sm,
411					 struct eap_sake_data *data,
412					 const struct wpabuf *respData,
413					 const u8 *payload, size_t payloadlen)
414{
415	wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Auth-Reject");
416	eap_sake_state(data, FAILURE);
417}
418
419
420static void eap_sake_process(struct eap_sm *sm, void *priv,
421			     struct wpabuf *respData)
422{
423	struct eap_sake_data *data = priv;
424	struct eap_sake_hdr *resp;
425	u8 subtype;
426	size_t len;
427	const u8 *pos, *end;
428
429	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, respData, &len);
430	if (pos == NULL || len < sizeof(struct eap_sake_hdr))
431		return;
432
433	resp = (struct eap_sake_hdr *) pos;
434	end = pos + len;
435	subtype = resp->subtype;
436	pos = (u8 *) (resp + 1);
437
438	wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Received attributes",
439		    pos, end - pos);
440
441	switch (subtype) {
442	case EAP_SAKE_SUBTYPE_IDENTITY:
443		eap_sake_process_identity(sm, data, respData, pos, end - pos);
444		break;
445	case EAP_SAKE_SUBTYPE_CHALLENGE:
446		eap_sake_process_challenge(sm, data, respData, pos, end - pos);
447		break;
448	case EAP_SAKE_SUBTYPE_CONFIRM:
449		eap_sake_process_confirm(sm, data, respData, pos, end - pos);
450		break;
451	case EAP_SAKE_SUBTYPE_AUTH_REJECT:
452		eap_sake_process_auth_reject(sm, data, respData, pos,
453					     end - pos);
454		break;
455	}
456}
457
458
459static Boolean eap_sake_isDone(struct eap_sm *sm, void *priv)
460{
461	struct eap_sake_data *data = priv;
462	return data->state == SUCCESS || data->state == FAILURE;
463}
464
465
466static u8 * eap_sake_getKey(struct eap_sm *sm, void *priv, size_t *len)
467{
468	struct eap_sake_data *data = priv;
469	u8 *key;
470
471	if (data->state != SUCCESS)
472		return NULL;
473
474	key = os_memdup(data->msk, EAP_MSK_LEN);
475	if (key == NULL)
476		return NULL;
477	*len = EAP_MSK_LEN;
478
479	return key;
480}
481
482
483static u8 * eap_sake_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
484{
485	struct eap_sake_data *data = priv;
486	u8 *key;
487
488	if (data->state != SUCCESS)
489		return NULL;
490
491	key = os_memdup(data->emsk, EAP_EMSK_LEN);
492	if (key == NULL)
493		return NULL;
494	*len = EAP_EMSK_LEN;
495
496	return key;
497}
498
499
500static Boolean eap_sake_isSuccess(struct eap_sm *sm, void *priv)
501{
502	struct eap_sake_data *data = priv;
503	return data->state == SUCCESS;
504}
505
506
507static u8 * eap_sake_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
508{
509	struct eap_sake_data *data = priv;
510	u8 *id;
511
512	if (data->state != SUCCESS)
513		return NULL;
514
515	*len = 1 + 2 * EAP_SAKE_RAND_LEN;
516	id = os_malloc(*len);
517	if (id == NULL)
518		return NULL;
519
520	id[0] = EAP_TYPE_SAKE;
521	os_memcpy(id + 1, data->rand_s, EAP_SAKE_RAND_LEN);
522	os_memcpy(id + 1 + EAP_SAKE_RAND_LEN, data->rand_s, EAP_SAKE_RAND_LEN);
523	wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Derived Session-Id", id, *len);
524
525	return id;
526}
527
528
529int eap_server_sake_register(void)
530{
531	struct eap_method *eap;
532
533	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
534				      EAP_VENDOR_IETF, EAP_TYPE_SAKE, "SAKE");
535	if (eap == NULL)
536		return -1;
537
538	eap->init = eap_sake_init;
539	eap->reset = eap_sake_reset;
540	eap->buildReq = eap_sake_buildReq;
541	eap->check = eap_sake_check;
542	eap->process = eap_sake_process;
543	eap->isDone = eap_sake_isDone;
544	eap->getKey = eap_sake_getKey;
545	eap->isSuccess = eap_sake_isSuccess;
546	eap->get_emsk = eap_sake_get_emsk;
547	eap->getSessionId = eap_sake_get_session_id;
548
549	return eap_server_method_register(eap);
550}
551