• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
eap_sake_state_txt(int state)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 
eap_sake_state(struct eap_sake_data * data,int state)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 
eap_sake_init(struct eap_sm * sm)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 
eap_sake_reset(struct eap_sm * sm,void * priv)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 
eap_sake_build_msg(struct eap_sake_data * data,u8 id,size_t length,u8 subtype)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 
eap_sake_build_identity(struct eap_sm * sm,struct eap_sake_data * data,u8 id)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->cfg->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->cfg->server_id, sm->cfg->server_id_len);
139 
140 	return msg;
141 }
142 
143 
eap_sake_build_challenge(struct eap_sm * sm,struct eap_sake_data * data,u8 id)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->cfg->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->cfg->server_id, sm->cfg->server_id_len);
175 
176 	return msg;
177 }
178 
179 
eap_sake_build_confirm(struct eap_sm * sm,struct eap_sake_data * data,u8 id)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->cfg->server_id, sm->cfg->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 
eap_sake_buildReq(struct eap_sm * sm,void * priv,u8 id)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 
eap_sake_check(struct eap_sm * sm,void * priv,struct wpabuf * respData)235 static bool 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 
eap_sake_process_identity(struct eap_sm * sm,struct eap_sake_data * data,const struct wpabuf * respData,const u8 * payload,size_t payloadlen)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 
eap_sake_process_challenge(struct eap_sm * sm,struct eap_sake_data * data,const struct wpabuf * respData,const u8 * payload,size_t payloadlen)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 	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->cfg->server_id, sm->cfg->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 
eap_sake_process_confirm(struct eap_sm * sm,struct eap_sake_data * data,const struct wpabuf * respData,const u8 * payload,size_t payloadlen)372 static 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->cfg->server_id, sm->cfg->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 
eap_sake_process_auth_reject(struct eap_sm * sm,struct eap_sake_data * data,const struct wpabuf * respData,const u8 * payload,size_t payloadlen)410 static 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 
eap_sake_process(struct eap_sm * sm,void * priv,struct wpabuf * respData)420 static 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 
eap_sake_isDone(struct eap_sm * sm,void * priv)459 static bool 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 
eap_sake_getKey(struct eap_sm * sm,void * priv,size_t * len)466 static 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 
eap_sake_get_emsk(struct eap_sm * sm,void * priv,size_t * len)483 static 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 
eap_sake_isSuccess(struct eap_sm * sm,void * priv)500 static bool 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 
eap_sake_get_session_id(struct eap_sm * sm,void * priv,size_t * len)507 static 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 
eap_server_sake_register(void)529 int 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