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