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