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