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 
17 struct 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 
33 static 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 
52 static 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 
61 static 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 
82 static 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 
90 static 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 
116 static 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 
144 static 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 
180 static 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 		os_free(msg);
208 		return NULL;
209 	}
210 
211 	return msg;
212 }
213 
214 
215 static 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 
235 static 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 
287 static 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 
301 static 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 	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, data->emsk);
347 
348 	eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
349 			     sm->server_id, sm->server_id_len,
350 			     data->peerid, data->peerid_len, 1,
351 			     wpabuf_head(respData), wpabuf_len(respData),
352 			     attr.mic_p, mic_p);
353 	if (os_memcmp_const(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) {
354 		wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P");
355 		eap_sake_state(data, FAILURE);
356 		return;
357 	}
358 
359 	eap_sake_state(data, CONFIRM);
360 }
361 
362 
363 static void eap_sake_process_confirm(struct eap_sm *sm,
364 				     struct eap_sake_data *data,
365 				     const struct wpabuf *respData,
366 				     const u8 *payload, size_t payloadlen)
367 {
368 	struct eap_sake_parse_attr attr;
369 	u8 mic_p[EAP_SAKE_MIC_LEN];
370 
371 	if (data->state != CONFIRM)
372 		return;
373 
374 	wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Confirm");
375 
376 	if (eap_sake_parse_attributes(payload, payloadlen, &attr))
377 		return;
378 
379 	if (!attr.mic_p) {
380 		wpa_printf(MSG_INFO, "EAP-SAKE: Response/Confirm did not "
381 			   "include AT_MIC_P");
382 		return;
383 	}
384 
385 	eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
386 			     sm->server_id, sm->server_id_len,
387 			     data->peerid, data->peerid_len, 1,
388 			     wpabuf_head(respData), wpabuf_len(respData),
389 			     attr.mic_p, mic_p);
390 	if (os_memcmp_const(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) {
391 		wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P");
392 		eap_sake_state(data, FAILURE);
393 	} else
394 		eap_sake_state(data, SUCCESS);
395 }
396 
397 
398 static void eap_sake_process_auth_reject(struct eap_sm *sm,
399 					 struct eap_sake_data *data,
400 					 const struct wpabuf *respData,
401 					 const u8 *payload, size_t payloadlen)
402 {
403 	wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Auth-Reject");
404 	eap_sake_state(data, FAILURE);
405 }
406 
407 
408 static void eap_sake_process(struct eap_sm *sm, void *priv,
409 			     struct wpabuf *respData)
410 {
411 	struct eap_sake_data *data = priv;
412 	struct eap_sake_hdr *resp;
413 	u8 subtype;
414 	size_t len;
415 	const u8 *pos, *end;
416 
417 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, respData, &len);
418 	if (pos == NULL || len < sizeof(struct eap_sake_hdr))
419 		return;
420 
421 	resp = (struct eap_sake_hdr *) pos;
422 	end = pos + len;
423 	subtype = resp->subtype;
424 	pos = (u8 *) (resp + 1);
425 
426 	wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Received attributes",
427 		    pos, end - pos);
428 
429 	switch (subtype) {
430 	case EAP_SAKE_SUBTYPE_IDENTITY:
431 		eap_sake_process_identity(sm, data, respData, pos, end - pos);
432 		break;
433 	case EAP_SAKE_SUBTYPE_CHALLENGE:
434 		eap_sake_process_challenge(sm, data, respData, pos, end - pos);
435 		break;
436 	case EAP_SAKE_SUBTYPE_CONFIRM:
437 		eap_sake_process_confirm(sm, data, respData, pos, end - pos);
438 		break;
439 	case EAP_SAKE_SUBTYPE_AUTH_REJECT:
440 		eap_sake_process_auth_reject(sm, data, respData, pos,
441 					     end - pos);
442 		break;
443 	}
444 }
445 
446 
447 static Boolean eap_sake_isDone(struct eap_sm *sm, void *priv)
448 {
449 	struct eap_sake_data *data = priv;
450 	return data->state == SUCCESS || data->state == FAILURE;
451 }
452 
453 
454 static u8 * eap_sake_getKey(struct eap_sm *sm, void *priv, size_t *len)
455 {
456 	struct eap_sake_data *data = priv;
457 	u8 *key;
458 
459 	if (data->state != SUCCESS)
460 		return NULL;
461 
462 	key = os_memdup(data->msk, EAP_MSK_LEN);
463 	if (key == NULL)
464 		return NULL;
465 	*len = EAP_MSK_LEN;
466 
467 	return key;
468 }
469 
470 
471 static u8 * eap_sake_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
472 {
473 	struct eap_sake_data *data = priv;
474 	u8 *key;
475 
476 	if (data->state != SUCCESS)
477 		return NULL;
478 
479 	key = os_memdup(data->emsk, EAP_EMSK_LEN);
480 	if (key == NULL)
481 		return NULL;
482 	*len = EAP_EMSK_LEN;
483 
484 	return key;
485 }
486 
487 
488 static Boolean eap_sake_isSuccess(struct eap_sm *sm, void *priv)
489 {
490 	struct eap_sake_data *data = priv;
491 	return data->state == SUCCESS;
492 }
493 
494 
495 static u8 * eap_sake_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
496 {
497 	struct eap_sake_data *data = priv;
498 	u8 *id;
499 
500 	if (data->state != SUCCESS)
501 		return NULL;
502 
503 	*len = 1 + 2 * EAP_SAKE_RAND_LEN;
504 	id = os_malloc(*len);
505 	if (id == NULL)
506 		return NULL;
507 
508 	id[0] = EAP_TYPE_SAKE;
509 	os_memcpy(id + 1, data->rand_s, EAP_SAKE_RAND_LEN);
510 	os_memcpy(id + 1 + EAP_SAKE_RAND_LEN, data->rand_s, EAP_SAKE_RAND_LEN);
511 	wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Derived Session-Id", id, *len);
512 
513 	return id;
514 }
515 
516 
517 int eap_server_sake_register(void)
518 {
519 	struct eap_method *eap;
520 
521 	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
522 				      EAP_VENDOR_IETF, EAP_TYPE_SAKE, "SAKE");
523 	if (eap == NULL)
524 		return -1;
525 
526 	eap->init = eap_sake_init;
527 	eap->reset = eap_sake_reset;
528 	eap->buildReq = eap_sake_buildReq;
529 	eap->check = eap_sake_check;
530 	eap->process = eap_sake_process;
531 	eap->isDone = eap_sake_isDone;
532 	eap->getKey = eap_sake_getKey;
533 	eap->isSuccess = eap_sake_isSuccess;
534 	eap->get_emsk = eap_sake_get_emsk;
535 	eap->getSessionId = eap_sake_get_session_id;
536 
537 	return eap_server_method_register(eap);
538 }
539